2013-03-08 19:46:07

by Chuck Lever III

[permalink] [raw]
Subject: [PATCH 00/11] gssd clean-ups for nfs-utils

I've been working on several fixes and clean-ups in response to an
issue reported by Veli-Matti Lintu <[email protected]>.
The thread is archived here:

http://www.spinics.net/lists/linux-nfs/msg35306.html

There were two suggestions in that thread:

1. Clarify rpc.gssd(8)

2. Implement a gssd option to name a credential file where
machine credentials can be found

This series implements both suggestions along with a number of
clean-ups I found while working on this code.

I had this series with me at Connectathon, and did some light
testing there. I think they are ready for broader review and
comment.

---

Chuck Lever (11):
gssd: Add "-c" command line option
gssd: gethostname(3) returns zero or -1, not an errno
gssd: Fix whitespace nits
rpc.gssd: Clean up gssd_setup_krb5_user_gss_ccache()
gssd: Update description of "-l" option
gssd: Clarify use of the term "machine credentials" in rpc.gssd(8)
gssd: Provide an introduction in gssd(8)
gssd: gssd.man is missing a description of the "-M" option
gssd: Use italics for option values and pathnames
mountd: make local functions in v4root.c static
mountd: remove unused variable


utils/gssd/gssd.c | 16 ++
utils/gssd/gssd.h | 1
utils/gssd/gssd.man | 328 +++++++++++++++++++++++++++++++++++-------------
utils/gssd/gssd_proc.c | 12 +-
utils/gssd/krb5_util.c | 84 +++++++++++-
utils/mountd/cache.c | 2
utils/mountd/v4root.c | 6 +
7 files changed, 339 insertions(+), 110 deletions(-)

--
Chuck Lever


2013-03-11 17:59:18

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 11/11] gssd: Add "-c" command line option

On Mon, Mar 11, 2013 at 01:58:33PM -0400, bfields wrote:
> On Fri, Mar 08, 2013 at 02:47:41PM -0500, Chuck Lever wrote:
> > When a system does not have a keytab, it has no way to obtain
> > machine credentials. In the past, we've fudged it by simply using
> > root's user credential as the machine credential. (root must kinit
> > in this case, of course).
> >
> > Since v1 of the kernel's GSS upcall was introduced, rpc.gssd can
> > tell when the kernel requests a machine credential specifically,
> > say, for NFSv4 operations that are required to use one
> > (SETCLIENTID).
> >
> > Since I moved SETCLIENTID to the first operation performed against
> > a new server, this means a sec=krb5 NFSv4 mount can't succeed at all
> > if there is no machine credential. Previously, this kind of mount
> > could work at least until a SETCLIENTID was done, as the kernel
> > could use root's user credential for housekeeping like grabbing the
> > server's root FH. (Or maybe there is some kind of gssd bug that
> > allowed it to keep working?).
> >
> > In any event, what we want to do is provide a clean and secure way
> > for rpc.gssd to substitute a specific user credential if there is no
> > machine credential available. (I'm actually not clear on why this
> > is OK to do).
> >
> > Recent work to add Active Directory support to rpc.gssd gives us a
> > potential user principal that can be used for this purpose:
> >
> > HOSTNAME$@REALM
> >
> > We have a specific user principal then. We have to tell rpc.gssd
> > where to look for the credential file. Introduce a command line
> > option that specifies exactly where the file containing the
> > credential for this principal can be found. This credential would
> > be used only if no other machine credential is available.
> >
> > One might then use it this way:
> >
> > # /usr/sbin/rpc.gssd -n -c /tmp/krb5cc_0
> > # kinit <hostname>$@<REALM>
> > Password for <hostname>$@<REALM>:
> > # mount -t nfs4 -o sec=krb5 server:/export /mnt
>
> Say my university gives me a kerberos identity [email protected] and
> a home directory files.example.edu:/home/bfields.
>
> Then previously I could access my home directory from my personal laptop
> with
>
> # kinit [email protected]
> # mount -tnfs4 -osec=krb5 files.example.edu:/home/bfields /u-home
>
> But now if I want to do that I have to talk example.u's IT department
> into issuing me [email protected]?
>
> If so, haven't we lost a useful feature?

(ACK to all the other patches.)

--b.

2013-03-08 19:47:07

by Chuck Lever III

[permalink] [raw]
Subject: [PATCH 07/11] gssd: Update description of "-l" option

Move most of the text in the description of the "-l" option up to
the DESCRIPTION section, to match what was done for "-n" and "-k".

The discussion is then less restricted by formatting, and we can
take the space to introduce a few concepts before describing the
behavior of rpc.gssd.

Fix a few misspellings and grammar issues while here.

Signed-off-by: Chuck Lever <[email protected]>
---

utils/gssd/gssd.c | 2 +-
utils/gssd/gssd.man | 47 ++++++++++++++++++++++++++---------------------
2 files changed, 27 insertions(+), 22 deletions(-)

diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c
index a3292c9..0be2517 100644
--- a/utils/gssd/gssd.c
+++ b/utils/gssd/gssd.c
@@ -147,7 +147,7 @@ main(int argc, char *argv[])
#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
limit_to_legacy_enctypes = 1;
#else
- errx(1, "Setting encryption type not support by Kerberos libraries.");
+ errx(1, "Encryption type limits not supported by Kerberos libraries.");
#endif
break;
default:
diff --git a/utils/gssd/gssd.man b/utils/gssd/gssd.man
index 1d6fb4c..79d9bf9 100644
--- a/utils/gssd/gssd.man
+++ b/utils/gssd/gssd.man
@@ -172,6 +172,27 @@ If
.B rpc.gssd
cannot obtain a machine credential (say, the local system has
no keytab), NFSv4 operations that require machine credentials will fail.
+.SS Encryption types
+A realm administrator can choose to add keys encoded in a number of different
+encryption types to the local system's keytab.
+For instance, a host/ principal might have keys for the
+.BR aes256-cts-hmac-sha1-96 ,
+.BR aes128-cts-hmac-sha1-96 ,
+.BR des3-cbc-sha1 ", and"
+.BR arcfour-hmac " encryption types."
+This permits
+.B rpc.gssd
+to choose an appropriate encryption type that the target NFS server
+supports.
+.P
+These encryption types are stronger than legacy single-DES encryption types.
+To interoperate in environments where servers support
+only weak encryption types,
+you can restrict your client to use only single-DES encryption types
+by specifying the
+.B -l
+option when starting
+.BR rpc.gssd .
.SH OPTIONS
.TP
.B -f
@@ -193,28 +214,12 @@ The default value is
.IR /etc/krb5.keytab .
.TP
.B -l
-Tells
+When specified, restricts
.B rpc.gssd
-to limit session keys to Single DES even if the kernel supports stronger
-encryption types. Service ticket encryption is still governed by what
-the KDC believes the target server supports. This way the client can
-access a server that has strong keys in its keytab for ticket decryption
-but whose kernel only supports Single DES.
-.IP
-The alternative is to put only Single DES keys in the server's keytab
-and limit encryption types for its principal to Single DES on the KDC
-which will cause service tickets for this server to be encrypted using
-only Single DES and (as a side-effect) contain only Single DES session
-keys.
-.IP
-This legacy behaviour is only required for older servers
-(pre nfs-utils-1.2.4). If the server has a recent kernel, Kerberos
-implementation and nfs-utils it will work just fine with stronger
-encryption.
-.IP
-.B Note:
-This option is only available with Kerberos libraries that
-support setable encryption types.
+to sessions to weak encryption types such as
+.BR des-cbc-crc .
+This option is available only when the local system's Kerberos library
+supports settable encryption types.
.TP
.BI "-p " path
Tells


2013-03-12 20:05:07

by Chuck Lever III

[permalink] [raw]
Subject: Re: [PATCH 08/11] rpc.gssd: Clean up gssd_setup_krb5_user_gss_ccache()


On Mar 11, 2013, at 8:02 AM, Jeff Layton <[email protected]> wrote:

