]> git.decadent.org.uk Git - nfs-utils.git/commitdiff
Merge branch 'upstream'
authorBen Hutchings <ben@decadent.org.uk>
Wed, 14 Jul 2010 01:50:49 +0000 (02:50 +0100)
committerBen Hutchings <ben@decadent.org.uk>
Wed, 14 Jul 2010 01:50:49 +0000 (02:50 +0100)
Conflicts:
configure
support/include/config.h.in

22 files changed:
NEWS
README
aclocal/kerberos5.m4
configure
configure.ac
support/include/config.h.in
support/nfs/svc_socket.c
utils/exportfs/exportfs.c
utils/gssd/context_heimdal.c
utils/gssd/gssd.c
utils/gssd/gssd.man
utils/gssd/gssd_main_loop.c
utils/gssd/gssd_proc.c
utils/gssd/krb5_util.c
utils/gssd/krb5_util.h
utils/mountd/cache.c
utils/statd/callback.c
utils/statd/monitor.c
utils/statd/rmtcall.c
utils/statd/sm-notify.c
utils/statd/stat.c
utils/statd/statd.c

diff --git a/NEWS b/NEWS
index fe3571a4db1b274e377b0d1d51bb8094e35d4d97..e71acf1fc12721074f7a7acbb3f3cc6fcdff92bb 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,7 +1,7 @@
 Significant changes for nfs-utils 1.1.0 - March/April 2007
 
  - rpc.lockd is gone.  One 3 old kernel releases need it.
- - /sbin/{u,}mount.nfs{,4} is now installed so 'mount' will
+ - /sbin/{u,}mount.nfs{,4} are now installed so 'mount' will
    use these to mount nfs filesystems instead of internal code.
   + mount.nfs will check for 'statd' to be running when mounting
     a filesystem which requires it.  If it is not running it will
@@ -18,7 +18,9 @@ Significant changes for nfs-utils 1.1.0 - March/April 2007
     if you kill and restart it, it will restore that state and
     continue working correctly.
   + statd makes more use of DNS lookup and should handle
-    multi-homed peers better.
+    multi-homed peers better.  In particular, files in
+    /var/lib/nfs/sm/ are named with the Full Qualified Domain Name
+    if available.
  - If you export a directory as 'crossmnt', all filesystems
    mounted beneath are automatically exported with the same
    options (unless explicitly exported with different options).
@@ -26,18 +28,33 @@ Significant changes for nfs-utils 1.1.0 - March/April 2007
    no_subtree_check.
  - By default the system 'rpcgen' is used while building
    nfs-utils rather than the internal one.
+ - Exportfs will warn if you try to export a filesystem that does
+   not support NFS export.
+ - Comprehensive notes on startup dependencies have been added
+   to the README file.
+ - Mount and statd now listen on a non-privileged port by default.
+   For maximum safety an upgrade to portmap is recommended.
+        git://neil.brown.name/portmap
 
+ - This release should work with MIT Kerberos and Heimdal 0.8.1 and later.
 
-Further notes on statd:
 
- statd should be installed in /usr/sbin, not /sbin.
- If you need to mount /usr via nfs, use 'nolock'
+ - A new option, -n, was added to rpc.gssd which specifies that
+   accesses by root should not use 'machine credentials' when
+   accessing NFS file systems mounted with Kerberos.  Using this
+   option allows the root user to access the NFS space using any
+   Kerberos principal, rather than always using the machine
+   credentials.  However, its use also requires that root manually
+   authenticate before attempting a mount with Kerberos.
 
- At boot time, run "/usr/sbin/sm-notify".
- Run "statd" only when starting the NFS server.
- "statd" should be run before starting the NFS server.
- You do not need to start statd at boot time incase an
- NFS filesystem is mounted.  mount.nfs will take care of that.
+   When rpc.gssd uses machine credentials, the selection algorithm has
+   been changed.  Instead of simply using the first "nfs/*" key in the
+   keytab, the keytab is now searched for keys in the following
+   defined order:
 
- Make sure /usr/sbin/start-statd will run statd with required
- arguments.
+     root/<fqdn>@REALM
+     nfs/<fqdn>@REALM
+     host/<fqdn>@REALM
+     root/<any-name>@REALM
+     nfs/<any-name>@REALM
+     host/<any-name>@REALM
diff --git a/README b/README
index 7f18118e98fd40a3e49c1efe7e085e7902aa7c07..aa4666f35012cd1f14560a2ef63f58f07ddcd2cf 100644 (file)
--- a/README
+++ b/README
@@ -45,6 +45,120 @@ simply
 
 Finally, build as usual as above.
 
+3. DAEMON STARTUP ORDER
+
+This nfs-utils packages does not provide any scripts for starting
+various daemons as most distributions replace them with their own, so
+any scripts we package would not get much testing.
+Instead, we explain the dependencies involved in startup so that
+scripts can be written to work correctly.
+
+3.0   PREREQUISITES 
+
+   Name service (host name lookup) should be working before any
+   NFS services are started.
+
+   "portmap" must be running before any NFS services (server or
+   client) are started.
+   
+   Normally network interfaces should be configured first as well,
+   though this isn't critical for the NFS server (providing name
+   service is handled locally).
+   
+3.1.  SERVER STARTUP
+
+
+   A/  mount -t nfsd /proc/fs/nfsd
+      This filesystem needs to be mount before most daemons,
+      particularly exportfs, mountd, svcgssd, idmapd.
+      It could be mounted once, or the script that starts each daemon
+      could test if it is mounted and mount it if not.
+
+   B/ svcgssd ; idmapd
+       These supply services to nfsd and so should be started before
+       rpc.nfsd.  Where they come between mounting the nfsd filesystem
+       and starting the nfsd server is not important.
+       idmapd is only needed for NFSv4 support.
+       svcgssd is only needed if exportfs NFS filesystem with crypto-
+       security (Kerberos or SPKM3).
+
+   C/ exportfs -av ; rpc.mountd
+       It is important that exportfs be run before mountd so that
+       mountd is working from current information (in
+       /var/lib/nfs/etab).
+       It is also important that both of these are run before
+       rpc.nfsd.
+       If not, any NFS requests that arrive before mountd is started
+       will get replied to with a 'Stale NFS File handle' error.
+
+   D/ rpc.statd --no-notify
+       It is best if statd is started before nfsd though this isn't
+       critical.  Certainly it should be at most a few seconds after
+       nfsd.
+       When nfsd starts it will start lockd. If lockd then receives a
+       lock request it will communicate with statd.  If statd is not
+       running lockd will retry, but it won't wait forever for a
+       reply.
+       Note that if statd is started before nfsd, the --no-notify
+       option must be used.  If notify requests are sent out before
+       nfsd start, clients may try to reclaim locks and, on finding
+       that lockd isn't running, they will give up and never reclaim
+       the lock.
+       rpc.statd is only needed for NFSv2 and NFSv3 support.
+
+   E/ rpc.nfsd
+       Starting nfsd will automatically start lockd.  The nfs server
+       will now be fully active and respond to any requests from
+       clients.
+       
+   F/ sm-notify
+       This will notify any client which might have locks from before
+       a reboot to try to reclaim their locks.  This should start
+       immediately after rpc.nfsd is started so that clients have a
+       chance to reclaim locks within the 90 second grace period.
+       sm-notify is only needed for NFSv2 and NFSv3 support.
+
+
+3.2.  CLIENT STARTUP
+
+   A/ sm-notify
+      This should be run shortly after boot and before any NFS
+      filesystems are mounted with remote-locking support -
+      filesystems can be mounted with "-o nolock" before sm-notify.
+      This is appropriate for '/', '/usr', and '/var'.
+
+   B/ gssd ; idmapd
+      idmapd should be started before mounting any NFSv4 filesystems.
+      gssd should be started before mounting any NFS filesystems
+      securely (with Kerberos of SPKM3).
+
+   C/ statd should be run before any NFSv2 or NFSv3 filesystem is
+      mounted with remote locking (i.e. without -o nolock).
+      'mount' will try to use "/usr/sbin/start-statd" to start statd
+      if it is not already running, so there is no need to explicitly
+      start statd in boot-time scripts.
+
+3.3.  SERVER/CLIENT INTERACTIONS
+
+   A/ sm-notify
+      Both the server and the client need sm-notify to be run.
+      It should be run after the NFS server is started, but before
+      and NFS filesystems are mounted with remote locking.
+
+   B/ rpc.statd
+      Both the server and the client need rpc.statd to be running.
+      Each should try to start when they need it.
+
+   C/ idmapd
+
+      Both the server and client need idmapd to be running.  If idmapd
+      is started (for the client) before starting nfsd the 'nfsd'
+      filesystem is mounted, then idmapd should be sent a HUP signal
+      afterwards to signal that the server channels should be opened.
+
+   
+      
+
 Share And Enjoy!
 
     -- the nfs-utils developers
