1/4 Cleans up a minor memory leak while processing the keytab.
2/4 Adds a new "-n" option to specify that machine credentials
should not be used for all root accesses.
3/4 No longer limits machine credentials to only "nfs/<hostname>".
Any key in the keytab (i.e. "host/<hostname>") can be used for
machine credentials.
4/4 Creates separate paths for the base pipefs directory and the
nfs subdirectory. This is needed by future changes.
K.C.
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
NFS maillist - [email protected]
https://lists.sourceforge.net/lists/listinfo/nfs
From: Kevin Coffman <[email protected]>
Free keytab entries while processing keytab file.
Signed-off-by: Kevin Coffman <[email protected]>
---
utils/gssd/krb5_util.c | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
index 6af2869..cd777e4 100644
--- a/utils/gssd/krb5_util.c
+++ b/utils/gssd/krb5_util.c
@@ -485,6 +485,7 @@ gssd_process_krb5_keytab(krb5_context co
printerr(0, "WARNING: Skipping keytab entry because "
"we failed to unparse principal name: %s\n",
error_message(code));
+ krb5_kt_free_entry(context, &kte);
continue;
}
printerr(2, "Processing keytab entry for principal '%s'\n",
@@ -510,6 +511,7 @@ #ifdef HAVE_KRB5
#else
free(pname);
#endif
+ krb5_kt_free_entry(context, &kte);
retval = ENOMEM;
goto out;
}
@@ -533,6 +535,7 @@ #ifdef HAVE_KRB5
#else
free(pname);
#endif
+ krb5_kt_free_entry(context, &kte);
retval = ENOMEM;
goto out;
}
@@ -546,6 +549,7 @@ #ifdef HAVE_KRB5
#else
free(pname);
#endif
+ krb5_kt_free_entry(context, &kte);
retval = code;
goto out;
}
@@ -565,6 +569,7 @@ #ifdef HAVE_KRB5
#else
free(pname);
#endif
+ krb5_kt_free_entry(context, &kte);
}
if ((code = krb5_kt_end_seq_get(context, kt, &cursor))) {
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
NFS maillist - [email protected]
https://lists.sourceforge.net/lists/listinfo/nfs
From: Kevin Coffman <[email protected]>
Add a new option ("-n") to rpc.gssd to indicate that accesses as root
(uid 0) should not use machine credentials, but should instead use
"normal" Kerberos credentials obtained by root.
This change was prompted by a suggestion and patch from Daniel
Muntz <[email protected]>. That patch suggested trying "normal"
credentials first and falling back to using machine creds for
uid 0 if normal creds failed.
This opens up the case where root may have credentials as "foo@REALM"
and begins accessing files. Then the context using those credentials
expires and must be renewed. If the credentials are now expired, then
root's new context would fall back and be created with the machine
credentials.
Instead, this patch insists that the administrator choose to use either
machine credentials for accesses by uid 0 (the default behavior, as
it was before) or "normal" credentials. In the latter case, arrangements
must be made to obtain credentials before attempting a mount. There
should be no doubts which credentials are used for uid 0.
Signed-off-by: Kevin Coffman <[email protected]>
---
utils/gssd/gssd.c | 14 ++++++--
utils/gssd/gssd.h | 1 +
utils/gssd/gssd.man | 26 +++++++++------
utils/gssd/gssd_proc.c | 82 +++++++++++++++++++++++++-----------------------
4 files changed, 69 insertions(+), 54 deletions(-)
diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c
index 9988fe4..319dca4 100644
--- a/utils/gssd/gssd.c
+++ b/utils/gssd/gssd.c
@@ -57,12 +57,14 @@ char pipefsdir[PATH_MAX] = GSSD_PIPEFS_D
char keytabfile[PATH_MAX] = GSSD_DEFAULT_KEYTAB_FILE;
char ccachedir[PATH_MAX] = GSSD_DEFAULT_CRED_DIR;
int use_memcache = 0;
+int root_uses_machine_creds = 1;
void
sig_die(int signal)
{
/* destroy krb5 machine creds */
- gssd_destroy_krb5_machine_creds();
+ if (root_uses_machine_creds)
+ gssd_destroy_krb5_machine_creds();
printerr(1, "exiting on signal %d\n", signal);
exit(1);
}
@@ -78,7 +80,7 @@ sig_hup(int signal)
static void
usage(char *progname)
{
- fprintf(stderr, "usage: %s [-f] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir]\n",
+ fprintf(stderr, "usage: %s [-f] [-n] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir]\n",
progname);
exit(1);
}
@@ -93,7 +95,7 @@ main(int argc, char *argv[])
extern char *optarg;
char *progname;
- while ((opt = getopt(argc, argv, "fvrmMp:k:d:")) != -1) {
+ while ((opt = getopt(argc, argv, "fvrmnMp:k:d:")) != -1) {
switch (opt) {
case 'f':
fg = 1;
@@ -104,6 +106,9 @@ main(int argc, char *argv[])
case 'M':
use_memcache = 1;
break;
+ case 'n':
+ root_uses_machine_creds = 0;
+ break;
case 'v':
verbosity++;
break;
@@ -160,7 +165,8 @@ #endif
signal(SIGHUP, sig_hup);
/* Process keytab file and get machine credentials */
- gssd_refresh_krb5_machine_creds();
+ if (root_uses_machine_creds)
+ gssd_refresh_krb5_machine_creds();
gssd_run();
printerr(0, "gssd_run returned!\n");
diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h
index ec91e89..3622b48 100644
--- a/utils/gssd/gssd.h
+++ b/utils/gssd/gssd.h
@@ -62,6 +62,7 @@ extern char pipefsdir[PATH_MAX];
extern char keytabfile[PATH_MAX];
extern char ccachedir[PATH_MAX];
extern int use_memcache;
+extern int root_uses_machine_creds;
TAILQ_HEAD(clnt_list_head, clnt_info) clnt_list;
diff --git a/utils/gssd/gssd.man b/utils/gssd/gssd.man
index 250d26f..1a30d69 100644
--- a/utils/gssd/gssd.man
+++ b/utils/gssd/gssd.man
@@ -2,11 +2,11 @@
.\" rpc.gssd(8)
.\"
.\" Copyright (C) 2003 J. Bruce Fields <[email protected]>
-.TH rpc.gssd 8 "17 Mar 2003"
+.TH rpc.gssd 8 "14 Mar 2007"
.SH NAME
rpc.gssd \- rpcsec_gss daemon
.SH SYNOPSIS
-.B "rpc.gssd [-f] [-k keytab] [-p pipefsdir] [-v] [-r] [-d ccachedir]"
+.B "rpc.gssd [-f] [-n] [-k keytab] [-p pipefsdir] [-v] [-r] [-d ccachedir]"
.SH DESCRIPTION
The rpcsec_gss protocol gives a means of using the gss-api generic security
api to provide security for protocols using rpc (in particular, nfs). Before
@@ -25,6 +25,19 @@ Runs
.B rpc.gssd
in the foreground and sends output to stderr (as opposed to syslogd)
.TP
+.B -n
+By default,
+.B rpc.gssd
+treats accesses by the user with UID 0 specially, and uses
+"machine credentials" for all accesses by that user which
+require Kerberos authentication.
+With the \-n option, "machine credentials" will not be used
+for accesses by UID 0. Instead, credentials must be obtained
+manually like all other users. Use of this option means that
+"root" must manually obtain Kerberos credentials before
+attemtpting to mount an nfs filesystem requiring Kerberos
+authentication.
+.TP
.B -k keytab
Tells
.B rpc.gssd
@@ -32,15 +45,6 @@ to use the keys for principals nfs/hostn
.I keytab
to obtain machine credentials.
The default value is "/etc/krb5.keytab".
-.\".TP
-.\".B -m
-.\"Ordinarily,
-.\".B rpc.gssd
-.\"looks for a cached ticket for user $UID in /tmp/krb5cc_$UID.
-.\"With the -m option, the user with uid 0 will be treated specially, and will
-.\"be mapped instead to the credentials for the principal nfs/hostname found in
-.\"the keytab file.
-.\"(This option is now the default and is ignored if specified.)
.TP
.B -p path
Tells
diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c
index 68d645d..04de4e6 100644
--- a/utils/gssd/gssd_proc.c
+++ b/utils/gssd/gssd_proc.c
@@ -675,6 +675,7 @@ handle_krb5_upcall(struct clnt_info *clp
gss_buffer_desc token;
char **credlist = NULL;
char **ccname;
+ int create_resp = -1;
printerr(1, "handling krb5 upcall\n");
@@ -688,49 +689,52 @@ handle_krb5_upcall(struct clnt_info *clp
goto out;
}
- if (uid == 0) {
- int success = 0;
-
- /*
- * 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);
- goto out_return_error;
- }
- for (ccname = credlist; ccname && *ccname; ccname++) {
- gssd_setup_krb5_machine_gss_ccache(*ccname);
- if ((create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
- AUTHTYPE_KRB5)) == 0) {
- /* Success! */
- success++;
- break;
- }
- printerr(2, "WARNING: Failed to create krb5 context "
- "for user with uid %d with credentials "
- "cache %s for server %s\n",
- uid, *ccname, clp->servername);
- }
- gssd_free_krb5_machine_cred_list(credlist);
- if (!success) {
- printerr(0, "WARNING: Failed to create krb5 context "
- "for user with uid %d with any "
- "credentials cache for server %s\n",
- uid, clp->servername);
- goto out_return_error;
- }
- }
- else {
+ if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0)) {
/* Tell krb5 gss which credentials cache to use */
gssd_setup_krb5_user_gss_ccache(uid, clp->servername);
- if ((create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
- AUTHTYPE_KRB5)) != 0) {
+ create_resp = create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
+ AUTHTYPE_KRB5);
+ }
+ if (create_resp != 0) {
+ if (uid == 0 && root_uses_machine_creds == 1) {
+ int success = 0;
+
+ /*
+ * 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);
+ goto out_return_error;
+ }
+ for (ccname = credlist; ccname && *ccname; ccname++) {
+ gssd_setup_krb5_machine_gss_ccache(*ccname);
+ if ((create_auth_rpc_client(clp, &rpc_clnt,
+ &auth, uid,
+ AUTHTYPE_KRB5)) == 0) {
+ /* Success! */
+ success++;
+ break;
+ }
+ printerr(2, "WARNING: Failed to create krb5 context "
+ "for user with uid %d with credentials "
+ "cache %s for server %s\n",
+ uid, *ccname, clp->servername);
+ }
+ gssd_free_krb5_machine_cred_list(credlist);
+ if (!success) {
+ printerr(0, "WARNING: Failed to create krb5 context "
+ "for user with uid %d with any "
+ "credentials cache for server %s\n",
+ uid, clp->servername);
+ goto out_return_error;
+ }
+ } else {
printerr(0, "WARNING: Failed to create krb5 context "
- "for user with uid %d for server %s\n",
+ "for user with uid %d for server %s\n",
uid, clp->servername);
goto out_return_error;
}
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
NFS maillist - [email protected]
https://lists.sourceforge.net/lists/listinfo/nfs
From: Kevin Coffman <[email protected]>
Don't restrict machine credentials to be "nfs/<machine.name>".
Use any usable credentials contained in the keytab file.
[We actually attempt to use the first entry found for each
realm, not every entry, in the keytab.]
Signed-off-by: Kevin Coffman <[email protected]>
---
utils/gssd/gssd.man | 12 ++++++++++--
utils/gssd/krb5_util.c | 22 ++++++----------------
2 files changed, 16 insertions(+), 18 deletions(-)
diff --git a/utils/gssd/gssd.man b/utils/gssd/gssd.man
index 1a30d69..f2ecd69 100644
--- a/utils/gssd/gssd.man
+++ b/utils/gssd/gssd.man
@@ -41,10 +41,18 @@ authentication.
.B -k keytab
Tells
.B rpc.gssd
-to use the keys for principals nfs/hostname in
+to use the keys found in
.I keytab
-to obtain machine credentials.
+to obtain "machine credentials".
The default value is "/etc/krb5.keytab".
+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.
.TP
.B -p path
Tells
diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
index cd777e4..f1682b8 100644
--- a/utils/gssd/krb5_util.c
+++ b/utils/gssd/krb5_util.c
@@ -448,7 +448,7 @@ #endif
/*
* Process the given keytab file and create a list of principals we
- * might use to perform mount operations.
+ * might use as machine credentials.
*
* Returns:
* 0 => Sucess
@@ -465,9 +465,8 @@ gssd_process_krb5_keytab(krb5_context co
/*
* Look through each entry in the keytab file and determine
- * if we might want to use it later to do a mount. If so,
- * save info in the global principal list
- * (gssd_k5_kt_princ_list).
+ * 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_start_seq_get(context, kt, &cursor))) {
@@ -490,18 +489,9 @@ gssd_process_krb5_keytab(krb5_context co
}
printerr(2, "Processing keytab entry for principal '%s'\n",
pname);
-#ifdef HAVE_KRB5
- if ( (kte.principal->data[0].length == GSSD_SERVICE_NAME_LEN) &&
- (strncmp(kte.principal->data[0].data, GSSD_SERVICE_NAME,
- GSSD_SERVICE_NAME_LEN) == 0) &&
-#else
- if ( (strlen(kte.principal->name.name_string.val[0]) == GSSD_SERVICE_NAME_LEN) &&
- (strncmp(kte.principal->name.name_string.val[0], GSSD_SERVICE_NAME,
- GSSD_SERVICE_NAME_LEN) == 0) &&
-
-#endif
- (!gssd_have_realm_ple((void *)&kte.principal->realm)) ) {
- printerr(2, "We will use this entry (%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));
if (ple == NULL) {
printerr(0, "ERROR: could not allocate storage "
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
NFS maillist - [email protected]
https://lists.sourceforge.net/lists/listinfo/nfs
From: Kevin Coffman <[email protected]>
Future work needs access to the base pipefs directory rather than
the nfs subdirectory. Create two separate paths called
pipefs_dir and pipefs_nfsdir with the name of each.
Signed-off-by: Kevin Coffman <[email protected]>
---
utils/gssd/gssd.c | 15 ++++++++-------
utils/gssd/gssd.h | 3 ++-
utils/gssd/gssd_main_loop.c | 4 ++--
utils/gssd/gssd_proc.c | 22 +++++++++++-----------
4 files changed, 23 insertions(+), 21 deletions(-)
diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c
index 319dca4..747637c 100644
--- a/utils/gssd/gssd.c
+++ b/utils/gssd/gssd.c
@@ -53,7 +53,8 @@ #include "err_util.h"
#include "gss_util.h"
#include "krb5_util.h"
-char pipefsdir[PATH_MAX] = GSSD_PIPEFS_DIR;
+char pipefs_dir[PATH_MAX] = GSSD_PIPEFS_DIR;
+char pipefs_nfsdir[PATH_MAX] = GSSD_PIPEFS_DIR;
char keytabfile[PATH_MAX] = GSSD_DEFAULT_KEYTAB_FILE;
char ccachedir[PATH_MAX] = GSSD_DEFAULT_CRED_DIR;
int use_memcache = 0;
@@ -116,8 +117,8 @@ main(int argc, char *argv[])
rpc_verbosity++;
break;
case 'p':
- strncpy(pipefsdir, optarg, sizeof(pipefsdir));
- if (pipefsdir[sizeof(pipefsdir)-1] != '\0')
+ strncpy(pipefs_dir, optarg, sizeof(pipefs_dir));
+ if (pipefs_dir[sizeof(pipefs_dir)-1] != '\0')
errx(1, "pipefs path name too long");
break;
case 'k':
@@ -135,10 +136,10 @@ main(int argc, char *argv[])
break;
}
}
- strncat(pipefsdir + strlen(pipefsdir), "/" GSSD_SERVICE_NAME,
- sizeof(pipefsdir)-strlen(pipefsdir));
- if (pipefsdir[sizeof(pipefsdir)-1] != '\0')
- errx(1, "pipefs path name too long");
+ snprintf(pipefs_nfsdir, sizeof(pipefs_nfsdir), "%s/%s",
+ pipefs_dir, GSSD_SERVICE_NAME);
+ if (pipefs_nfsdir[sizeof(pipefs_nfsdir)-1] != '\0')
+ errx(1, "pipefs_nfsdir path name too long");
if ((progname = strrchr(argv[0], '/')))
progname++;
diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h
index 3622b48..6b96ce1 100644
--- a/utils/gssd/gssd.h
+++ b/utils/gssd/gssd.h
@@ -58,7 +58,8 @@ enum {AUTHTYPE_KRB5, AUTHTYPE_SPKM3, AUT
-extern char pipefsdir[PATH_MAX];
+extern char pipefs_dir[PATH_MAX];
+extern char pipefs_nfsdir[PATH_MAX];
extern char keytabfile[PATH_MAX];
extern char ccachedir[PATH_MAX];
extern int use_memcache;
diff --git a/utils/gssd/gssd_main_loop.c b/utils/gssd/gssd_main_loop.c
index a086bb3..0559f7b 100644
--- a/utils/gssd/gssd_main_loop.c
+++ b/utils/gssd/gssd_main_loop.c
@@ -106,9 +106,9 @@ gssd_run()
dn_act.sa_flags = SA_SIGINFO;
sigaction(DNOTIFY_SIGNAL, &dn_act, NULL);
- if ((fd = open(pipefsdir, O_RDONLY)) == -1) {
+ if ((fd = open(pipefs_nfsdir, O_RDONLY)) == -1) {
printerr(0, "ERROR: failed to open %s: %s\n",
- pipefsdir, strerror(errno));
+ pipefs_nfsdir, strerror(errno));
exit(1);
}
fcntl(fd, F_SETSIG, DNOTIFY_SIGNAL);
diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c
index 04de4e6..3b190f2 100644
--- a/utils/gssd/gssd_proc.c
+++ b/utils/gssd/gssd_proc.c
@@ -80,19 +80,19 @@ #include "context.h"
* with an index into pollarray[], and other basic data about that client.
*
* Directory structure: created by the kernel nfs client
- * /pipefsdir/clntXX : one per rpc_clnt struct in the kernel
- * /pipefsdir/clntXX/krb5 : read uid for which kernel wants
- * a context, write the resulting context
- * /pipefsdir/clntXX/info : stores info such as server name
+ * {pipefs_nfsdir}/clntXX : one per rpc_clnt struct in the kernel
+ * {pipefs_nfsdir}/clntXX/krb5 : read uid for which kernel wants
+ * a context, write the resulting context
+ * {pipefs_nfsdir}/clntXX/info : stores info such as server name
*
* Algorithm:
- * Poll all /pipefsdir/clntXX/krb5 files. When ready, data read
+ * Poll all {pipefs_nfsdir}/clntXX/krb5 files. When ready, data read
* is a uid; performs rpcsec_gss context initialization protocol to
* get a cred for that user. Writes result to corresponding krb5 file
* in a form the kernel code will understand.
* In addition, we make sure we are notified whenever anything is
- * created or destroyed in pipefsdir/ or in an of the clntXX directories,
- * and rescan the whole pipefsdir when this happens.
+ * created or destroyed in {pipefs_nfsdir} or in an of the clntXX directories,
+ * and rescan the whole {pipefs_nfsdir} when this happens.
*/
struct pollfd * pollarray;
@@ -389,16 +389,16 @@ update_client_list(void)
struct dirent **namelist;
int i, j;
- if (chdir(pipefsdir) < 0) {
+ if (chdir(pipefs_nfsdir) < 0) {
printerr(0, "ERROR: can't chdir to %s: %s\n",
- pipefsdir, strerror(errno));
+ pipefs_nfsdir, strerror(errno));
return -1;
}
- j = scandir(pipefsdir, &namelist, NULL, alphasort);
+ j = scandir(pipefs_nfsdir, &namelist, NULL, alphasort);
if (j < 0) {
printerr(0, "ERROR: can't scandir %s: %s\n",
- pipefsdir, strerror(errno));
+ pipefs_nfsdir, strerror(errno));
return -1;
}
update_old_clients(namelist, j);
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
NFS maillist - [email protected]
https://lists.sourceforge.net/lists/listinfo/nfs
On Friday March 16, [email protected] wrote:
> 1/4 Cleans up a minor memory leak while processing the keytab.
> 2/4 Adds a new "-n" option to specify that machine credentials
> should not be used for all root accesses.
> 3/4 No longer limits machine credentials to only "nfs/<hostname>".
> Any key in the keytab (i.e. "host/<hostname>") can be used for
> machine credentials.
> 4/4 Creates separate paths for the base pipefs directory and the
> nfs subdirectory. This is needed by future changes.
Complete with man-page updates - excellent!
Applied - thanks.
NeilBrown
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
NFS maillist - [email protected]
https://lists.sourceforge.net/lists/listinfo/nfs