> On Fri, 08 Mar 2013 14:47:14 -0500
> Chuck Lever <[email protected]> wrote:
>
>> Remove a contradictory portion of the block comment documenting
>> gssd_find_existing_krb5_ccache(). This should have been removed by
>> commit 289ad31e, which reversed the meaning of the function's return
>> values.
>>
>> Note that, in user space, typically errno's are positive. But here
>> we follow the kernel convention of using negative values to return
>> error codes. Make the documenting comments explicit about the sign
>> of an error return -- it will never be positive in the case of an
>> error.
>>
>> And a nit: At the last return statement in
>> gssd_setup_krb5_user_gss_ccache(), "err" always contains zero, as
>> far as I can tell. Make it explicit (to human readers) that when
>> execution reaches this point, gssd_setup_krb5_user_gss_ccache() is
>> going to return "success."
>>
>> Signed-off-by: Chuck Lever <[email protected]>
>> Cc: Jeff Layton <[email protected]>
>> ---
>>
>> utils/gssd/krb5_util.c | 13 +++++--------
>> 1 files changed, 5 insertions(+), 8 deletions(-)
>>
>> diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
>> index aeb8f70..6c37094 100644
>> --- a/utils/gssd/krb5_util.c
>> +++ b/utils/gssd/krb5_util.c
>> @@ -169,13 +169,10 @@ select_krb5_ccache(const struct dirent *d)
>>
>> /*
>> * Look in directory "dirname" for files that look like they
>> - * are Kerberos Credential Cache files for a given UID. Return
>> - * non-zero and the dirent pointer for the entry most likely to be
>> - * what we want. Otherwise, return zero and no dirent pointer.
>> - * The caller is responsible for freeing the dirent if one is returned.
>> + * are Kerberos Credential Cache files for a given UID.
>
> Why trim out the info about "d" being set to a valid dirent here?
> Rather than doing that, I'd prefer adding some verbiage about cctype
> also being set on a valid return.

Particularly since, unlike "*d", "*cctype" should not be freed by the caller. I'll respin this one, and post a fresh version of the series.

>
>> *
>> - * Returns 0 if a valid-looking entry was found and a non-zero error
>> - * code otherwise.
>> + * Returns 0 if a valid-looking entry was found, or a negative
>> + * errno code otherwise.
>> */
>> static int
>> gssd_find_existing_krb5_ccache(uid_t uid, char *dirname,
>> @@ -1037,7 +1034,7 @@ err_cache:
>> * given only a UID. We really need more information, but we
>> * do the best we can.
>> *
>> - * Returns 0 if a ccache was found, and a non-zero error code otherwise.
>> + * Returns 0 if a ccache was found, or a negative errno otherwise.
>> */
>> int
>> gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, char *dirpattern)
>> @@ -1082,7 +1079,7 @@ gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, char *dirpattern)
>> printerr(2, "using %s as credentials cache for client with "
>> "uid %u for server %s\n", buf, uid, servername);
>> gssd_set_krb5_ccache_name(buf);
>> - return err;
>> + return 0;
>> }
>>
>> /*
>>
>
> --
> Jeff Layton <[email protected]>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

--
Chuck Lever
chuck[dot]lever[at]oracle[dot]com





2013-03-08 19:46:15

by Chuck Lever III

[permalink] [raw]
Subject: [PATCH 01/11] mountd: remove unused variable

Clean up:

Making all in mountd
cache.c: In function ‘subexport’:
cache.c:374:9: warning: unused variable ‘l2’ [-Wunused-variable]

Commit 8e2fb3fc removed the last use of "l2" in the subexport()
function.

Signed-off-by: Chuck Lever <[email protected]>
---

utils/mountd/cache.c | 2 --
1 files changed, 0 insertions(+), 2 deletions(-)

diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c
index 45012be..c8aa46f 100644
--- a/utils/mountd/cache.c
+++ b/utils/mountd/cache.c
@@ -371,8 +371,6 @@ export_matches(nfs_export *exp, char *dom, char *path, struct addrinfo *ai)
static bool subexport(struct exportent *e1, struct exportent *e2)
{
char *p1 = e1->e_path, *p2 = e2->e_path;
- size_t l2 = strlen(p2);
-
return e2->e_flags & NFSEXP_CROSSMOUNT
&& is_subdirectory(p1, p2);
}


2013-03-08 19:47:34

by Chuck Lever III

[permalink] [raw]
Subject: [PATCH 10/11] gssd: gethostname(3) returns zero or -1, not an errno

According to "man gethostname," gssd is handling the return value of
gethostname(3) incorrectly. It looks like other gethostname(3) call
sites in nfs-utils are already correct.

Signed-off-by: Chuck Lever <[email protected]>
---

utils/gssd/krb5_util.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
index 6c37094..215c0a4 100644
--- a/utils/gssd/krb5_util.c
+++ b/utils/gssd/krb5_util.c
@@ -802,8 +802,8 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *tgtname,
goto out;

/* Get full local hostname */
- retval = gethostname(myhostname, sizeof(myhostname));
- if (retval) {
+ if (gethostname(myhostname, sizeof(myhostname)) == -1) {
+ retval = errno;
k5err = gssd_k5_err_msg(context, retval);
printerr(1, "%s while getting local hostname\n", k5err);
goto out;


2013-03-08 19:47:25

by Chuck Lever III

[permalink] [raw]
Subject: [PATCH 09/11] gssd: Fix whitespace nits

Signed-off-by: Chuck Lever <[email protected]>
---

utils/gssd/gssd_proc.c | 12 ++++++------
1 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c
index c17ab3b..6b5e90e 100644
--- a/utils/gssd/gssd_proc.c
+++ b/utils/gssd/gssd_proc.c
@@ -169,7 +169,7 @@ sockaddr_to_hostname(const struct sockaddr *sa, const char *addr)
{
socklen_t addrlen;
int err;
- char *hostname;
+ char *hostname;
char hbuf[NI_MAXHOST];

switch (sa->sa_family) {
@@ -708,7 +708,7 @@ out_err:

/*
* If the port isn't already set, do an rpcbind query to the remote server
- * using the program and version and get the port.
+ * using the program and version and get the port.
*
* Newer kernels send the value of the port= mount option in the "info"
* file for the upcall or '0' for NFSv2/3. For NFSv4 it sends the value
@@ -1013,7 +1013,7 @@ process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname,
printerr(0, "ERROR: No credentials found "
"for connection to server %s\n",
clp->servername);
- goto out_return_error;
+ goto out_return_error;
}
for (ccname = credlist; ccname && *ccname; ccname++) {
gssd_setup_krb5_machine_gss_ccache(*ccname);
@@ -1023,12 +1023,12 @@ process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname,
/* Success! */
success++;
break;
- }
+ }
printerr(2, "WARNING: Failed to create machine krb5 context "
"with credentials cache %s for server %s\n",
*ccname, clp->servername);
}
- gssd_free_krb5_machine_cred_list(credlist);
+ gssd_free_krb5_machine_cred_list(credlist);
if (!success) {
if(nocache == 0) {
nocache++;
@@ -1225,6 +1225,6 @@ out:
free(enctypes);
free(target);
free(service);
- return;
+ return;
}



2013-03-08 19:46:41

by Chuck Lever III

[permalink] [raw]
Subject: [PATCH 04/11] gssd: gssd.man is missing a description of the "-M" option

Signed-off-by: Chuck Lever <[email protected]>
---

utils/gssd/gssd.man | 26 ++++++++++++++++----------
1 files changed, 16 insertions(+), 10 deletions(-)

diff --git a/utils/gssd/gssd.man b/utils/gssd/gssd.man
index 0cc7bf4..dbbfbbb 100644
--- a/utils/gssd/gssd.man
+++ b/utils/gssd/gssd.man
@@ -7,7 +7,7 @@
rpc.gssd \- rpcsec_gss daemon
.SH SYNOPSIS
.B rpc.gssd
-.RB [ \-fnlvr ]
+.RB [ \-fMnlvr ]
.RB [ \-k
.IR keytab ]
.RB [ \-p
@@ -112,17 +112,23 @@ Tells
where to look for the rpc_pipefs filesystem. The default value is
.IR /var/lib/nfs/rpc_pipefs .
.TP
-.BI "-d " directory
-Tells
+.BI "-d " search-path
+This option specifies a colon separated list of directories that
.B rpc.gssd
-where to look for Kerberos credential files. The default value is
+searches for credential files. The default value is
.IR /tmp:/run/user/%U .
-This can also be a colon separated list of directories to be searched for
-Kerberos credential files. The sequence "%U", if used, is replaced with
-the UID of the user for whom credentials are being searched.
-Note that if machine credentials are being
-stored in files, then the first directory on this list is where the
-machine credentials are stored.
+The literal sequence "%U" can be specified to substitue the UID
+of the user for whom credentials are being searched.
+.TP
+.B -M
+By default, machine credentials are stored in files in the first
+directory in the credential directory search path (see the
+.B -d
+option). When
+.B -M
+is set,
+.B rpc.gssd
+stores machine credentials in memory instead.
.TP
.B -v
Increases the verbosity of the output (can be specified multiple times).


2013-03-11 12:02:21

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH 08/11] rpc.gssd: Clean up gssd_setup_krb5_user_gss_ccache()

On Fri, 08 Mar 2013 14:47:14 -0500
Chuck Lever <[email protected]> wrote:

> Remove a contradictory portion of the block comment documenting
> gssd_find_existing_krb5_ccache(). This should have been removed by
> commit 289ad31e, which reversed the meaning of the function's return
> values.
>
> Note that, in user space, typically errno's are positive. But here
> we follow the kernel convention of using negative values to return
> error codes. Make the documenting comments explicit about the sign
> of an error return -- it will never be positive in the case of an
> error.
>
> And a nit: At the last return statement in
> gssd_setup_krb5_user_gss_ccache(), "err" always contains zero, as
> far as I can tell. Make it explicit (to human readers) that when
> execution reaches this point, gssd_setup_krb5_user_gss_ccache() is
> going to return "success."
>
> Signed-off-by: Chuck Lever <[email protected]>
> Cc: Jeff Layton <[email protected]>
> ---
>
> utils/gssd/krb5_util.c | 13 +++++--------
> 1 files changed, 5 insertions(+), 8 deletions(-)
>
> diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
> index aeb8f70..6c37094 100644
> --- a/utils/gssd/krb5_util.c
> +++ b/utils/gssd/krb5_util.c
> @@ -169,13 +169,10 @@ select_krb5_ccache(const struct dirent *d)
>
> /*
> * Look in directory "dirname" for files that look like they
> - * are Kerberos Credential Cache files for a given UID. Return
> - * non-zero and the dirent pointer for the entry most likely to be
> - * what we want. Otherwise, return zero and no dirent pointer.
> - * The caller is responsible for freeing the dirent if one is returned.
> + * are Kerberos Credential Cache files for a given UID.

Why trim out the info about "d" being set to a valid dirent here?
Rather than doing that, I'd prefer adding some verbiage about cctype
also being set on a valid return.

> *
> - * Returns 0 if a valid-looking entry was found and a non-zero error
> - * code otherwise.
> + * Returns 0 if a valid-looking entry was found, or a negative
> + * errno code otherwise.
> */
> static int
> gssd_find_existing_krb5_ccache(uid_t uid, char *dirname,
> @@ -1037,7 +1034,7 @@ err_cache:
> * given only a UID. We really need more information, but we
> * do the best we can.
> *
> - * Returns 0 if a ccache was found, and a non-zero error code otherwise.
> + * Returns 0 if a ccache was found, or a negative errno otherwise.
> */
> int
> gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, char *dirpattern)
> @@ -1082,7 +1079,7 @@ gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, char *dirpattern)
> printerr(2, "using %s as credentials cache for client with "
> "uid %u for server %s\n", buf, uid, servername);
> gssd_set_krb5_ccache_name(buf);
> - return err;
> + return 0;
> }
>
> /*
>

--
Jeff Layton <[email protected]>

2013-03-08 19:46:58

by Chuck Lever III

[permalink] [raw]
Subject: [PATCH 06/11] gssd: Clarify use of the term "machine credentials" in rpc.gssd(8)

Out NFSv4 implementation uses machine credentials for operations
that manage state on behalf of the whole client (for example,
SETCLIENTID or RENEW). The rpc.gssd man page is missing a
description of this usage, especially in the discussion of the "-n"
option.

The issue is that rpc.gssd's "-n" option requires root to acquire a
user credential. In the absense of a system keytab (for instance,
if the system is diskless) root's credential is not to be used as
the machine credential that manages NFSv4 state.

Group the discussion of machine credentials and UID 0 in one place
to help clarify the discussion and simplify the description of
several of these options.

Signed-off-by: Chuck Lever <[email protected]>
---

utils/gssd/gssd.man | 142 ++++++++++++++++++++++++++++++++++++++-------------
1 files changed, 105 insertions(+), 37 deletions(-)

diff --git a/utils/gssd/gssd.man b/utils/gssd/gssd.man
index fb3ab97..1d6fb4c 100644
--- a/utils/gssd/gssd.man
+++ b/utils/gssd/gssd.man
@@ -71,6 +71,107 @@ the Linux kernel RPC client depends on a userspace daemon called
The
.B rpc.gssd
daemon uses the rpc_pipefs filesystem to communicate with the kernel.
+.SS User Credentials
+When a user authenticates using a command such as
+.BR kinit (1),
+the resulting credential is stored in a file with a well-known name
+constructed using the user's UID.
+.P
+To interact with an NFS server
+on behalf of a particular Kerberos-authenticated user,
+the Linux kernel RPC client requests that
+.B rpc.gssd
+initialize a security context with the credential
+in that user's credential file.
+.P
+Typically, credential files are placed in
+.IR /tmp .
+However,
+.B rpc.gssd
+can search for credential files in more than one directory.
+See the description of the
+.B -d
+option for details.
+.SS Machine Credentials
+A user credential is established by a user and
+is then shared with the kernel and
+.BR rpc.gssd .
+A machine credential is established by
+.B rpc.gssd
+for the kernel when there is no user.
+Therefore
+.B rpc.gssd
+must already have the materials on hand to establish this credential
+without requiring user intervention.
+.P
+.B rpc.gssd
+searches the local system's keytab for a principal and key to use
+to establish the machine credential.
+By default,
+.B rpc.gssd
+assumes the file
+.I /etc/krb5.keytab
+contains principals and keys that can be used to obtain machine credentials.
+.P
+.B rpc.gssd
+searches in the following order for a principal to use.
+The first matching credential is used.
+For the search, <hostname> and <REALM> are replaced with the local
+system's hostname and Kerberos realm.
+.sp
+ <HOSTNAME>$@<REALM>
+.br
+ root/<hostname>@<REALM>
+.br
+ nfs/<hostname>@<REALM>
+.br
+ host/<hostname>@<REALM>
+.br
+ root/<anyname>@<REALM>
+.br
+ nfs/<anyname>@<REALM>
+.br
+ host/<anyname>@<REALM>
+.sp
+The <anyname> entries match on the service name and realm, but ignore the hostname.
+These can be used if a principal matching the local host's name is not found.
+.P
+Note that the first principal in the search order is a user principal
+that enables Kerberized NFS when the local system is joined
+to an Active Directory domain using Samba.
+A password for this principal must be provided in the local system's keytab.
+.P
+You can specify another keytab by using the
+.B -k
+option if
+.I /etc/krb5.keytab
+does not exist or does not provide one of these principals.
+.SS Credentials for UID 0
+UID 0 is a special case.
+By default
+.B rpc.gssd
+uses the system's machine credentials for UID 0 accesses
+that require GSS authentication.
+This limits the privileges of the root user
+when accessing network resources that require authentication.
+.P
+Specify the
+.B -n
+option when starting
+.B rpc.gssd
+if you'd like to force the root user to obtain a user credential
+rather than use the local system's machine credential.
+.P
+When
+.B -n
+is specified,
+the kernel continues to request a GSS context established
+with a machine credential for NFSv4 operations,
+such as SETCLIENTID or RENEW, that manage state.
+If
+.B rpc.gssd
+cannot obtain a machine credential (say, the local system has
+no keytab), NFSv4 operations that require machine credentials will fail.
.SH OPTIONS
.TP
.B -f
@@ -79,50 +180,17 @@ Runs
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
-attempting to mount an nfs filesystem requiring Kerberos
-authentication.
+When specified, UID 0 is forced to obtain user credentials
+which are used instead of the local system's machine credentials.
.TP
.BI "-k " keytab
Tells
.B rpc.gssd
to use the keys found in
.I keytab
-to obtain "machine credentials".
+to obtain machine credentials.
The default value is
-.I /etc/krb5.keytab.
-.IP
-Previous versions of
-.B rpc.gssd
-used only "nfs/*" keys found within the keytab.
-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
- <HOSTNAME>$@<REALM>
-.br
- root/<hostname>@<REALM>
-.br
- nfs/<hostname>@<REALM>
-.br
- host/<hostname>@<REALM>
-.br
- root/<anyname>@<REALM>
-.br
- nfs/<anyname>@<REALM>
-.br
- host/<anyname>@<REALM>
-.IP
-If this search order does not use the correct key then provide a
-keytab file that contains only correct keys.
+.IR /etc/krb5.keytab .
.TP
.B -l
Tells


2013-03-08 19:46:50

by Chuck Lever III

[permalink] [raw]
Subject: [PATCH 05/11] gssd: Provide an introduction in gssd(8)

It's good practice in user documentation to define terms before they
are used. Add an INTRODUCTION section that defines important terms
that are used in the DESCRIPTION and OPTIONS sections. The key
concepts are GSS context, user credential, machine credential, and
keytab.

The RFCs I looked at capitalize both "gss" and "rpcsec_gss". For
consistency I changed this throughout the man page.

Signed-off-by: Chuck Lever <[email protected]>
---

utils/gssd/gssd.man | 73 +++++++++++++++++++++++++++++++++++++++++----------
1 files changed, 59 insertions(+), 14 deletions(-)

diff --git a/utils/gssd/gssd.man b/utils/gssd/gssd.man
index dbbfbbb..fb3ab97 100644
--- a/utils/gssd/gssd.man
+++ b/utils/gssd/gssd.man
@@ -2,9 +2,10 @@
.\" rpc.gssd(8)
.\"
.\" Copyright (C) 2003 J. Bruce Fields <[email protected]>
-.TH rpc.gssd 8 "14 Mar 2007"
+.\"
+.TH rpc.gssd 8 "20 Feb 2013"
.SH NAME
-rpc.gssd \- rpcsec_gss daemon
+rpc.gssd \- RPCSEC_GSS daemon
.SH SYNOPSIS
.B rpc.gssd
.RB [ \-fMnlvr ]
@@ -18,17 +19,58 @@ rpc.gssd \- rpcsec_gss daemon
.IR timeout ]
.RB [ \-R
.IR realm ]
+.SH INTRODUCTION
+The RPCSEC_GSS protocol, defined in RFC 5403, is used to provide
+strong security for RPC-based protocols such as NFS.
+.P
+Before exchanging RPC requests using RPCSEC_GSS, an RPC client must
+establish a GSS
+.IR "security context" .
+A security context is shared state on each
+end of a network transport that enables GSS-API security services.
+.P
+Security contexts are established using
+.IR "security credentials" .
+A credential grants temporary access to a secure network service,
+much as a railway ticket grants temporary access to use a rail service.
+.P
+A user typically obtains a credential by providing a password to the
+.BR kinit (1)
+command, or via a PAM library at login time.
+A credential acquired with a
+.I user principal
+is known as a
+.I user credential
+(see
+.BR kerberos (1)
+for more on principals).
+.P
+For certain operations, a credential is required
+which represents no user,
+is otherwise unprivileged,
+and is always available.
+This is referred to as a
+.IR "machine credential" .
+.P
+Machine credentials are typically established using a
+.IR "service principal" ,
+whose encrypted password, called its
+.IR key ,
+is stored in a file, called a
+.IR keytab ,
+to avoid requiring a user prompt.
+A machine credential effectively does not expire because the system
+can renew it as needed without user intervention.
+.P
+Once obtained, credentials are typically stored in local temporary files
+with well-known pathnames.
.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
-exchanging any rpc requests using rpcsec_gss, the rpc client must first
-establish a security context. The linux kernel's implementation of rpcsec_gss
-depends on the userspace daemon
-.B rpc.gssd
-to establish security contexts. The
+To establish GSS security contexts using these credential files,
+the Linux kernel RPC client depends on a userspace daemon called
+.BR rpc.gssd .
+The
.B rpc.gssd
-daemon uses files in the rpc_pipefs filesystem to communicate with the kernel.
-
+daemon uses the rpc_pipefs filesystem to communicate with the kernel.
.SH OPTIONS
.TP
.B -f
@@ -134,7 +176,7 @@ stores machine credentials in memory instead.
Increases the verbosity of the output (can be specified multiple times).
.TP
.B -r
-If the rpcsec_gss library supports setting debug level,
+If the RPCSEC_GSS library supports setting debug level,
increases the verbosity of the output (can be specified multiple times).
.TP
.BI "-R " realm
@@ -145,14 +187,17 @@ used to create a context. By default, the default realm, as configured
in the Kerberos configuration file, is preferred.
.TP
.BI "-t " timeout
-Timeout, in seconds, for kernel gss contexts. This option allows you to force
+Timeout, in seconds, for kernel GSS contexts. This option allows you to force
new kernel contexts to be negotiated after
.I timeout
seconds, which allows changing Kerberos tickets and identities frequently.
The default is no explicit timeout, which means the kernel context will live
the lifetime of the Kerberos service ticket used in its creation.
.SH SEE ALSO
-.BR rpc.svcgssd(8)
+.BR rpc.svcgssd (8),
+.BR kerberos (1),
+.BR kinit (1),
+.BR krb5.conf (5)
.SH AUTHORS
.br
Dug Song <[email protected]>


2013-03-12 19:05:18

by Chuck Lever III

[permalink] [raw]
Subject: Re: [PATCH 11/11] gssd: Add "-c" command line option


On Mar 11, 2013, at 1:58 PM, J. Bruce Fields <[email protected]> wrote:

> On Fri, Mar 08, 2013 at 02:47:41PM -0500, Chuck Lever wrote:
>> When a system does not have a keytab, it has no way to obtain
>> machine credentials. In the past, we've fudged it by simply using
>> root's user credential as the machine credential. (root must kinit
>> in this case, of course).
>>
>> Since v1 of the kernel's GSS upcall was introduced, rpc.gssd can
>> tell when the kernel requests a machine credential specifically,
>> say, for NFSv4 operations that are required to use one
>> (SETCLIENTID).
>>
>> Since I moved SETCLIENTID to the first operation performed against
>> a new server, this means a sec=krb5 NFSv4 mount can't succeed at all
>> if there is no machine credential. Previously, this kind of mount
>> could work at least until a SETCLIENTID was done, as the kernel
>> could use root's user credential for housekeeping like grabbing the
>> server's root FH. (Or maybe there is some kind of gssd bug that
>> allowed it to keep working?).
>>
>> In any event, what we want to do is provide a clean and secure way
>> for rpc.gssd to substitute a specific user credential if there is no
>> machine credential available. (I'm actually not clear on why this
>> is OK to do).
>>
>> Recent work to add Active Directory support to rpc.gssd gives us a
>> potential user principal that can be used for this purpose:
>>
>> HOSTNAME$@REALM
>>
>> We have a specific user principal then. We have to tell rpc.gssd
>> where to look for the credential file. Introduce a command line
>> option that specifies exactly where the file containing the
>> credential for this principal can be found. This credential would
>> be used only if no other machine credential is available.
>>
>> One might then use it this way:
>>
>> # /usr/sbin/rpc.gssd -n -c /tmp/krb5cc_0
>> # kinit <hostname>$@<REALM>
>> Password for <hostname>$@<REALM>:
>> # mount -t nfs4 -o sec=krb5 server:/export /mnt
>
> Say my university gives me a kerberos identity [email protected] and
> a home directory files.example.edu:/home/bfields.
>
> Then previously I could access my home directory from my personal laptop
> with
>
> # kinit [email protected]
> # mount -tnfs4 -osec=krb5 files.example.edu:/home/bfields /u-home
>
> But now if I want to do that I have to talk example.u's IT department
> into issuing me [email protected]?
>
> If so, haven't we lost a useful feature?

I agree this is a cumbersome solution. I have a kernel-side solution that might be a better approach which I will post later this week.

Meanwhile, I'll drop this patch from the present series. We can revisit if needed.

>
> --b.
>
>>
>> Signed-off-by: Chuck Lever <[email protected]>
>> ---
>>
>> utils/gssd/gssd.c | 14 +++++++++-
>> utils/gssd/gssd.h | 1 +
>> utils/gssd/gssd.man | 18 +++++++++++++
>> utils/gssd/krb5_util.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++
>> 4 files changed, 98 insertions(+), 2 deletions(-)
>>
>> diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c
>> index 0be2517..7537a97 100644
>> --- a/utils/gssd/gssd.c
>> +++ b/utils/gssd/gssd.c
>> @@ -58,6 +58,7 @@
>> char pipefs_dir[PATH_MAX] = GSSD_PIPEFS_DIR;
>> char keytabfile[PATH_MAX] = GSSD_DEFAULT_KEYTAB_FILE;
>> char ccachedir[PATH_MAX] = GSSD_DEFAULT_CRED_DIR ":" GSSD_USER_CRED_DIR;
>> +char mach_cred_file[PATH_MAX] = "";
>> char *ccachesearch[GSSD_MAX_CCACHE_SEARCH + 1];
>> int use_memcache = 0;
>> int root_uses_machine_creds = 1;
>> @@ -85,7 +86,9 @@ sig_hup(int signal)
>> static void
>> usage(char *progname)
>> {
>> - fprintf(stderr, "usage: %s [-f] [-l] [-M] [-n] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir] [-t timeout] [-R preferred realm]\n",
>> + fprintf(stderr, "usage: %s [-f] [-l] [-M] [-n] [-v] [-r] "
>> + "[-p pipefsdir] [-k keytab] [-d ccachedir] "
>> + "[-t timeout] [-R preferred realm] [-c credfile]\n",
>> progname);
>> exit(1);
>> }
>> @@ -102,7 +105,7 @@ main(int argc, char *argv[])
>> char *progname;
>>
>> memset(ccachesearch, 0, sizeof(ccachesearch));
>> - while ((opt = getopt(argc, argv, "fvrlmnMp:k:d:t:R")) != -1) {
>> + while ((opt = getopt(argc, argv, "fvrlmnMp:k:d:t:R:c:")) != -1) {
>> switch (opt) {
>> case 'f':
>> fg = 1;
>> @@ -122,6 +125,11 @@ main(int argc, char *argv[])
>> case 'r':
>> rpc_verbosity++;
>> break;
>> + case 'c':
>> + strncpy(mach_cred_file, optarg, sizeof(mach_cred_file));
>> + if (mach_cred_file[sizeof(mach_cred_file)-1] != '\0')
>> + errx(1, "credential file name too long");
>> + break;
>> case 'p':
>> strncpy(pipefs_dir, optarg, sizeof(pipefs_dir));
>> if (pipefs_dir[sizeof(pipefs_dir)-1] != '\0')
>> @@ -156,6 +164,8 @@ main(int argc, char *argv[])
>> }
>> }
>>
>> + if (mach_cred_file[0] != '\0' && use_memcache)
>> + errx(1, "Cannot use memcache and credential file together");
>> i = 0;
>> ccachesearch[i++] = strtok(ccachedir, ":");
>> do {
>> diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h
>> index 86472a1..e76bad1 100644
>> --- a/utils/gssd/gssd.h
>> +++ b/utils/gssd/gssd.h
>> @@ -62,6 +62,7 @@ enum {AUTHTYPE_KRB5, AUTHTYPE_LIPKEY};
>>
>> extern char pipefs_dir[PATH_MAX];
>> extern char keytabfile[PATH_MAX];
>> +extern char mach_cred_file[PATH_MAX];
>> extern char *ccachesearch[];
>> extern int use_memcache;
>> extern int root_uses_machine_creds;
>> diff --git a/utils/gssd/gssd.man b/utils/gssd/gssd.man
>> index 79d9bf9..ef26577 100644
>> --- a/utils/gssd/gssd.man
>> +++ b/utils/gssd/gssd.man
>> @@ -19,6 +19,8 @@ rpc.gssd \- RPCSEC_GSS daemon
>> .IR timeout ]
>> .RB [ \-R
>> .IR realm ]
>> +.RB [ \-c
>> +.IR file ]
>> .SH INTRODUCTION
>> The RPCSEC_GSS protocol, defined in RFC 5403, is used to provide
>> strong security for RPC-based protocols such as NFS.
>> @@ -146,6 +148,18 @@ You can specify another keytab by using the
>> option if
>> .I /etc/krb5.keytab
>> does not exist or does not provide one of these principals.
>> +.P
>> +If no keytab is found, the
>> +.B -c
>> +option can specify a file containing a credential that
>> +.B rpc.gssd
>> +should use as a machine credential.
>> +The principal must be of the form
>> +.sp
>> + <HOSTNAME>$@<REALM>
>> +.sp
>> +Where <HOSTNAME> is the hostname of the local system, and <REALM> is the
>> +system's Kerberos realm.
>> .SS Credentials for UID 0
>> UID 0 is a special case.
>> By default
>> @@ -213,6 +227,10 @@ to obtain machine credentials.
>> The default value is
>> .IR /etc/krb5.keytab .
>> .TP
>> +.BI "-c " file
>> +Specifies a file in which to find a credential that can be used
>> +as a machine credential, if the local system has no keytab.
>> +.TP
>> .B -l
>> When specified, restricts
>> .B rpc.gssd
>> diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
>> index 215c0a4..c49aed9 100644
>> --- a/utils/gssd/krb5_util.c
>> +++ b/utils/gssd/krb5_util.c
>> @@ -937,6 +937,63 @@ out:
>> return retval;
>> }
>>
>> +/*
>> + * Returns zero if credential cache file can be used for a machine credential
>> + */
>> +static int
>> +verify_mach_cc(const char *ccache)
>> +{
>> + char *hostname = NULL;
>> + char *default_realm = NULL;
>> + char *buf = NULL;
>> + char *princname = NULL;
>> + char *realm = NULL;
>> + int retval;
>> +
>> + retval = ENOMEM;
>> + buf = malloc(PATH_MAX + 5);
>> + if (buf == NULL)
>> + goto out;
>> +
>> + hostname = malloc(NI_MAXHOST + 1);
>> + if (hostname == NULL)
>> + goto out;
>> + if (gethostname(hostname, NI_MAXHOST) == -1) {
>> + retval = errno;
>> + goto out;
>> + }
>> + strcat(hostname, "$");
>> +
>> + retval = EKEYEXPIRED;
>> + gssd_k5_get_default_realm(&default_realm);
>> + if (default_realm == NULL)
>> + goto out;
>> +
>> + printerr(2, "looking for machine credentials in %s\n", ccache);
>> + snprintf(buf, PATH_MAX + 5, "FILE:%s", ccache);
>> + if (!query_krb5_ccache(buf, &princname, &realm))
>> + goto out;
>> +
>> + if (strcasecmp(princname, hostname) != 0) {
>> + printerr(2, "ERROR: found principal name %s in %s\n",
>> + princname, ccache);
>> + goto out;
>> + }
>> + if (strcasecmp(realm, default_realm) != 0) {
>> + printerr(2, "ERROR: found realm name %s in %s\n",
>> + realm, ccache);
>> + goto out;
>> + }
>> + retval = 0;
>> +
>> +out:
>> + free(realm);
>> + free(princname);
>> + free(default_realm);
>> + free(buf);
>> + free(hostname);
>> + return retval;
>> +}
>>
>> static inline int data_is_equal(krb5_data d1, krb5_data d2)
>> {
>> @@ -1147,6 +1204,16 @@ gssd_get_krb5_machine_cred_list(char ***list)
>> }
>> }
>> }
>> + if (i == 0 && mach_cred_file[0] != '\0') {
>> + if (verify_mach_cc(mach_cred_file)) {
>> + retval = EKEYEXPIRED;
>> + goto out;
>> + }
>> + if ((l[i++] = strdup(mach_cred_file)) == NULL) {
>> + retval = ENOMEM;
>> + goto out;
>> + }
>> + }
>> if (i > 0) {
>> l[i] = NULL;
>> *list = l;
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
>> the body of a message to [email protected]
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

--
Chuck Lever
chuck[dot]lever[at]oracle[dot]com





2013-03-11 17:58:34

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 11/11] gssd: Add "-c" command line option