index b83e12236be86f3b9e06ee22b8b6feae9cc0e231..2475f506a25fa4d8d76444e45c953fecba951f01 100644 (file)
@@ -93,6 +93,10 @@ AC_DEFUN([AC_KERBEROS_V5],[
   AC_CHECK_LIB($gssapi_lib, gss_krb5_ccache_name,
     AC_DEFINE(HAVE_GSS_KRB5_CCACHE_NAME, 1, [Define this if the Kerberos GSS library supports gss_krb5_ccache_name]), ,$KRBLIBS)
 
+  dnl Check for newer error message facility
+  AC_CHECK_LIB($gssapi_lib, krb5_get_error_message,
+    AC_DEFINE(HAVE_KRB5_GET_ERROR_MESSAGE, 1, [Define this if the function krb5_get_error_message is available]), ,$KRBLIBS)
+
   dnl If they specified a directory and it didn't work, give them a warning
   if test "x$krb5_with" != "x" -a "$krb5_with" != "$KRBDIR"; then
     AC_MSG_WARN(Using $KRBDIR instead of requested value of $krb5_with for Kerberos!)
index 0f561fff42014bdd002f9db621823b229a999386..85fb8c2e1c126f72ecba8eed491f84d620b1d5dd 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.61 for linux nfs-utils 1.1.0-rc1.
+# Generated by GNU Autoconf 2.61 for linux nfs-utils 1.1.0-rc2.
 #
 # Report bugs to <nfs@lists.sf.net>.
 #
@@ -728,8 +728,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
 # Identity of this package.
 PACKAGE_NAME='linux nfs-utils'
 PACKAGE_TARNAME='nfs-utils'
-PACKAGE_VERSION='1.1.0-rc1'
-PACKAGE_STRING='linux nfs-utils 1.1.0-rc1'
+PACKAGE_VERSION='1.1.0-rc2'
+PACKAGE_STRING='linux nfs-utils 1.1.0-rc2'
 PACKAGE_BUGREPORT='nfs@lists.sf.net'
 
 ac_default_prefix=/usr
@@ -1450,7 +1450,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures linux nfs-utils 1.1.0-rc1 to adapt to many kinds of systems.
+\`configure' configures linux nfs-utils 1.1.0-rc2 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1520,7 +1520,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of linux nfs-utils 1.1.0-rc1:";;
+     short | recursive ) echo "Configuration of linux nfs-utils 1.1.0-rc2:";;
    esac
   cat <<\_ACEOF
 
@@ -1654,7 +1654,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-linux nfs-utils configure 1.1.0-rc1
+linux nfs-utils configure 1.1.0-rc2
 generated by GNU Autoconf 2.61
 
 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -1668,7 +1668,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by linux nfs-utils $as_me 1.1.0-rc1, which was
+It was created by linux nfs-utils $as_me 1.1.0-rc2, which was
 generated by GNU Autoconf 2.61.  Invocation command line was
 
   $ $0 $@
@@ -2442,7 +2442,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='nfs-utils'
- VERSION='1.1.0-rc1'
+ VERSION='1.1.0-rc2'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -22484,12 +22484,12 @@ if test -n "$PKG_CONFIG"; then
         pkg_cv_GSSAPI_CFLAGS="$GSSAPI_CFLAGS"
     else
         if test -n "$PKG_CONFIG" && \
-    { (echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libgssapi >= 0.9\"") >&5
-  ($PKG_CONFIG --exists --print-errors "libgssapi >= 0.9") 2>&5
+    { (echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libgssapi >= 0.11\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libgssapi >= 0.11") 2>&5
   ac_status=$?
   echo "$as_me:$LINENO: \$? = $ac_status" >&5
   (exit $ac_status); }; then
-  pkg_cv_GSSAPI_CFLAGS=`$PKG_CONFIG --cflags "libgssapi >= 0.9" 2>/dev/null`
+  pkg_cv_GSSAPI_CFLAGS=`$PKG_CONFIG --cflags "libgssapi >= 0.11" 2>/dev/null`
 else
   pkg_failed=yes
 fi
@@ -22502,12 +22502,12 @@ if test -n "$PKG_CONFIG"; then
         pkg_cv_GSSAPI_LIBS="$GSSAPI_LIBS"
     else
         if test -n "$PKG_CONFIG" && \
-    { (echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libgssapi >= 0.9\"") >&5
-  ($PKG_CONFIG --exists --print-errors "libgssapi >= 0.9") 2>&5
+    { (echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libgssapi >= 0.11\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libgssapi >= 0.11") 2>&5
   ac_status=$?
   echo "$as_me:$LINENO: \$? = $ac_status" >&5
   (exit $ac_status); }; then
-  pkg_cv_GSSAPI_LIBS=`$PKG_CONFIG --libs "libgssapi >= 0.9" 2>/dev/null`
+  pkg_cv_GSSAPI_LIBS=`$PKG_CONFIG --libs "libgssapi >= 0.11" 2>/dev/null`
 else
   pkg_failed=yes
 fi
@@ -22526,14 +22526,14 @@ else
         _pkg_short_errors_supported=no
 fi
         if test $_pkg_short_errors_supported = yes; then
-               GSSAPI_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "libgssapi >= 0.9"`
+               GSSAPI_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "libgssapi >= 0.11"`
         else
-               GSSAPI_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "libgssapi >= 0.9"`
+               GSSAPI_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "libgssapi >= 0.11"`
         fi
        # Put the nasty error message in config.log where it belongs
        echo "$GSSAPI_PKG_ERRORS" >&5
 
-       { { echo "$as_me:$LINENO: error: Package requirements (libgssapi >= 0.9) were not met:
+       { { echo "$as_me:$LINENO: error: Package requirements (libgssapi >= 0.11) were not met:
 
 $GSSAPI_PKG_ERRORS
 
@@ -22544,7 +22544,7 @@ Alternatively, you may set the environment variables GSSAPI_CFLAGS
 and GSSAPI_LIBS to avoid the need to call pkg-config.
 See the pkg-config man page for more details.
 " >&5
-echo "$as_me: error: Package requirements (libgssapi >= 0.9) were not met:
+echo "$as_me: error: Package requirements (libgssapi >= 0.11) were not met:
 
 $GSSAPI_PKG_ERRORS
 
@@ -23544,6 +23544,78 @@ cat >>confdefs.h <<\_ACEOF
 #define HAVE_GSS_KRB5_CCACHE_NAME 1
 _ACEOF
 
+fi
+
+
+    as_ac_Lib=`echo "ac_cv_lib_$gssapi_lib''_krb5_get_error_message" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for krb5_get_error_message in -l$gssapi_lib" >&5
+echo $ECHO_N "checking for krb5_get_error_message in -l$gssapi_lib... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-l$gssapi_lib $KRBLIBS $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char krb5_get_error_message ();
+int
+main ()
+{
+return krb5_get_error_message ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  eval "$as_ac_Lib=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_KRB5_GET_ERROR_MESSAGE 1
+_ACEOF
+
 fi
 
 
@@ -29993,7 +30065,7 @@ exec 6>&1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by linux nfs-utils $as_me 1.1.0-rc1, which was
+This file was extended by linux nfs-utils $as_me 1.1.0-rc2, which was
 generated by GNU Autoconf 2.61.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -30046,7 +30118,7 @@ Report bugs to <bug-autoconf@gnu.org>."
 _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF
 ac_cs_version="\\
-linux nfs-utils config.status 1.1.0-rc1
+linux nfs-utils config.status 1.1.0-rc2
 configured by $0, generated by GNU Autoconf 2.61,
   with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
 
index 65a2d02723d1f68fff445b14f84e231a84ae6966..37572083e6433163abeadd5d1253af12663a6d2b 100644 (file)
@@ -1,6 +1,6 @@
 dnl Process this file with autoconf to produce a configure script.
 dnl
-AC_INIT([linux nfs-utils],[1.1.0-rc1],[nfs@lists.sf.net],[nfs-utils])
+AC_INIT([linux nfs-utils],[1.1.0-rc2],[nfs@lists.sf.net],[nfs-utils])
 AC_CANONICAL_BUILD([])
 AC_CANONICAL_HOST([])
 AC_CONFIG_MACRO_DIR(aclocal)
@@ -197,7 +197,7 @@ if test "$enable_nfsv4" = yes; then
       [AC_MSG_ERROR([Unable to locate information required to use librpcsecgss.  If you have pkgconfig installed, you might try setting environment variable PKG_CONFIG_PATH to /usr/local/lib/pkgconfig])
       ]
      )
-    PKG_CHECK_MODULES(GSSAPI, libgssapi >= 0.9)
+    PKG_CHECK_MODULES(GSSAPI, libgssapi >= 0.11)
     fi
 
 fi
index 19ff77c968209b9632871988099ed2cee06819e8..209d958394a20ce7d79d75dff4b4759489602373 100644 (file)
 /* Define this if you have MIT Kerberos libraries */
 #undef HAVE_KRB5
 
+/* Define this if the function krb5_get_error_message is available */
+#undef HAVE_KRB5_GET_ERROR_MESSAGE
+
 /* Define to 1 if you have the <libintl.h> header file. */
 #undef HAVE_LIBINTL_H
 
index 6799d16230e5278f74c2f404c39b14dd1f21c72b..f44217a404821dcd059e8374531d3ac32c12d1fa 100644 (file)
@@ -101,8 +101,6 @@ svc_socket (u_long number, int type, int protocol, int reuse)
     }
   else
     {
-      if (bindresvport (sock, &addr))
-       {
          addr.sin_port = 0;
          if (bind (sock, (struct sockaddr *) &addr, len) < 0)
            {
@@ -110,7 +108,6 @@ svc_socket (u_long number, int type, int protocol, int reuse)
              (void) __close (sock);
              sock = -1;
            }
-       }
     }
 
   if (sock >= 0)
index 8c3f634de5984848de3ccc61037774ecbf89f791..99618c94ee0ed0040f29a32246b3af50d2824539 100644 (file)
@@ -12,6 +12,7 @@
 #include <config.h>
 #endif
 
+#include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdarg.h>
@@ -32,7 +33,7 @@ static void   exports_update(int verbose);
 static void    dump(int verbose);
 static void    error(nfs_export *exp, int err);
 static void    usage(void);
-
+static void    validate_export(nfs_export *exp);
 
 int
 main(int argc, char **argv)
@@ -219,6 +220,7 @@ export_all(int verbose)
                        exp->m_mayexport = 1;
                        exp->m_changed = 1;
                        exp->m_warned = 0;
+                       validate_export(exp);
                }
        }
 }
@@ -276,6 +278,7 @@ exportfs(char *arg, char *options, int verbose)
        exp->m_mayexport = 1;
        exp->m_changed = 1;
        exp->m_warned = 0;
