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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mar 11, 2013, at 1:58 PM, J. Bruce Fields <bfields@xxxxxxxxxxxx> 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 bfields@xxxxxxxxxxx and
> a home directory files.example.edu:/home/bfields.
> 
> Then previously I could access my home directory from my personal laptop
> with
> 
> 	# kinit bfields@xxxxxxxxx
> 	# 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 bfields$@umich.edu?
> 
> 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 <chuck.lever@xxxxxxxxxx>
>> ---
>> 
>> 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 majordomo@xxxxxxxxxxxxxxx
>> 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 majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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




--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Filesystem Development]     [Linux USB Development]     [Linux Media Development]     [Video for Linux]     [Linux NILFS]     [Linux Audio Users]     [Yosemite Info]     [Linux SCSI]

  Powered by Linux