On Fri, Mar 08, 2013 at 02:47:41PM -0500, Chuck Lever wrote:
> When a system does not have a keytab, it has no way to obtain
> machine credentials. In the past, we've fudged it by simply using
> root's user credential as the machine credential. (root must kinit
> in this case, of course).
>
> Since v1 of the kernel's GSS upcall was introduced, rpc.gssd can
> tell when the kernel requests a machine credential specifically,
> say, for NFSv4 operations that are required to use one
> (SETCLIENTID).
>
> Since I moved SETCLIENTID to the first operation performed against
> a new server, this means a sec=krb5 NFSv4 mount can't succeed at all
> if there is no machine credential. Previously, this kind of mount
> could work at least until a SETCLIENTID was done, as the kernel
> could use root's user credential for housekeeping like grabbing the
> server's root FH. (Or maybe there is some kind of gssd bug that
> allowed it to keep working?).
>
> In any event, what we want to do is provide a clean and secure way
> for rpc.gssd to substitute a specific user credential if there is no
> machine credential available. (I'm actually not clear on why this
> is OK to do).
>
> Recent work to add Active Directory support to rpc.gssd gives us a
> potential user principal that can be used for this purpose:
>
> HOSTNAME$@REALM
>
> We have a specific user principal then. We have to tell rpc.gssd
> where to look for the credential file. Introduce a command line
> option that specifies exactly where the file containing the
> credential for this principal can be found. This credential would
> be used only if no other machine credential is available.
>
> One might then use it this way:
>
> # /usr/sbin/rpc.gssd -n -c /tmp/krb5cc_0
> # kinit <hostname>$@<REALM>
> Password for <hostname>$@<REALM>:
> # mount -t nfs4 -o sec=krb5 server:/export /mnt