+       validate_export(exp);
        if (hp) free (hp);
 }
 
@@ -340,6 +343,87 @@ unexportfs(char *arg, int verbose)
        if (hp) free (hp);
 }
 
+static int can_test(void)
+{
+       int fd;
+       int n;
+       char *setup = "nfsd 0.0.0.0 2147483647 -test-client-\n";
+       fd = open("/proc/net/rpc/auth.unix.ip/channel", O_WRONLY);
+       if ( fd < 0) return 0;
+       n = write(fd, setup, strlen(setup));
+       close(fd);
+       if (n < 0)
+               return 0;
+       fd = open("/proc/net/rpc/nfsd.export/channel", O_WRONLY);
+       if ( fd < 0) return 0;
+       close(fd);
+       return 1;
+}
+
+static int test_export(char *path, int with_fsid)
+{
+       char buf[1024];
+       int fd, n;
+
+       sprintf(buf, "-test-client- %s 3 %d -1 -1 0\n",
+               path,
+               with_fsid ? NFSEXP_FSID : 0);
+       fd = open("/proc/net/rpc/nfsd.export/channel", O_WRONLY);
+       if (fd < 0)
+               return 0;
+       n = write(fd, buf, strlen(buf));
+       close(fd);
+       if (n < 0)
+               return 0;
+       return 1;
+}
+
+static void
+validate_export(nfs_export *exp)
+{
+       /* Check that the given export point is potentially exportable.
+        * We just give warnings here, don't cause anything to fail.
+        * If a path doesn't exist, or is not a dir or file, give an warning
+        * otherwise trial-export to '-test-client-' and check for failure.
+        */
+       struct stat stb;
+       char *path = exp->m_export.e_path;
+
+       if (stat(path, &stb) < 0) {
+               fprintf(stderr, "exportfs: Warning: %s does not exist\n",
+                       path);
+               return;
+       }
+       if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) {
+               fprintf(stderr, "exportfs: Warning: %s is neither "
+                       "a directory nor a file.\n"
+                       "     remote access will fail\n", path);
+               return;
+       }
+       if (!can_test())
+               return;
+
+       if ((exp->m_export.e_flags & NFSEXP_FSID) || exp->m_export.e_uuid) {
+               if ( !test_export(path, 1)) {
+                       fprintf(stderr, "exportfs: Warning: %s does not "
+                               "support NFS export.\n",
+                               path);
+                       return;
+               }
+       } else if ( ! test_export(path, 0)) {
+               if (test_export(path, 1))
+                       fprintf(stderr, "exportfs: Warning: %s requires fsid= "
+                               "for NFS export\n", path);
+               else
+                       fprintf(stderr, "exportfs: Warning: %s does not "
+                               "support NFS export.\n",
+                               path);
+               return;
+
+       }
+}
+
+
 static char
 dumpopt(char c, char *fmt, ...)
 {
index 5520cbcc61d15850b16f9d249ed28de139f2a41c..6fb8fbdb35da550e3a562f4d66e2268d006f25ab 100644 (file)
@@ -72,14 +72,14 @@ int write_heimdal_enc_key(char **p, char *end, gss_ctx_id_t ctx)
 
        if ((ret = krb5_init_context(&context))) {
                printerr(0, "ERROR: initializing krb5_context: %s\n",
-                       error_message(ret));
+                       gssd_k5_err_msg(NULL, ret));
                goto out_err;
        }
 
        if ((ret = krb5_auth_con_getlocalsubkey(context,
                                                ctx->auth_context, &key))){
                printerr(0, "ERROR: getting auth_context key: %s\n",
-                       error_message(ret));
+                       gssd_k5_err_msg(context, ret));
                goto out_err_free_context;
        }
 
@@ -97,7 +97,7 @@ int write_heimdal_enc_key(char **p, char *end, gss_ctx_id_t ctx)
                                calloc(1, enc_key.keyvalue.length)) == NULL) {
 
                printerr(0, "ERROR: allocating memory for enc key: %s\n",
-                       error_message(ENOMEM));
+                       gssd_k5_err_msg(context, ENOMEM));
                goto out_err_free_key;
        }
        skd = (char *) key->keyvalue.data;
@@ -130,14 +130,14 @@ int write_heimdal_seq_key(char **p, char *end, gss_ctx_id_t ctx)
 
        if ((ret = krb5_init_context(&context))) {
                printerr(0, "ERROR: initializing krb5_context: %s\n",
-                       error_message(ret));
+                       gssd_k5_err_msg(NULL, ret));
                goto out_err;
        }
 
        if ((ret = krb5_auth_con_getlocalsubkey(context,
                                                ctx->auth_context, &key))){
                printerr(0, "ERROR: getting auth_context key: %s\n",
-                       error_message(ret));
+                       gssd_k5_err_msg(context, ret));
                goto out_err_free_context;
        }
 
index 747637c636ef4234295780649924269916e6212e..b6c4ee429f3dbb6af3543986334ef5e154982d4c 100644 (file)
@@ -165,10 +165,6 @@ main(int argc, char *argv[])
        signal(SIGTERM, sig_die);
        signal(SIGHUP, sig_hup);
 
-       /* Process keytab file and get machine credentials */
-       if (root_uses_machine_creds)
-               gssd_refresh_krb5_machine_creds();
-
        gssd_run();
        printerr(0, "gssd_run returned!\n");
        abort();
index f2ecd69995f589832198066f78b90136bceba951..8da10b22defacc34de5ca8c7ffc143d5e054c4c8 100644 (file)
@@ -45,14 +45,25 @@ to use the keys found in
 .I keytab
 to obtain "machine credentials".
 The default value is "/etc/krb5.keytab".
+.IP
 Previous versions of
 .B rpc.gssd
 used only "nfs/*" keys found within the keytab.
-Now, the first keytab entry for each distinct Kerberos realm
-within the keytab is used.  This means that an NFS client
-no longer needs an "nfs/hostname" principal and keytab entry,
-but can instead use a "host/hostname" (or any other) keytab
-entry that is available.
+To be more consistent with other implementations, we now look for
+specific keytab entries.  The search order for keytabs to be used
+for "machine credentials" is now:
+.br
+  root/<hostname>@<REALM>
+.br
+  nfs/<hostname>@<REALM>
+.br
+  host/<hostname>@<REALM>
+.br
+  root/<anyname>@<REALM>
+.br
+  nfs/<anyname>@<REALM>
+.br
+  host/<anyname>@<REALM>
 .TP
 .B -p path
 Tells
index 0559f7b7c7b9478dd223f2e76eb71818265eb332..84f04e96eda218cc236e835067b8e0728f27191c 100644 (file)
@@ -116,6 +116,7 @@ gssd_run()
 
        init_client_list();
 
+       printerr(1, "beginning poll\n");
        while (1) {
                while (dir_changed) {
                        dir_changed = 0;
index 3b190f2995a97f54ebe3e76e6a93078d37c65374..48880b6d26ac0032b2f0ef9c35d99c8a81c2b97b 100644 (file)
@@ -555,7 +555,7 @@ int create_auth_rpc_client(struct clnt_info *clp,
                ai_hints.ai_protocol = IPPROTO_UDP;
        } else {
                printerr(0, "WARNING: unrecognized protocol, '%s', requested "
-                        "for connection to server %s for user with uid %d",
+                        "for connection to server %s for user with uid %d\n",
                         clp->protocol, clp->servername, uid);
                goto out_fail;
        }
@@ -563,12 +563,12 @@ int create_auth_rpc_client(struct clnt_info *clp,
        /* extract the service name from clp->servicename */
        if ((at_sign = strchr(clp->servicename, '@')) == NULL) {
                printerr(0, "WARNING: servicename (%s) not formatted as "
-                       "expected with service@host", clp->servicename);
+                       "expected with service@host\n", clp->servicename);
                goto out_fail;
        }
        if ((at_sign - clp->servicename) >= sizeof(service)) {
                printerr(0, "WARNING: service portion of servicename (%s) "
-                       "is too long!", clp->servicename);
+                       "is too long!\n", clp->servicename);
                goto out_fail;
        }
        strncpy(service, clp->servicename, at_sign - clp->servicename);
@@ -577,13 +577,13 @@ int create_auth_rpc_client(struct clnt_info *clp,
        errcode = getaddrinfo(clp->servername, service, &ai_hints, &a);
        if (errcode) {
                printerr(0, "WARNING: Error from getaddrinfo for server "
-                        "'%s': %s", clp->servername, gai_strerror(errcode));
+                        "'%s': %s\n", clp->servername, gai_strerror(errcode));
                goto out_fail;
        }
 
        if (a == NULL) {
                printerr(0, "WARNING: No address information found for "
-                        "connection to server %s for user with uid %d",
+                        "connection to server %s for user with uid %d\n",
                         clp->servername, uid);
                goto out_fail;
        }
@@ -617,7 +617,7 @@ int create_auth_rpc_client(struct clnt_info *clp,
        } else {
                /* Shouldn't happen! */
                printerr(0, "ERROR: requested protocol '%s', but "
-                        "got addrinfo with protocol %d",
+                        "got addrinfo with protocol %d\n",
                         clp->protocol, a->ai_protocol);
                goto out_fail;
        }
@@ -700,14 +700,16 @@ handle_krb5_upcall(struct clnt_info *clp)
                if (uid == 0 && root_uses_machine_creds == 1) {
                        int success = 0;
 
+                       gssd_refresh_krb5_machine_credential(clp->servername,
+                                                            NULL);
                        /*
                         * Get a list of credential cache names and try each
                         * of them until one works or we've tried them all
                         */
                        if (gssd_get_krb5_machine_cred_list(&credlist)) {
-                               printerr(0, "WARNING: Failed to obtain machine "
-                                        "credentials for connection to "
-                                        "server %s\n", clp->servername);
+                               printerr(0, "ERROR: No credentials found "
+                                        "for connection to server %s\n",
+                                        clp->servername);
                                        goto out_return_error;
                        }
                        for (ccname = credlist; ccname && *ccname; ccname++) {
index f1682b8ec8701f5090577122869b76cf470c0eb9..87bd7e41cc563d89527c7b56b4c50c98fc7edbbd 100644 (file)
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <unistd.h>
 #include <string.h>
 #include <dirent.h>
+#include <netdb.h>
+#include <ctype.h>
 #include <errno.h>
 #include <time.h>
 #include <gssapi/gssapi.h>
@@ -131,9 +134,7 @@ static int select_krb5_ccache(const struct dirent *d);
 static int gssd_find_existing_krb5_ccache(uid_t uid, struct dirent **d);
 static int gssd_get_single_krb5_cred(krb5_context context,
                krb5_keytab kt, struct gssd_k5_kt_princ *ple);
-static int gssd_have_realm_ple(void *realm);
-static int gssd_process_krb5_keytab(krb5_context context, krb5_keytab kt,
-               char *kt_name);
+
 
 /*
  * Called from the scandir function to weed out potential krb5
@@ -298,6 +299,7 @@ limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid)
        if (maj_stat != GSS_S_COMPLETE) {
                pgsserr("gss_set_allowable_enctypes",
                        maj_stat, min_stat, &krb5oid);
+               gss_release_cred(&min_stat, &credh);
                return -1;
        }
        sec->cred = credh;
@@ -329,6 +331,7 @@ gssd_get_single_krb5_cred(krb5_context context,
        int code;
        time_t now = time(0);
        char *cache_type;
+       char *pname = NULL;
 
        memset(&my_creds, 0, sizeof(my_creds));
 
@@ -345,6 +348,9 @@ gssd_get_single_krb5_cred(krb5_context context,
                goto out;
        }
 
+       if ((krb5_unparse_name(context, ple->princ, &pname)))
+               pname = NULL;
+
        krb5_get_init_creds_opt_init(&options);
        krb5_get_init_creds_opt_set_address_list(&options, NULL);
 
@@ -353,21 +359,12 @@ gssd_get_single_krb5_cred(krb5_context context,
        printerr(0, "WARNING: Using (debug) short machine cred lifetime!\n");
        krb5_get_init_creds_opt_set_tkt_life(&options, 5*60);
 #endif
-        if ((code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ,
-                                         kt, 0, NULL, &options))) {
-               char *pname;
-               if ((krb5_unparse_name(context, ple->princ, &pname))) {
-                       pname = NULL;
-               }
+       if ((code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ,
+                                              kt, 0, NULL, &options))) {
                printerr(0, "WARNING: %s while getting initial ticket for "
-                           "principal '%s' from keytab '%s'\n",
-                        error_message(code),
+                        "principal '%s' using keytab '%s'\n",
+                        gssd_k5_err_msg(context, code),
                         pname ? pname : "<unparsable>", kt_name);
-#ifdef HAVE_KRB5
-               if (pname) krb5_free_unparsed_name(context, pname);
-#else
-               if (pname) free(pname);
-#endif
                goto out;
        }
 
@@ -384,32 +381,38 @@ gssd_get_single_krb5_cred(krb5_context context,
                GSSD_DEFAULT_CRED_DIR, GSSD_DEFAULT_CRED_PREFIX,
                GSSD_DEFAULT_MACHINE_CRED_SUFFIX, ple->realm);
        ple->endtime = my_creds.times.endtime;
+       if (ple->ccname != NULL)
+               free(ple->ccname);
        ple->ccname = strdup(cc_name);
        if (ple->ccname == NULL) {
                printerr(0, "ERROR: no storage to duplicate credentials "
-                           "cache name\n");
+                           "cache name '%s'\n", cc_name);
                code = ENOMEM;
                goto out;
        }
        if ((code = krb5_cc_resolve(context, cc_name, &ccache))) {
                printerr(0, "ERROR: %s while opening credential cache '%s'\n",
-                        error_message(code), cc_name);
+                        gssd_k5_err_msg(context, code), cc_name);
                goto out;
        }
        if ((code = krb5_cc_initialize(context, ccache, ple->princ))) {
                printerr(0, "ERROR: %s while initializing credential "
-                        "cache '%s'\n", error_message(code), cc_name);
+                        "cache '%s'\n", gssd_k5_err_msg(context, code),
+                        cc_name);
                goto out;
        }
        if ((code = krb5_cc_store_cred(context, ccache, &my_creds))) {
                printerr(0, "ERROR: %s while storing credentials in '%s'\n",
-                        error_message(code), cc_name);
+                        gssd_k5_err_msg(context, code), cc_name);
                goto out;
        }
 
        code = 0;
-       printerr(1, "Using (machine) credentials cache: '%s'\n", cc_name);
+       printerr(2, "Successfully obtained machine credentials for "
+                "principal '%s' stored in ccache '%s'\n", pname, cc_name);
   out:
+       if (pname)
+               k5_free_unparsed_name(context, pname);
        if (ccache)
                krb5_cc_close(context, ccache);
        krb5_free_cred_contents(context, &my_creds);
@@ -417,155 +420,293 @@ gssd_get_single_krb5_cred(krb5_context context,
 }
 
 /*
- * Determine if we already have a ple for the given realm
- *
- * Returns:
- *     0 => no ple found for given realm
- *     1 => found ple for given realm
+ * Depending on the version of Kerberos, we either need to use
+ * a private function, or simply set the environment variable.
  */
-static int
-gssd_have_realm_ple(void *r)
+static void
+gssd_set_krb5_ccache_name(char *ccname)
+{
+#ifdef USE_GSS_KRB5_CCACHE_NAME
+       u_int   maj_stat, min_stat;
+
+       printerr(2, "using gss_krb5_ccache_name to select krb5 ccache %s\n",
+                ccname);
+       maj_stat = gss_krb5_ccache_name(&min_stat, ccname, NULL);
+       if (maj_stat != GSS_S_COMPLETE) {
+               printerr(0, "WARNING: gss_krb5_ccache_name with "
+                       "name '%s' failed (%s)\n",
+                       ccname, error_message(min_stat));
+       }
+#else
+       /*
+        * Set the KRB5CCNAME environment variable to tell the krb5 code
+        * which credentials cache to use.  (Instead of using the private
+        * function above for which there is no generic gssapi
+        * equivalent.)
+        */
+       printerr(2, "using environment variable to select krb5 ccache %s\n",
+                ccname);
+       setenv("KRB5CCNAME", ccname, 1);
+#endif
+}
+
+/*
+ * Given a principal, find a matching ple structure
+ */
+static struct gssd_k5_kt_princ *
+find_ple_by_princ(krb5_context context, krb5_principal princ)
 {
        struct gssd_k5_kt_princ *ple;
+
+       for (ple = gssd_k5_kt_princ_list; ple != NULL; ple = ple->next) {
+               if (krb5_principal_compare(context, ple->princ, princ))
+                       return ple;
+       }
+       /* no match found */
+       return NULL;
+}
+
+/*
+ * Create, initialize, and add a new ple structure to the global list
+ */
+static struct gssd_k5_kt_princ *
+new_ple(krb5_context context, krb5_principal princ)
+{
+       struct gssd_k5_kt_princ *ple = NULL, *p;
+       krb5_error_code code;
+       char *default_realm;
+       int is_default_realm = 0;
+
+       ple = malloc(sizeof(struct gssd_k5_kt_princ));
+       if (ple == NULL)
+               goto outerr;
+       memset(ple, 0, sizeof(*ple));
+
 #ifdef HAVE_KRB5
-       krb5_data *realm = (krb5_data *)r;
+       ple->realm = strndup(princ->realm.data,
+                            princ->realm.length);
 #else
-       char *realm = (char *)r;
+       ple->realm = strdup(princ->realm);
 #endif
+       if (ple->realm == NULL)
+               goto outerr;
+       code = krb5_copy_principal(context, princ, &ple->princ);
+       if (code)
+               goto outerr;
 
-       for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
+       /*
+        * Add new entry onto the list (if this is the default
+        * realm, always add to the front of the list)
+        */
+
+       code = krb5_get_default_realm(context, &default_realm);
+       if (code == 0) {
+               if (strcmp(ple->realm, default_realm) == 0)
+                       is_default_realm = 1;
+               k5_free_default_realm(context, default_realm);
+       }
+
+       if (is_default_realm) {
+               ple->next = gssd_k5_kt_princ_list;
+               gssd_k5_kt_princ_list = ple;
+       } else {
+               p = gssd_k5_kt_princ_list;
+               while (p != NULL && p->next != NULL)
+                       p = p->next;
+               if (p == NULL)
+                       gssd_k5_kt_princ_list = ple;
+               else
+                       p->next = ple;
+       }
+
+       return ple;
+outerr:
+       if (ple) {
+               if (ple->realm)
+                       free(ple->realm);
+               free(ple);
+       }
+       return NULL;
+}
+
+/*
+ * Given a principal, find an existing ple structure, or create one
+ */
+static struct gssd_k5_kt_princ *
+get_ple_by_princ(krb5_context context, krb5_principal princ)
+{
+       struct gssd_k5_kt_princ *ple;
+
+       /* Need to serialize list if we ever become multi-threaded! */
+
+       ple = find_ple_by_princ(context, princ);
+       if (ple == NULL) {
+               ple = new_ple(context, princ);
+       }
+
+       return ple;
+}
+
+/*
+ * Given a (possibly unqualified) hostname,
+ * return the fully qualified (lower-case!) hostname
+ */
+static int
+get_full_hostname(const char *inhost, char *outhost, int outhostlen)
+{
+       struct addrinfo *addrs = NULL;
+       struct addrinfo hints;
+       int retval;
+       char *c;
+
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_socktype = SOCK_STREAM;
+       hints.ai_family = PF_UNSPEC;
+       hints.ai_flags = AI_CANONNAME;
+
+       /* Get full target hostname */
+       retval = getaddrinfo(inhost, NULL, &hints, &addrs);
+       if (retval) {
+               printerr(0, "%s while getting full hostname for '%s'\n",
+                        gai_strerror(retval), inhost);
+               goto out;
+       }
+       strncpy(outhost, addrs->ai_canonname, outhostlen);
+       freeaddrinfo(addrs);
+       for (c = outhost; *c != '\0'; c++)
+           *c = tolower(*c);
+
+       printerr(3, "Full hostname for '%s' is '%s'\n", inhost, outhost);
+       retval = 0;
+out:
+       return retval;
+}
+
+/* 
+ * If principal matches the given realm and service name,
+ * and has *any* instance (hostname), return 1.
+ * Otherwise return 0, indicating no match.
+ */
+static int
+realm_and_service_match(krb5_context context, krb5_principal p,
+                       const char *realm, const char *service)
+{
 #ifdef HAVE_KRB5
-               if ((realm->length == strlen(ple->realm)) &&
-                   (strncmp(realm->data, ple->realm, realm->length) == 0)) {
+       /* Must have two components */
+       if (p->length != 2)
+               return 0;
+       if ((strlen(realm) == p->realm.length)
+           && (strncmp(realm, p->realm.data, p->realm.length) == 0)
+           && (strlen(service) == p->data[0].length)
+           && (strncmp(service, p->data[0].data, p->data[0].length) == 0))
+               return 1;
 #else
-               if (strcmp(realm, ple->realm) == 0) {
+       const char *name, *inst;
+
+       if (p->name.name_string.len != 2)
+               return 0;
+       name = krb5_principal_get_comp_string(context, p, 0);
+       inst = krb5_principal_get_comp_string(context, p, 1);
+       if (name == NULL || inst == NULL)
+               return 0;
+       if ((strcmp(realm, p->realm) == 0)
+           && (strcmp(service, name) == 0))
+               return 1;
 #endif
-                   return 1;
-               }
-       }
        return 0;
 }
 
 /*
- * Process the given keytab file and create a list of principals we
- * might use as machine credentials.
+ * Search the given keytab file looking for an entry with the given
+ * service name and realm, ignoring hostname (instance).
  *
  * Returns:
- *     0 => Sucess
- *     nonzero => Error
+ *     0 => No error
+ *     non-zero => An error occurred
+ *
+ * If a keytab entry is found, "found" is set to one, and the keytab
+ * entry is returned in "kte".  Otherwise, "found" is zero, and the
+ * value of "kte" is unpredictable.
  */
 static int
-gssd_process_krb5_keytab(krb5_context context, krb5_keytab kt, char *kt_name)
+gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt,
+                       const char *realm, const char *service,
+                       int *found, krb5_keytab_entry *kte)
 {
        krb5_kt_cursor cursor;
-       krb5_keytab_entry kte;
        krb5_error_code code;
        struct gssd_k5_kt_princ *ple;
        int retval = -1;
+       char kt_name[BUFSIZ];
+       char *pname;
+
+       if (found == NULL) {
+               retval = EINVAL;
+               goto out;
+       }
+       *found = 0;
 
        /*
         * Look through each entry in the keytab file and determine
         * if we might want to use it as machine credentials.  If so,
         * save info in the global principal list (gssd_k5_kt_princ_list).
-        * Note: (ple == principal list entry)
         */
+       if ((code = krb5_kt_get_name(context, kt, kt_name, BUFSIZ))) {
+               printerr(0, "ERROR: %s attempting to get keytab name\n",
+                        gssd_k5_err_msg(context, code));
+               retval = code;
+               goto out;
+       }
        if ((code = krb5_kt_start_seq_get(context, kt, &cursor))) {
                printerr(0, "ERROR: %s while beginning keytab scan "
                            "for keytab '%s'\n",
-                       error_message(code), kt_name);
+                       gssd_k5_err_msg(context, code), kt_name);
                retval = code;
                goto out;
        }
 
-       while ((code = krb5_kt_next_entry(context, kt, &kte, &cursor)) == 0) {
-               char *pname;
-               if ((code = krb5_unparse_name(context, kte.principal,
+       while ((code = krb5_kt_next_entry(context, kt, kte, &cursor)) == 0) {
+               if ((code = krb5_unparse_name(context, kte->principal,
                                              &pname))) {
                        printerr(0, "WARNING: Skipping keytab entry because "
-                                   "we failed to unparse principal name: %s\n",
-                                error_message(code));
-                       krb5_kt_free_entry(context, &kte);
+                                "we failed to unparse principal name: %s\n",
+                                gssd_k5_err_msg(context, code));
+                       k5_free_kt_entry(context, kte);
                        continue;
                }
-               printerr(2, "Processing keytab entry for principal '%s'\n",
+               printerr(4, "Processing keytab entry for principal '%s'\n",
                         pname);
-               /* Just use the first keytab entry found for each realm */
-               if ((!gssd_have_realm_ple((void *)&kte.principal->realm)) ) {
-                       printerr(2, "We WILL use this entry (%s)\n", pname);
-                       ple = malloc(sizeof(struct gssd_k5_kt_princ));
+               /* Use the first matching keytab entry found */
+               if ((realm_and_service_match(context, kte->principal, realm,
+                                            service))) {
+                       printerr(4, "We WILL use this entry (%s)\n", pname);
+                       ple = get_ple_by_princ(context, kte->principal);
+                       /*
+                        * Return, don't free, keytab entry if
+                        * we were successful!
+                        */
                        if (ple == NULL) {
-                               printerr(0, "ERROR: could not allocate storage "
-                                           "for principal list entry\n");
-#ifdef HAVE_KRB5
-                               krb5_free_unparsed_name(context, pname);
-#else
-                               free(pname);
-#endif
-                               krb5_kt_free_entry(context, &kte);
                                retval = ENOMEM;
-                               goto out;
-                       }
-                       /* These will be filled in later */
-                       ple->next = NULL;
-                       ple->ccname = NULL;
-                       ple->endtime = 0;
-                       if ((ple->realm =
-#ifdef HAVE_KRB5
-                               strndup(kte.principal->realm.data,
-                                       kte.principal->realm.length))
-#else
-                               strdup(kte.principal->realm))
-#endif
-                                       == NULL) {
-                               printerr(0, "ERROR: %s while copying realm to "
-                                           "principal list entry\n",
-                                        "not enough memory");
-#ifdef HAVE_KRB5
-                               krb5_free_unparsed_name(context, pname);
-#else
-                               free(pname);
-#endif
-                               krb5_kt_free_entry(context, &kte);
-                               retval = ENOMEM;
-                               goto out;
-                       }
-                       if ((code = krb5_copy_principal(context,
-                                       kte.principal, &ple->princ))) {
-                               printerr(0, "ERROR: %s while copying principal "
-                                           "to principal list entry\n",
-                                       error_message(code));
-#ifdef HAVE_KRB5
-                               krb5_free_unparsed_name(context, pname);
-#else
-                               free(pname);
-#endif
-                               krb5_kt_free_entry(context, &kte);
-                               retval = code;
-                               goto out;
-                       }
-                       if (gssd_k5_kt_princ_list == NULL)
-                               gssd_k5_kt_princ_list = ple;
-                       else {
-                               ple->next = gssd_k5_kt_princ_list;
-                               gssd_k5_kt_princ_list = ple;
+                               k5_free_kt_entry(context, kte);
+                       } else {
+                               retval = 0;
+                               *found = 1;
                        }
+                       k5_free_unparsed_name(context, pname);
+                       break;
                }
                else {
-                       printerr(2, "We will NOT use this entry (%s)\n",
+                       printerr(4, "We will NOT use this entry (%s)\n",
                                pname);
                }
-#ifdef HAVE_KRB5
-               krb5_free_unparsed_name(context, pname);
-#else
-               free(pname);
-#endif
-               krb5_kt_free_entry(context, &kte);
+               k5_free_unparsed_name(context, pname);
+               k5_free_kt_entry(context, kte);
        }
 
        if ((code = krb5_kt_end_seq_get(context, kt, &cursor))) {
                printerr(0, "WARNING: %s while ending keytab scan for "
                            "keytab '%s'\n",
-                        error_message(code), kt_name);
+                        gssd_k5_err_msg(context, code), kt_name);
        }
 
        retval = 0;
@@ -574,34 +715,138 @@ gssd_process_krb5_keytab(krb5_context context, krb5_keytab kt, char *kt_name)
 }
 
 /*
- * Depending on the version of Kerberos, we either need to use
- * a private function, or simply set the environment variable.
+ * Find a keytab entry to use for a given target hostname.
+ * Tries to find the most appropriate keytab to use given the
+ * name of the host we are trying to connect with.
  */
-static void
-gssd_set_krb5_ccache_name(char *ccname)
+static int
+find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname,
+                 krb5_keytab_entry *kte)
 {
-#ifdef USE_GSS_KRB5_CCACHE_NAME
-       u_int   maj_stat, min_stat;
+       krb5_error_code code;
+       const char *svcnames[] = { "root", "nfs", "host", NULL };
+       char **realmnames = NULL;
+       char myhostname[NI_MAXHOST], targethostname[NI_MAXHOST];
+       int i, j, retval;
+       char *default_realm = NULL;
+       char *realm;
+       int tried_all = 0, tried_default = 0;
+       krb5_principal princ;
+
+
+       /* Get full target hostname */
+       retval = get_full_hostname(hostname, targethostname,
+                                  sizeof(targethostname));
+       if (retval)
+               goto out;
 
-       printerr(2, "using gss_krb5_ccache_name to select krb5 ccache %s\n",
-                ccname);
-       maj_stat = gss_krb5_ccache_name(&min_stat, ccname, NULL);
-       if (maj_stat != GSS_S_COMPLETE) {
-               printerr(0, "WARNING: gss_krb5_ccache_name with "
-                       "name '%s' failed (%s)\n",
-                       ccname, error_message(min_stat));
+       /* Get full local hostname */
+       retval = gethostname(myhostname, sizeof(myhostname));
+       if (retval) {
+               printerr(1, "%s while getting local hostname\n",
+                        gssd_k5_err_msg(context, retval));
+               goto out;
        }
-#else
+       retval = get_full_hostname(myhostname, myhostname, sizeof(myhostname));
+       if (retval)
+               goto out;
+
+       code = krb5_get_default_realm(context, &default_realm);
+       if (code) {
+               retval = code;
+               printerr(1, "%s while getting default realm name\n",
+                        gssd_k5_err_msg(context, code));
+               goto out;
+       }
+
        /*
-        * Set the KRB5CCNAME environment variable to tell the krb5 code
-        * which credentials cache to use.  (Instead of using the private
-        * function above for which there is no generic gssapi
-        * equivalent.)
+        * Get the realm name(s) for the target hostname.
+        * In reality, this function currently only returns a
+        * single realm, but we code with the assumption that
+        * someday it may actually return a list.
         */
-       printerr(2, "using environment variable to select krb5 ccache %s\n",
-                ccname);
-       setenv("KRB5CCNAME", ccname, 1);
-#endif
+       code = krb5_get_host_realm(context, targethostname, &realmnames);
+       if (code) {
+               printerr(0, "ERROR: %s while getting realm(s) for host '%s'\n",
+                        gssd_k5_err_msg(context, code), targethostname);
+               retval = code;
+               goto out;
+       }
+
+       /*
+        * Try the "appropriate" realm first, and if nothing found for that
+        * realm, try the default realm (if it hasn't already been tried).
+        */
+       i = 0;
+       realm = realmnames[i];
+       while (1) {
+               if (realm == NULL) {
+                       tried_all = 1;
+                       if (!tried_default)
+                               realm = default_realm;
+               }
+               if (tried_all && tried_default)
+                       break;
+               if (strcmp(realm, default_realm) == 0)
+                       tried_default = 1;
+               for (j = 0; svcnames[j] != NULL; j++) {
+                       code = krb5_build_principal_ext(context, &princ,
+                                                       strlen(realm),
+                                                       realm,
+                                                       strlen(svcnames[j]),
+                                                       svcnames[j],
+                                                       strlen(myhostname),
+                                                       myhostname,
+                                                       NULL);
+                       if (code) {
+                               printerr(1, "%s while building principal for "
+                                        "'%s/%s@%s'\n",
+                                        gssd_k5_err_msg(context, code),
+                                        svcnames[j], myhostname, realm);
+                               continue;
+                       }
+                       code = krb5_kt_get_entry(context, kt, princ, 0, 0, kte);
+                       krb5_free_principal(context, princ);
+                       if (code) {
+                               printerr(3, "%s while getting keytab entry for "
+                                        "'%s/%s@%s'\n",
+                                        gssd_k5_err_msg(context, code),
+                                        svcnames[j], myhostname, realm);
+                       } else {
+                               printerr(3, "Success getting keytab entry for "
+                                        "'%s/%s@%s'\n",
+                                        svcnames[j], myhostname, realm);
+                               retval = 0;
+                               goto out;
+                       }
+                       retval = code;
+               }
+               /*
+                * Nothing found with our hostname instance, now look for
+                * names with any instance (they must have an instance)
+                */
+               for (j = 0; svcnames[j] != NULL; j++) {
+                       int found = 0;
+                       code = gssd_search_krb5_keytab(context, kt, realm,
+                                                      svcnames[j], &found, kte);
+                       if (!code && found) {
+                               printerr(3, "Success getting keytab entry for "
+                                        "%s/*@%s\n", svcnames[j], realm);
+                               retval = 0;
+                               goto out;
+                       }
+               }
+               if (!tried_all) {
+                       i++;
+                       realm = realmnames[i];
+               }
+       }
+out:
+       if (default_realm)
+               k5_free_default_realm(context, default_realm);
+       if (realmnames)
+               krb5_free_host_realm(context, realmnames);
+       return retval;
 }
 
 /*==========================*/
@@ -652,96 +897,6 @@ gssd_setup_krb5_machine_gss_ccache(char *ccname)
        gssd_set_krb5_ccache_name(ccname);
 }
 
-/*
- * The first time through this routine, go through the keytab and
- * determine which keys we will try to use as machine credentials.
- * Every time through this routine, try to obtain credentials using
- * the keytab entries selected the first time through.
- *
- * Returns:
- *     0 => obtained one or more credentials
- *     nonzero => error
- *
- */
-
-int
-gssd_refresh_krb5_machine_creds(void)
-{
-       krb5_context context = NULL;
-       krb5_keytab kt = NULL;;
-       krb5_error_code code;
-       int retval = -1;
-       struct gssd_k5_kt_princ *ple;
-       int gotone = 0;
-       static int processed_keytab = 0;
-
-
-       code = krb5_init_context(&context);
-       if (code) {
-               printerr(0, "ERROR: %s while initializing krb5 in "
-                           "gssd_refresh_krb5_machine_creds\n",
-                        error_message(code));
-               retval = code;
-               goto out;
-       }
-
-       printerr(1, "Using keytab file '%s'\n", keytabfile);
-
-       if ((code = krb5_kt_resolve(context, keytabfile, &kt))) {
-               printerr(0, "ERROR: %s while resolving keytab '%s'\n",
-                        error_message(code), keytabfile);
-               goto out;
-       }
-
-       /* Only go through the keytab file once.  Only print messages once. */
-       if (gssd_k5_kt_princ_list == NULL && !processed_keytab) {
-               processed_keytab = 1;
-               gssd_process_krb5_keytab(context, kt, keytabfile);
-               if (gssd_k5_kt_princ_list == NULL) {
-                       printerr(0, "ERROR: No usable keytab entries found in "
-                                   "keytab '%s'\n", keytabfile);
-                       printerr(0, "Do you have a valid keytab entry for "
-                                   "%s/<your.host>@<YOUR.REALM> in "
-                                   "keytab file %s ?\n",
-                                   GSSD_SERVICE_NAME, keytabfile);
-                       printerr(0, "Continuing without (machine) credentials "
-                                   "- nfs4 mounts with Kerberos will fail\n");
-               }
-       }
-
-       /*
-        * If we don't have any keytab entries we liked, then we have a problem
-        */
-       if (gssd_k5_kt_princ_list == NULL) {
-               retval = ENOENT;
-               goto out;
-       }
-
-       /*
-        * Now go through the list of saved entries and get initial
-        * credentials for them (We can't do this while making the
-        * list because it messes up the keytab iteration cursor
-        * when we use the keytab to get credentials.)
-        */
-       for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
-               if ((gssd_get_single_krb5_cred(context, kt, ple)) == 0) {
-                       gotone++;
-               }
-       }
-       if (!gotone) {
-               printerr(0, "ERROR: No usable machine credentials obtained\n");
-               goto out;
-       }
-
-       retval = 0;
-  out:
-       if (kt) krb5_kt_close(context, kt);
-       krb5_free_context(context);
-
-       return retval;
-}
-
-
 /*
  * Return an array of pointers to names of credential cache files
  * which can be used to try to create gss contexts with a server.
@@ -764,18 +919,19 @@ gssd_get_krb5_machine_cred_list(char ***list)
        retval = -1;
        *list = (char **) NULL;
 
-       /* Refresh machine credentials */
-       if ((retval = gssd_refresh_krb5_machine_creds())) {
-               goto out;
-       }
-
        if ((l = (char **) malloc(listsize * sizeof(char *))) == NULL) {
                retval = ENOMEM;
                goto out;
        }
 
+       /* Need to serialize list if we ever become multi-threaded! */
+
        for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
                if (ple->ccname) {
+                       /* Make sure cred is up-to-date before returning it */
+                       retval = gssd_refresh_krb5_machine_credential(NULL, ple);
+                       if (retval)
+                               continue;
                        if (i + 1 > listsize) {
                                listsize += listinc;
                                l = (char **)
@@ -831,7 +987,7 @@ gssd_destroy_krb5_machine_creds(void)
        code = krb5_init_context(&context);
        if (code) {
                printerr(0, "ERROR: %s while initializing krb5\n",
-                        error_message(code));
+                        gssd_k5_err_msg(NULL, code));
                goto out;
        }
 
@@ -841,17 +997,105 @@ gssd_destroy_krb5_machine_creds(void)
                if ((code = krb5_cc_resolve(context, ple->ccname, &ccache))) {
                        printerr(0, "WARNING: %s while resolving credential "
                                    "cache '%s' for destruction\n",
-                                error_message(code), ple->ccname);
+                                gssd_k5_err_msg(context, code), ple->ccname);
                        continue;
                }
 
                if ((code = krb5_cc_destroy(context, ccache))) {
                        printerr(0, "WARNING: %s while destroying credential "
                                    "cache '%s'\n",
-                                error_message(code), ple->ccname);
+                                gssd_k5_err_msg(context, code), ple->ccname);
                }
        }
   out:
        krb5_free_context(context);
 }
 