Say my university gives me a kerberos identity [email protected] and
a home directory files.example.edu:/home/bfields.

Then previously I could access my home directory from my personal laptop
with

# kinit [email protected]
# mount -tnfs4 -osec=krb5 files.example.edu:/home/bfields /u-home

But now if I want to do that I have to talk example.u's IT department
into issuing me [email protected]?

If so, haven't we lost a useful feature?

--b.

>
> Signed-off-by: Chuck Lever <[email protected]>
> ---
>
> utils/gssd/gssd.c | 14 +++++++++-
> utils/gssd/gssd.h | 1 +
> utils/gssd/gssd.man | 18 +++++++++++++
> utils/gssd/krb5_util.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 98 insertions(+), 2 deletions(-)
>
> diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c
> index 0be2517..7537a97 100644
> --- a/utils/gssd/gssd.c
> +++ b/utils/gssd/gssd.c
> @@ -58,6 +58,7 @@
> char pipefs_dir[PATH_MAX] = GSSD_PIPEFS_DIR;
> char keytabfile[PATH_MAX] = GSSD_DEFAULT_KEYTAB_FILE;
> char ccachedir[PATH_MAX] = GSSD_DEFAULT_CRED_DIR ":" GSSD_USER_CRED_DIR;
> +char mach_cred_file[PATH_MAX] = "";
> char *ccachesearch[GSSD_MAX_CCACHE_SEARCH + 1];
> int use_memcache = 0;
> int root_uses_machine_creds = 1;
> @@ -85,7 +86,9 @@ sig_hup(int signal)
> static void
> usage(char *progname)
> {
> - fprintf(stderr, "usage: %s [-f] [-l] [-M] [-n] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir] [-t timeout] [-R preferred realm]\n",
> + fprintf(stderr, "usage: %s [-f] [-l] [-M] [-n] [-v] [-r] "
> + "[-p pipefsdir] [-k keytab] [-d ccachedir] "
> + "[-t timeout] [-R preferred realm] [-c credfile]\n",
> progname);
> exit(1);
> }
> @@ -102,7 +105,7 @@ main(int argc, char *argv[])
> char *progname;
>
> memset(ccachesearch, 0, sizeof(ccachesearch));
> - while ((opt = getopt(argc, argv, "fvrlmnMp:k:d:t:R")) != -1) {
> + while ((opt = getopt(argc, argv, "fvrlmnMp:k:d:t:R:c:")) != -1) {
> switch (opt) {
> case 'f':
> fg = 1;
> @@ -122,6 +125,11 @@ main(int argc, char *argv[])
> case 'r':
> rpc_verbosity++;
> break;
> + case 'c':
> + strncpy(mach_cred_file, optarg, sizeof(mach_cred_file));
> + if (mach_cred_file[sizeof(mach_cred_file)-1] != '\0')
> + errx(1, "credential file name too long");
> + break;
> case 'p':
> strncpy(pipefs_dir, optarg, sizeof(pipefs_dir));
> if (pipefs_dir[sizeof(pipefs_dir)-1] != '\0')
> @@ -156,6 +164,8 @@ main(int argc, char *argv[])
> }
> }
>
> + if (mach_cred_file[0] != '\0' && use_memcache)
> + errx(1, "Cannot use memcache and credential file together");
> i = 0;
> ccachesearch[i++] = strtok(ccachedir, ":");
> do {
> diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h
> index 86472a1..e76bad1 100644
> --- a/utils/gssd/gssd.h
> +++ b/utils/gssd/gssd.h
> @@ -62,6 +62,7 @@ enum {AUTHTYPE_KRB5, AUTHTYPE_LIPKEY};
>
> extern char pipefs_dir[PATH_MAX];
> extern char keytabfile[PATH_MAX];
> +extern char mach_cred_file[PATH_MAX];
> extern char *ccachesearch[];
> extern int use_memcache;
> extern int root_uses_machine_creds;
> diff --git a/utils/gssd/gssd.man b/utils/gssd/gssd.man
> index 79d9bf9..ef26577 100644
> --- a/utils/gssd/gssd.man
> +++ b/utils/gssd/gssd.man
> @@ -19,6 +19,8 @@ rpc.gssd \- RPCSEC_GSS daemon
> .IR timeout ]
> .RB [ \-R
> .IR realm ]
> +.RB [ \-c
> +.IR file ]
> .SH INTRODUCTION
> The RPCSEC_GSS protocol, defined in RFC 5403, is used to provide
> strong security for RPC-based protocols such as NFS.
> @@ -146,6 +148,18 @@ You can specify another keytab by using the
> option if
> .I /etc/krb5.keytab
> does not exist or does not provide one of these principals.
> +.P
> +If no keytab is found, the
> +.B -c
> +option can specify a file containing a credential that
> +.B rpc.gssd
> +should use as a machine credential.
> +The principal must be of the form
> +.sp
> + <HOSTNAME>$@<REALM>
> +.sp
> +Where <HOSTNAME> is the hostname of the local system, and <REALM> is the
> +system's Kerberos realm.
> .SS Credentials for UID 0
> UID 0 is a special case.
> By default
> @@ -213,6 +227,10 @@ to obtain machine credentials.
> The default value is
> .IR /etc/krb5.keytab .
> .TP
> +.BI "-c " file
> +Specifies a file in which to find a credential that can be used
> +as a machine credential, if the local system has no keytab.
> +.TP
> .B -l
> When specified, restricts
> .B rpc.gssd
> diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
> index 215c0a4..c49aed9 100644
> --- a/utils/gssd/krb5_util.c
> +++ b/utils/gssd/krb5_util.c
> @@ -937,6 +937,63 @@ out:
> return retval;
> }
>
> +/*
> + * Returns zero if credential cache file can be used for a machine credential
> + */
> +static int
> +verify_mach_cc(const char *ccache)
> +{
> + char *hostname = NULL;
> + char *default_realm = NULL;
> + char *buf = NULL;
> + char *princname = NULL;
> + char *realm = NULL;
> + int retval;
> +
> + retval = ENOMEM;
> + buf = malloc(PATH_MAX + 5);
> + if (buf == NULL)
> + goto out;
> +
> + hostname = malloc(NI_MAXHOST + 1);
> + if (hostname == NULL)
> + goto out;
> + if (gethostname(hostname, NI_MAXHOST) == -1) {
> + retval = errno;
> + goto out;
> + }
> + strcat(hostname, "$");
> +
> + retval = EKEYEXPIRED;
> + gssd_k5_get_default_realm(&default_realm);
> + if (default_realm == NULL)
> + goto out;
> +
> + printerr(2, "looking for machine credentials in %s\n", ccache);
> + snprintf(buf, PATH_MAX + 5, "FILE:%s", ccache);
> + if (!query_krb5_ccache(buf, &princname, &realm))
> + goto out;
> +
> + if (strcasecmp(princname, hostname) != 0) {
> + printerr(2, "ERROR: found principal name %s in %s\n",
> + princname, ccache);
> + goto out;
> + }
> + if (strcasecmp(realm, default_realm) != 0) {
> + printerr(2, "ERROR: found realm name %s in %s\n",
> + realm, ccache);
> + goto out;
> + }
> + retval = 0;
> +
> +out:
> + free(realm);
> + free(princname);
> + free(default_realm);
> + free(buf);
> + free(hostname);
> + return retval;
> +}
>
> static inline int data_is_equal(krb5_data d1, krb5_data d2)
> {
> @@ -1147,6 +1204,16 @@ gssd_get_krb5_machine_cred_list(char ***list)
> }
> }
> }
> + if (i == 0 && mach_cred_file[0] != '\0') {
> + if (verify_mach_cc(mach_cred_file)) {
> + retval = EKEYEXPIRED;
> + goto out;
> + }
> + if ((l[i++] = strdup(mach_cred_file)) == NULL) {
> + retval = ENOMEM;
> + goto out;
> + }
> + }
> if (i > 0) {
> l[i] = NULL;
> *list = l;
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-03-08 19:47:16

by Chuck Lever III

[permalink] [raw]
Subject: [PATCH 08/11] rpc.gssd: Clean up gssd_setup_krb5_user_gss_ccache()

Remove a contradictory portion of the block comment documenting
gssd_find_existing_krb5_ccache(). This should have been removed by
commit 289ad31e, which reversed the meaning of the function's return
values.

Note that, in user space, typically errno's are positive. But here
we follow the kernel convention of using negative values to return
error codes. Make the documenting comments explicit about the sign
of an error return -- it will never be positive in the case of an
error.

And a nit: At the last return statement in
gssd_setup_krb5_user_gss_ccache(), "err" always contains zero, as
far as I can tell. Make it explicit (to human readers) that when
execution reaches this point, gssd_setup_krb5_user_gss_ccache() is
going to return "success."

Signed-off-by: Chuck Lever <[email protected]>
Cc: Jeff Layton <[email protected]>
---

utils/gssd/krb5_util.c | 13 +++++--------
1 files changed, 5 insertions(+), 8 deletions(-)

diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
index aeb8f70..6c37094 100644
--- a/utils/gssd/krb5_util.c
+++ b/utils/gssd/krb5_util.c
@@ -169,13 +169,10 @@ select_krb5_ccache(const struct dirent *d)

/*
* Look in directory "dirname" for files that look like they
- * are Kerberos Credential Cache files for a given UID. Return
- * non-zero and the dirent pointer for the entry most likely to be
- * what we want. Otherwise, return zero and no dirent pointer.
- * The caller is responsible for freeing the dirent if one is returned.
+ * are Kerberos Credential Cache files for a given UID.
*
- * Returns 0 if a valid-looking entry was found and a non-zero error
- * code otherwise.
+ * Returns 0 if a valid-looking entry was found, or a negative
+ * errno code otherwise.
*/
static int
gssd_find_existing_krb5_ccache(uid_t uid, char *dirname,
@@ -1037,7 +1034,7 @@ err_cache:
* given only a UID. We really need more information, but we
* do the best we can.
*
- * Returns 0 if a ccache was found, and a non-zero error code otherwise.
+ * Returns 0 if a ccache was found, or a negative errno otherwise.
*/
int
gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, char *dirpattern)
@@ -1082,7 +1079,7 @@ gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, char *dirpattern)
printerr(2, "using %s as credentials cache for client with "
"uid %u for server %s\n", buf, uid, servername);
gssd_set_krb5_ccache_name(buf);
- return err;
+ return 0;
}