+/*
+ * Obtain (or refresh if necessary) Kerberos machine credentials
+ */
+int
+gssd_refresh_krb5_machine_credential(char *hostname,
+                                    struct gssd_k5_kt_princ *ple)
+{
+       krb5_error_code code = 0;
+       krb5_context context;
+       krb5_keytab kt = NULL;;
+       int retval = 0;
+
+       if (hostname == NULL && ple == NULL)
+               return EINVAL;
+
+       code = krb5_init_context(&context);
+       if (code) {
+               printerr(0, "ERROR: %s: %s while initializing krb5 context\n",
+                        __FUNCTION__, gssd_k5_err_msg(NULL, code));
+               retval = code;
+               goto out;
+       }
+
+       if ((code = krb5_kt_resolve(context, keytabfile, &kt))) {
+               printerr(0, "ERROR: %s: %s while resolving keytab '%s'\n",
+                        __FUNCTION__, gssd_k5_err_msg(context, code),
+                        keytabfile);
+               goto out;
+       }
+
+       if (ple == NULL) {
+               krb5_keytab_entry kte;
+
+               code = find_keytab_entry(context, kt, hostname, &kte);
+               if (code) {
+                       printerr(0, "ERROR: %s: no usable keytab entry found "
+                                "in keytab %s for connection with host %s\n",
+                                __FUNCTION__, keytabfile, hostname);
+                       retval = code;
+                       goto out;
+               }
+
+               ple = get_ple_by_princ(context, kte.principal);
+               k5_free_kt_entry(context, &kte);
+               if (ple == NULL) {
+                       char *pname;
+                       if ((krb5_unparse_name(context, kte.principal, &pname))) {
+                               pname = NULL;
+                       }
+                       printerr(0, "ERROR: %s: Could not locate or create "
+                                "ple struct for principal %s for connection "
+                                "with host %s\n",
+                                __FUNCTION__, pname ? pname : "<unparsable>",
+                                hostname);
+                       if (pname) k5_free_unparsed_name(context, pname);
+                       goto out;
+               }
+       }
+       retval = gssd_get_single_krb5_cred(context, kt, ple);
+out:
+       if (kt)
+               krb5_kt_close(context, kt);
+       krb5_free_context(context);
+       return retval;
+}
+
+/*
+ * A common routine for getting the Kerberos error message
+ */
+const char *
+gssd_k5_err_msg(krb5_context context, krb5_error_code code)
+{
+       const char *msg = NULL;
+#if HAVE_KRB5_GET_ERROR_MESSAGE
+       if (context != NULL)
+               msg = krb5_get_error_message(context, code);
+#endif
+       if (msg != NULL)
+               return msg;
+#if HAVE_KRB5
+       return error_message(code);
+#else
+       if (context != NULL)
+               return krb5_get_err_text(context, code);
+       else
+               return error_message(code);
+#endif
+}
index da04530fdaeddd9d0adfac4f2ac31da8696643a8..78ad45c040cd59881f82e98ab7a7c68522f0e395 100644 (file)
@@ -5,7 +5,8 @@
 
 /*
  * List of principals from our keytab that we
- * may try to get credentials for
+ * will try to use to obtain credentials
+ * (known as a principal list entry (ple))
  */
 struct gssd_k5_kt_princ {
        struct gssd_k5_kt_princ *next;
@@ -18,13 +19,33 @@ struct gssd_k5_kt_princ {
 
 void gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername);
 int  gssd_get_krb5_machine_cred_list(char ***list);
-int  gssd_refresh_krb5_machine_creds(void);
 void gssd_free_krb5_machine_cred_list(char **list);
 void gssd_setup_krb5_machine_gss_ccache(char *servername);
 void gssd_destroy_krb5_machine_creds(void);
+int  gssd_refresh_krb5_machine_credential(char *hostname,
+                                         struct gssd_k5_kt_princ *ple);
+const char *
+gssd_k5_err_msg(krb5_context context, krb5_error_code code);
 
 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
 int limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid);
 #endif
 
+/*
+ * Hide away some of the MIT vs. Heimdal differences
+ * here with macros...
+ */
+
+#ifdef HAVE_KRB5
+#define k5_free_unparsed_name(ctx, name)       krb5_free_unparsed_name((ctx), (name))
+#define k5_free_default_realm(ctx, realm)      krb5_free_default_realm((ctx), (realm))
+#define k5_free_kt_entry(ctx, kte)             krb5_free_keytab_entry_contents((ctx),(kte))
+#else  /* Heimdal */
+#define k5_free_unparsed_name(ctx, name)       free(name)
+#define k5_free_default_realm(ctx, realm)      free(realm)
+#define k5_free_kt_entry(ctx, kte)             krb5_kt_free_entry((ctx),(kte))
+#undef USE_GSS_KRB5_CCACHE_NAME
+#define USE_GSS_KRB5_CCACHE_NAME 1
+#endif
+
 #endif /* KRB5_UTIL_H */
index 85e89753c760b81044a2b5b9d535ba3c7d467d6c..d0688432b04aa8de76f1f636e137eeb343ce98e5 100644 (file)
@@ -397,6 +397,9 @@ void nfsd_fh(FILE *f)
                                dev_missing ++;
                        if (stat(path, &stb) != 0)
                                continue;
+                       if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) {
+                               continue;
+                       }
                        switch(fsidtype){
                        case FSID_DEV:
                        case FSID_MAJOR_MINOR:
@@ -620,9 +623,12 @@ void nfsd_export(FILE *f)
        }
 
        if (found) {
-               if (dump_to_cache(f, dom, path, &found->m_export) < 0)
+               if (dump_to_cache(f, dom, path, &found->m_export) < 0) {
+                       xlog(L_WARNING,
+                            "Cannot export %s, possibly unsupported filesystem"
+                            " or fsid= required", path);
                        dump_to_cache(f, dom, path, NULL);
-               else
+               else
                        mountlist_add(dom, path);
        } else {
                dump_to_cache(f, dom, path, NULL);
@@ -697,9 +703,14 @@ int cache_export_ent(char *domain, struct exportent *exp, char *path)
                return -1;
 
        err = dump_to_cache(f, domain, exp->e_path, exp);
+       if (err) {
+               xlog(L_WARNING,
+                    "Cannot export %s, possibly unsupported filesystem or"
+                    " fsid= required", exp->e_path);
+       }
        mountlist_add(domain, exp->e_path);
 
-       while ((exp->e_flags & NFSEXP_CROSSMOUNT) && path) {
+       while (err == 0 && (exp->e_flags & NFSEXP_CROSSMOUNT) && path) {
                /* really an 'if', but we can break out of
                 * a 'while' more easily */
                /* Look along 'path' for other filesystems
@@ -717,16 +728,17 @@ int cache_export_ent(char *domain, struct exportent *exp, char *path)
                dev = stb.st_dev;
                while(path[l] == '/') {
                        char c;
-                       int err;
+                       /* errors for submount should fail whole filesystem */
+                       int err2;
 
                        l++;
                        while (path[l] != '/' && path[l])
                                l++;
                        c = path[l];
                        path[l] = 0;
-                       err = lstat(path, &stb);
+                       err2 = lstat(path, &stb);
                        path[l] = c;
-                       if (err < 0)
+                       if (err2 < 0)
                                break;
                        if (stb.st_dev == dev)
                                continue;
index b19bb901d8ee2305d38b7c30e0a00ac5fdb8ebbb..d5d036e9fc28357b3ef703f3da3005f7cee0ad06 100644 (file)
@@ -9,8 +9,10 @@
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
+#include "misc.h"
 #include "statd.h"
 #include "notlist.h"
+#include <arpa/inet.h>
 
 /* Callback notify list. */
 /* notify_list *cbnl = NULL; ... never used */
index b95b0ad51692e7b6708a2a94d289ad7484ccbbd1..c447a269bb9b86f2c82091ff7844e6cb693977ac 100644 (file)
@@ -40,6 +40,7 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
                        *my_name  = argp->mon_id.my_id.my_name;
        struct my_id    *id = &argp->mon_id.my_id;
        char            *path;
+       char            *cp;
        int             fd;
        notify_list     *clnt;
        struct in_addr  my_addr;
@@ -70,7 +71,6 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
                goto failure;
        }
        my_addr.s_addr = htonl(INADDR_LOOPBACK);
-       my_name = "127.0.0.1";
 
        /* 2.   Reject any registrations for non-lockd services.
         *
@@ -127,6 +127,11 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
                goto failure;
        }
 
+       /* my_name must not have white space */
+       for (cp=my_name ; *cp ; cp++)
+               if (*cp == ' ' || *cp == '\t' || *cp == '\r' || *cp == '\n')
+                       *cp = '_';
+
        /*
         * Hostnames checked OK.
         * Now choose a hostname to use for matching.  We cannot
@@ -166,9 +171,7 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
                                mon_name, my_name);
 
                        /* But we'll let you pass anyway. */
-                       result.res_stat = STAT_SUCC;
-                       result.state = MY_STATE;
-                       return (&result);
+                       goto success;
                }
                clnt = NL_NEXT(clnt);
        }
@@ -204,7 +207,7 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
                goto failure;
        }
        {
-               char buf[LINELEN + 1 + SM_MAXSTRLEN + 2];
+               char buf[LINELEN + 1 + SM_MAXSTRLEN*2 + 4];
                char *e;
                int i;
                e = buf + sprintf(buf, "%08x %08x %08x %08x ",
@@ -213,7 +216,7 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
                for (i=0; i<SM_PRIV_SIZE; i++)
                        e += sprintf(e, "%02x", 0xff & (argp->priv[i]));
                if (e+1-buf != LINELEN) abort();
-               e += sprintf(e, " %s\n", mon_name);
+               e += sprintf(e, " %s %s\n", mon_name, my_name);
                write(fd, buf, e-buf);
        }
 
@@ -222,10 +225,20 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
        ha_callout("add-client", mon_name, my_name, -1);
        nlist_insert(&rtnl, clnt);
        close(fd);
-
+       dprintf(N_DEBUG, "MONITORING %s for %s", mon_name, my_name);
+ success:
        result.res_stat = STAT_SUCC;
+       /* SUN's sm_inter.x says this should be "state number of local site".
+        * X/Open says '"state" will be contain the state of the remote NSM.'
+        * href=http://www.opengroup.org/onlinepubs/9629799/SM_MON.htm
+        * Linux lockd currently (2.6.21 and prior) ignores whatever is
+        * returned, and given the above contraction, it probably always will..
+        * So we just return what we always returned.  If possible, we
+        * have already told lockd about our state number via a sysctl.
+        * If lockd wants the remote state, it will need to
+        * use SM_STAT (and prayer).
+        */
        result.state = MY_STATE;
-       dprintf(N_DEBUG, "MONITORING %s for %s", mon_name, my_name);
        return (&result);
 
 failure:
@@ -258,6 +271,7 @@ void load_state(void)
                while (fgets(buf, sizeof(buf), f) != NULL) {
                        int addr, proc, prog, vers;
                        char priv[SM_PRIV_SIZE];
+                       char *monname, *myname;
                        char *b;
                        int i;
                        notify_list     *clnt;
@@ -266,7 +280,7 @@ void load_state(void)
                        b = strchr(buf, '\n');
                        if (b) *b = 0;
                        sscanf(buf, "%x %x %x %x ",
-                              &addr, &prog, &vers, &proc);
+                              &addr, &prog, &vers, &proc, myname);
                        b = buf+36;
                        for (i=0; i<SM_PRIV_SIZE; i++) {
                                sscanf(b, "%2x", &p);
@@ -274,7 +288,12 @@ void load_state(void)
                                b += 2;
                        }
                        b++;
-                       clnt = nlist_new("127.0.0.1", b, 0);
+                       monname = b;
+                       while (*b && *b != ' ') b++;
+                       if (*b) *b++ = '\0';
+                       while (*b == ' ') b++;
+                       myname = b;
+                       clnt = nlist_new(myname, monname, 0);
                        if (!clnt)
                                break;
                        NL_ADDR(clnt).s_addr = addr;
@@ -309,6 +328,7 @@ sm_unmon_1_svc(struct mon_id *argp, struct svc_req *rqstp)
        char            *mon_name = argp->mon_name,
                        *my_name  = argp->my_id.my_name;
        struct my_id    *id = &argp->my_id;
+       char            *cp;
 #ifdef RESTRICTED_STATD
        struct in_addr  caller;
 #endif
@@ -327,8 +347,12 @@ sm_unmon_1_svc(struct mon_id *argp, struct svc_req *rqstp)
                        inet_ntoa(caller));
                goto failure;
        }