/*


2013-03-08 19:46:32

by Chuck Lever III

[permalink] [raw]
Subject: [PATCH 03/11] gssd: Use italics for option values and pathnames

Clean up: The usual convention for the values of command line
options and for pathnames is for them to appear italicized,
rather than emboldened or in double quotes.

Signed-off-by: Chuck Lever <[email protected]>
---

utils/gssd/gssd.man | 30 +++++++++++++++++++++---------
1 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/utils/gssd/gssd.man b/utils/gssd/gssd.man
index c74b7e8..0cc7bf4 100644
--- a/utils/gssd/gssd.man
+++ b/utils/gssd/gssd.man
@@ -6,7 +6,18 @@
.SH NAME
rpc.gssd \- rpcsec_gss daemon
.SH SYNOPSIS
-.B "rpc.gssd [-f] [-n] [-k keytab] [-l] [-p pipefsdir] [-v] [-r] [-d ccachedir]"
+.B rpc.gssd
+.RB [ \-fnlvr ]
+.RB [ \-k
+.IR keytab ]
+.RB [ \-p
+.IR pipefsdir ]
+.RB [ \-d
+.IR ccachedir ]
+.RB [ \-t
+.IR timeout ]
+.RB [ \-R
+.IR realm ]
.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
@@ -38,13 +49,14 @@ manually like all other users. Use of this option means that
attempting to mount an nfs filesystem requiring Kerberos
authentication.
.TP
-.B -k keytab
+.BI "-k " keytab
Tells
.B rpc.gssd
to use the keys found in
.I keytab
to obtain "machine credentials".
-The default value is "/etc/krb5.keytab".
+The default value is
+.I /etc/krb5.keytab.
.IP
Previous versions of
.B rpc.gssd
@@ -94,17 +106,17 @@ encryption.
This option is only available with Kerberos libraries that
support setable encryption types.
.TP
-.B -p path
+.BI "-p " path
Tells
.B rpc.gssd
where to look for the rpc_pipefs filesystem. The default value is
-"/var/lib/nfs/rpc_pipefs".
+.IR /var/lib/nfs/rpc_pipefs .
.TP
-.B -d directory
+.BI "-d " directory
Tells
.B rpc.gssd
where to look for Kerberos credential files. The default value is
-"/tmp:/run/user/%U".
+.IR /tmp:/run/user/%U .
This can also be a colon separated list of directories to be searched for
Kerberos credential files. The sequence "%U", if used, is replaced with
the UID of the user for whom credentials are being searched.
@@ -119,14 +131,14 @@ Increases the verbosity of the output (can be specified multiple times).
If the rpcsec_gss library supports setting debug level,
increases the verbosity of the output (can be specified multiple times).
.TP
-.B -R realm
+.BI "-R " realm
Kerberos tickets from this
.I realm
will be preferred when scanning available credentials cache files to be
used to create a context. By default, the default realm, as configured
in the Kerberos configuration file, is preferred.
.TP
-.B -t timeout
+.BI "-t " timeout
Timeout, in seconds, for kernel gss contexts. This option allows you to force
new kernel contexts to be negotiated after
.I timeout


2013-03-08 19:47:43

by Chuck Lever III

[permalink] [raw]
Subject: [PATCH 11/11] gssd: Add "-c" command line option

When a system does not have a keytab, it has no way to obtain
machine credentials. In the past, we've fudged it by simply using
root's user credential as the machine credential. (root must kinit
in this case, of course).

Since v1 of the kernel's GSS upcall was introduced, rpc.gssd can
tell when the kernel requests a machine credential specifically,
say, for NFSv4 operations that are required to use one
(SETCLIENTID).

Since I moved SETCLIENTID to the first operation performed against
a new server, this means a sec=krb5 NFSv4 mount can't succeed at all
if there is no machine credential. Previously, this kind of mount
could work at least until a SETCLIENTID was done, as the kernel
could use root's user credential for housekeeping like grabbing the
server's root FH. (Or maybe there is some kind of gssd bug that
allowed it to keep working?).

In any event, what we want to do is provide a clean and secure way
for rpc.gssd to substitute a specific user credential if there is no
machine credential available. (I'm actually not clear on why this
is OK to do).

Recent work to add Active Directory support to rpc.gssd gives us a
potential user principal that can be used for this purpose:

HOSTNAME$@REALM

We have a specific user principal then. We have to tell rpc.gssd
where to look for the credential file. Introduce a command line
option that specifies exactly where the file containing the
credential for this principal can be found. This credential would
be used only if no other machine credential is available.

One might then use it this way:

# /usr/sbin/rpc.gssd -n -c /tmp/krb5cc_0
# kinit <hostname>$@<REALM>
Password for <hostname>$@<REALM>:
# mount -t nfs4 -o sec=krb5 server:/export /mnt

Signed-off-by: Chuck Lever <[email protected]>
---

utils/gssd/gssd.c | 14 +++++++++-
utils/gssd/gssd.h | 1 +
utils/gssd/gssd.man | 18 +++++++++++++
utils/gssd/krb5_util.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 98 insertions(+), 2 deletions(-)

diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c
index 0be2517..7537a97 100644
--- a/utils/gssd/gssd.c
+++ b/utils/gssd/gssd.c
@@ -58,6 +58,7 @@
char pipefs_dir[PATH_MAX] = GSSD_PIPEFS_DIR;
char keytabfile[PATH_MAX] = GSSD_DEFAULT_KEYTAB_FILE;
char ccachedir[PATH_MAX] = GSSD_DEFAULT_CRED_DIR ":" GSSD_USER_CRED_DIR;
+char mach_cred_file[PATH_MAX] = "";
char *ccachesearch[GSSD_MAX_CCACHE_SEARCH + 1];
int use_memcache = 0;
int root_uses_machine_creds = 1;
@@ -85,7 +86,9 @@ sig_hup(int signal)
static void
usage(char *progname)
{
- fprintf(stderr, "usage: %s [-f] [-l] [-M] [-n] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir] [-t timeout] [-R preferred realm]\n",
+ fprintf(stderr, "usage: %s [-f] [-l] [-M] [-n] [-v] [-r] "
+ "[-p pipefsdir] [-k keytab] [-d ccachedir] "
+ "[-t timeout] [-R preferred realm] [-c credfile]\n",
progname);
exit(1);
}
@@ -102,7 +105,7 @@ main(int argc, char *argv[])
char *progname;

memset(ccachesearch, 0, sizeof(ccachesearch));
- while ((opt = getopt(argc, argv, "fvrlmnMp:k:d:t:R")) != -1) {
+ while ((opt = getopt(argc, argv, "fvrlmnMp:k:d:t:R:c:")) != -1) {
switch (opt) {
case 'f':
fg = 1;
@@ -122,6 +125,11 @@ main(int argc, char *argv[])
case 'r':
rpc_verbosity++;
break;
+ case 'c':
+ strncpy(mach_cred_file, optarg, sizeof(mach_cred_file));
+ if (mach_cred_file[sizeof(mach_cred_file)-1] != '\0')
+ errx(1, "credential file name too long");
+ break;
case 'p':
strncpy(pipefs_dir, optarg, sizeof(pipefs_dir));
if (pipefs_dir[sizeof(pipefs_dir)-1] != '\0')
@@ -156,6 +164,8 @@ main(int argc, char *argv[])
}
}

+ if (mach_cred_file[0] != '\0' && use_memcache)
+ errx(1, "Cannot use memcache and credential file together");
i = 0;
ccachesearch[i++] = strtok(ccachedir, ":");
do {
diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h
index 86472a1..e76bad1 100644
--- a/utils/gssd/gssd.h
+++ b/utils/gssd/gssd.h
@@ -62,6 +62,7 @@ enum {AUTHTYPE_KRB5, AUTHTYPE_LIPKEY};

extern char pipefs_dir[PATH_MAX];
extern char keytabfile[PATH_MAX];
+extern char mach_cred_file[PATH_MAX];
extern char *ccachesearch[];
extern int use_memcache;
extern int root_uses_machine_creds;
diff --git a/utils/gssd/gssd.man b/utils/gssd/gssd.man
index 79d9bf9..ef26577 100644
--- a/utils/gssd/gssd.man
+++ b/utils/gssd/gssd.man
@@ -19,6 +19,8 @@ rpc.gssd \- RPCSEC_GSS daemon
.IR timeout ]
.RB [ \-R
.IR realm ]
+.RB [ \-c
+.IR file ]
.SH INTRODUCTION
The RPCSEC_GSS protocol, defined in RFC 5403, is used to provide
strong security for RPC-based protocols such as NFS.
@@ -146,6 +148,18 @@ You can specify another keytab by using the
option if
.I /etc/krb5.keytab
does not exist or does not provide one of these principals.
+.P
+If no keytab is found, the
+.B -c
+option can specify a file containing a credential that
+.B rpc.gssd
+should use as a machine credential.
+The principal must be of the form
+.sp
+ <HOSTNAME>$@<REALM>
+.sp
+Where <HOSTNAME> is the hostname of the local system, and <REALM> is the
+system's Kerberos realm.
.SS Credentials for UID 0
UID 0 is a special case.
By default
@@ -213,6 +227,10 @@ to obtain machine credentials.
The default value is
.IR /etc/krb5.keytab .
.TP
+.BI "-c " file
+Specifies a file in which to find a credential that can be used
+as a machine credential, if the local system has no keytab.
+.TP
.B -l
When specified, restricts
.B rpc.gssd
diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
index 215c0a4..c49aed9 100644
--- a/utils/gssd/krb5_util.c
+++ b/utils/gssd/krb5_util.c
@@ -937,6 +937,63 @@ out:
return retval;
}

+/*
+ * Returns zero if credential cache file can be used for a machine credential
+ */
+static int
+verify_mach_cc(const char *ccache)
+{
+ char *hostname = NULL;
+ char *default_realm = NULL;
+ char *buf = NULL;
+ char *princname = NULL;
+ char *realm = NULL;
+ int retval;
+
+ retval = ENOMEM;
+ buf = malloc(PATH_MAX + 5);
+ if (buf == NULL)
+ goto out;
+
+ hostname = malloc(NI_MAXHOST + 1);
+ if (hostname == NULL)
+ goto out;
+ if (gethostname(hostname, NI_MAXHOST) == -1) {
+ retval = errno;
+ goto out;
+ }
+ strcat(hostname, "$");
+
+ retval = EKEYEXPIRED;
+ gssd_k5_get_default_realm(&default_realm);
+ if (default_realm == NULL)
+ goto out;
+
+ printerr(2, "looking for machine credentials in %s\n", ccache);
+ snprintf(buf, PATH_MAX + 5, "FILE:%s", ccache);
+ if (!query_krb5_ccache(buf, &princname, &realm))
+ goto out;
+
+ if (strcasecmp(princname, hostname) != 0) {
+ printerr(2, "ERROR: found principal name %s in %s\n",
+ princname, ccache);
+ goto out;
+ }
+ if (strcasecmp(realm, default_realm) != 0) {
+ printerr(2, "ERROR: found realm name %s in %s\n",
+ realm, ccache);
+ goto out;
+ }
+ retval = 0;
+
+out:
+ free(realm);
+ free(princname);
+ free(default_realm);
+ free(buf);
+ free(hostname);
+ return retval;
+}

static inline int data_is_equal(krb5_data d1, krb5_data d2)
{
@@ -1147,6 +1204,16 @@ gssd_get_krb5_machine_cred_list(char ***list)
}
}
}
+ if (i == 0 && mach_cred_file[0] != '\0') {
+ if (verify_mach_cc(mach_cred_file)) {
+ retval = EKEYEXPIRED;
+ goto out;
+ }
+ if ((l[i++] = strdup(mach_cred_file)) == NULL) {
+ retval = ENOMEM;
+ goto out;
+ }
+ }
if (i > 0) {
l[i] = NULL;
*list = l;


2013-03-08 19:46:23

by Chuck Lever III

[permalink] [raw]
Subject: [PATCH 02/11] mountd: make local functions in v4root.c static

Clean up. set_pseudofs_security() and pseudofs_update() have no
call sites outside of v4root.c, and there are no header declarations
for either function. Define both as static.

Signed-off-by: Chuck Lever <[email protected]>
---

utils/mountd/v4root.c | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/utils/mountd/v4root.c b/utils/mountd/v4root.c
index 726b50d..34d098a 100644
--- a/utils/mountd/v4root.c
+++ b/utils/mountd/v4root.c
@@ -55,7 +55,8 @@ static nfs_export pseudo_root = {
.m_warned = 0,
};

-void set_pseudofs_security(struct exportent *pseudo, struct exportent *source)
+static void
+set_pseudofs_security(struct exportent *pseudo, struct exportent *source)
{
struct sec_entry *se;
int i;
@@ -121,7 +122,8 @@ v4root_support(void)
return 0;
}

-int pseudofs_update(char *hostname, char *path, nfs_export *source)
+static int
+pseudofs_update(char *hostname, char *path, nfs_export *source)
{
nfs_export *exp;