-       my_name = "127.0.0.1";
 #endif
+       /* my_name must not have white space */
+       for (cp=my_name ; *cp ; cp++)
+               if (*cp == ' ' || *cp == '\t' || *cp == '\r' || *cp == '\n')
+                       *cp = '_';
+
 
        /* Check if we're monitoring anyone. */
        if (!(clnt = rtnl)) {
@@ -394,7 +418,6 @@ sm_unmon_all_1_svc(struct my_id *argp, struct svc_req *rqstp)
                        inet_ntoa(caller));
                goto failure;
        }
-       my_name = "127.0.0.1";
 #endif
 
        result.state = MY_STATE;
index 816a6f368fa208d12313de3f1721d297401cf42a..eb1919ab363d221d481069d6ca9b722577729110 100644 (file)
@@ -62,25 +62,37 @@ int
 statd_get_socket(void)
 {
        struct sockaddr_in      sin;
+       struct servent *se;
+       int loopcnt = 100;
 
        if (sockfd >= 0)
                return sockfd;
 
-       if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
-               note(N_CRIT, "Can't create socket: %m");
-               return -1;
-       }
+       while (loopcnt-- > 0) {
 
-       FD_SET(sockfd, &SVC_FDSET);
+               if (sockfd >= 0) close(sockfd);
 
-       memset(&sin, 0, sizeof(sin));
-       sin.sin_family = AF_INET;
-       sin.sin_addr.s_addr = INADDR_ANY;
+               if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+                       note(N_CRIT, "Can't create socket: %m");
+                       return -1;
+               }
 
-       if (bindresvport(sockfd, &sin) < 0) {
-               dprintf(N_WARNING,
-                       "process_hosts: can't bind to reserved port\n");
+
+               memset(&sin, 0, sizeof(sin));
+               sin.sin_family = AF_INET;
+               sin.sin_addr.s_addr = INADDR_ANY;
+
+               if (bindresvport(sockfd, &sin) < 0) {
+                       dprintf(N_WARNING,
+                               "process_hosts: can't bind to reserved port\n");
+                       break;
+               }
+               se = getservbyport(sin.sin_port, "udp");
+               if (se == NULL)
+                       break;
+               /* rather not use that port, try again */
        }
+       FD_SET(sockfd, &SVC_FDSET);
        return sockfd;
 }
 
index a94876d1b160190c819d6b7ae82d9caf800a5805..98c03f9d142656906d36bb7e558851d6b5328c6c 100644 (file)
@@ -88,6 +88,7 @@ static struct addrinfo        *host_lookup(int, const char *);
 void                   nsm_log(int fac, const char *fmt, ...);
 static int             record_pid();
 static void            drop_privs(void);
+static void set_kernel_nsm_state(int state);
 
 static struct nsm_host *       hosts = NULL;
 
@@ -166,6 +167,10 @@ usage:             fprintf(stderr,
        backup_hosts(_SM_DIR_PATH, _SM_BAK_PATH);
        get_hosts(_SM_BAK_PATH);
 
+       /* Get and update the NSM state. This will call sync() */
+       nsm_state = nsm_get_state(opt_update_state);
+       set_kernel_nsm_state(nsm_state);
+
        if (!opt_debug) {
                if (!opt_quiet)
                        printf("Backgrounding to notify hosts...\n");
@@ -184,9 +189,6 @@ usage:              fprintf(stderr,
                close(2);
        }
 
-       /* Get and update the NSM state. This will call sync() */
-       nsm_state = nsm_get_state(opt_update_state);
-
        notify();
 
        if (hosts) {
@@ -213,7 +215,9 @@ notify(void)
        nsm_address local_addr;
        time_t  failtime = 0;
        int     sock = -1;
+       int retry_cnt = 0;
 
+ retry:
        sock = socket(AF_INET, SOCK_DGRAM, 0);
        if (sock < 0) {
                perror("socket");
@@ -246,7 +250,16 @@ notify(void)
                        exit(1);
                }
        } else {
-               (void) bindresvport(sock, (struct sockaddr_in *) &local_addr);
+               struct servent *se;
+               struct sockaddr_in *sin = (struct sockaddr_in *)&local_addr;
+               (void) bindresvport(sock, sin);
+               /* try to avoid known ports */
+               se = getservbyport(sin->sin_port, "udp");
+               if (se && retry_cnt < 100) {
+                       retry_cnt++;
+                       close(sock);
+                       goto retry;
+               }
        }
 
        if (opt_max_retry)
@@ -758,3 +771,16 @@ static void drop_privs(void)
                exit(1);
        }
 }
+
+static void set_kernel_nsm_state(int state)
+{
+       int fd;
+
+       fd = open("/proc/sys/fs/nfs/nsm_local_state",O_WRONLY);
+       if (fd >= 0) {
+               char buf[20];
+               snprintf(buf, sizeof(buf), "%d", state);
+               write(fd, buf, strlen(buf));
+               close(fd);
+       }
+}
index bcd3550f34d54e1dbcf5feaa24a46c507256195d..799239f1d956f668c0cf2c79c98175914740b0b4 100644 (file)
  * status."  My implementation is operative; it returns 'STAT_SUCC'
  * whenever it can resolve the hostname that it's being asked to
  * monitor, and returns 'STAT_FAIL' otherwise.
+ *
+ * sm_inter.x says the 'state' returned should be
+ *   "state number of site sm_name".  It is not clear how to get this.
+ * X/Open says:
+ *   STAT_SUCC
+ *      The NSM will monitor the given host. "sm_stat_res.state" contains
+ *      the state of the NSM.
+ * Which implies that 'state' is the state number of the *local* NSM.
+ * href=http://www.opengroup.org/onlinepubs/9629799/SM_STAT.htm
+ *
+ * We return the *local* state as
+ *   1/ We have easy access to it.
+ *   2/ It might be useful to a remote client who needs it and has no
+ *      other way to get it.
+ *   3/ That's what we always did in the past.
  */
 struct sm_stat_res * 
 sm_stat_1_svc (struct sm_name *argp, struct svc_req *rqstp)
index 091ced9316e9220408fb7799004c297b8c0fd9ec..8337b64b993918fb8704daeb49cae711447779d3 100644 (file)
@@ -76,6 +76,7 @@ static struct option longopts[] =
 
 extern void sm_prog_1 (struct svc_req *, register SVCXPRT *);
 extern int statd_get_socket(void);
+static void load_state_number(void);
 
 #ifdef SIMULATIONS
 extern void simulator (int, char **);
@@ -483,7 +484,7 @@ int main (int argc, char **argv)
         * pass on any SM_NOTIFY that arrives
         */
        load_state();
-
+       load_state_number();
        pmap_unset (SM_PROG, SM_VERS);
 
        /* this registers both UDP and TCP services */
@@ -526,3 +527,23 @@ int main (int argc, char **argv)
        }
        return 0;
 }
+
+static void
+load_state_number(void)
+{
+       int fd;
+
+       if ((fd = open(SM_STAT_PATH, O_RDONLY)) == -1)
+               return;
+
+       read(fd, &MY_STATE, sizeof(MY_STATE));
+       close(fd);
+       fd = open("/proc/sys/fs/nfs/nsm_local_state",O_WRONLY);
+       if (fd >= 0) {
+               char buf[20];
+               snprintf(buf, sizeof(buf), "%d", MY_STATE);
+               write(fd, buf, strlen(buf));
+               close(fd);
+       }
+
+}