[linux-cifs-client] Keytab support in cifs.spnego helper

Jeff Layton jlayton at redhat.com
Wed Apr 2 16:06:43 GMT 2008


On Wed, 02 Apr 2008 18:04:22 +0400
Igor Mammedov <niallain at gmail.com> wrote:

> From: Igor Mammedov <niallain at gmail.com>
> To: samba-technical at lists.samba.org
> Cc: Simo Sorce <ssorce at redhat.com>, linux-cifs-client at lists.samba.org, Jeff Layton <jlayton at redhat.com>
> Subject: [linux-cifs-client] Keytab support in cifs.spnego helper
> Date: Wed, 02 Apr 2008 18:04:22 +0400
> Sender: linux-cifs-client-bounces+jlayton=poochiereds.net at lists.samba.org
> User-Agent: Thunderbird 2.0.0.12 (X11/20080213)
> 
> 0001-Fix-cifs-upcall-key-name-for-DNS-resolving.patch - corrects key name for cifs dns upcall
> 
> 0002-Adds-support-for-using-krb5.keytab-for-non-interacti.patch - adds support for non interactive mount using keytab
> 
> Some notes:
> It uses default keytab (as it defined in kerberos libs or in krb5.conf).
> Mount options should look like this:
> 	user=my_krb5_username,password=fake,sec=krb5i
> 
> option: 'password' is just for mount.cifs to be happy and not ask for password.
> 
> Entry to keytab added with ktutil command:
> 
> 'addent -password -p my_krb5_username -k 1 -e rc4-hmac'
> 
> PS:
>  In my case only 'rc4-hmac' encryption worked (a krb5 libs' issue I think).
>  You could check with 'kinit -k' if added entry works.
> 
> -- 
> 
> Best regards,
> 
> -------------------------
> Igor Mammedov,
> niallain "at" gmail.com
> 
> 
> 
> 
> 
> >From fbc1e88fbb09623dd9ab5c957582e5af50fa4e1b Mon Sep 17 00:00:00 2001  
> From: Igor Mammedov <niallain at gmail.com>
> Date: Wed, 2 Apr 2008 17:21:12 +0400
> Subject: [PATCH] Fix cifs upcall key name for DNS resolving
> 
> Signed-off-by: Igor Mammedov <niallain at gmail.com>
> ---
>  source/client/cifs.spnego.c |    6 ++++--
>  1 files changed, 4 insertions(+), 2 deletions(-)
> 
> diff --git a/source/client/cifs.spnego.c b/source/client/cifs.spnego.c
> index d10d19d..ece0ad7 100644
> --- a/source/client/cifs.spnego.c
> +++ b/source/client/cifs.spnego.c
> @@ -9,7 +9,7 @@
>  * /etc/request-key.conf file
>  
>  create cifs.spnego * * /usr/local/sbin/cifs.spnego [-v][-c] %k
> -create cifs.resolver * * /usr/local/sbin/cifs.spnego [-v] %k
> +create dns_resolver * * /usr/local/sbin/cifs.spnego [-v] %k
>  
>  * This program is free software; you can redistribute it and/or modify
>  * it under the terms of the GNU General Public License as published by
> @@ -36,6 +36,8 @@ typedef enum _secType {
>  	MS_KRB5
>  } secType_t;
>  
> +const char *CIFSDNSRESOLVER_KEYTYPE = "dns_resolver";
> +
>  /*
>   * Prepares AP-REQ data for mechToken and gets session key
>   * Uses credentials from cache. It will not ask for password
> @@ -253,7 +255,7 @@ int main(const int argc, char *const argv[])
>  		goto out;
>  	}
>  
> -	if (strncmp(buf, "cifs.resolver", sizeof("cifs.resolver")-1) == 0) {
> +	if (strncmp(buf, CIFSDNSRESOLVER_KEYTYPE, sizeof(CIFSDNSRESOLVER_KEYTYPE)-1) == 0) {
>  		rc = cifs_resolver(key, buf);
>  		goto out;
>  	}
> -- 
> 1.5.3.7
> 
> 

Idle thought...

Since the cifs.spnego helper is now doing more than just SPNEGO stuff,
should we consider renaming it to something more generic? cifs.helper
or cifs.upcall maybe?

> >From 811c97e00c336d23239033929a098ed543a47963 Mon Sep 17 00:00:00 2001  
> From: Igor Mammedov <niallain at gmail.com>
> Date: Wed, 2 Apr 2008 17:23:43 +0400
> Subject: [PATCH] Adds support for using krb5.keytab for non interactive cifs mount
> 
> Signed-off-by: Igor Mammedov <niallain at gmail.com>
> ---
>  source/client/cifs.spnego.c |  175 +++++++++++++++++++++++++++++++++++--------
>  1 files changed, 143 insertions(+), 32 deletions(-)
> 
> diff --git a/source/client/cifs.spnego.c b/source/client/cifs.spnego.c
> index ece0ad7..58be941 100644
> --- a/source/client/cifs.spnego.c
> +++ b/source/client/cifs.spnego.c
> @@ -29,7 +29,7 @@ create dns_resolver * * /usr/local/sbin/cifs.spnego [-v] %k
>  
>  #include "cifs_spnego.h"
>  
> -const char *CIFSSPNEGO_VERSION = "1.1";
> +const char *CIFSSPNEGO_VERSION = "1.2";
>  static const char *prog = "cifs.spnego";
>  typedef enum _secType {
>  	KRB5,
> @@ -59,7 +59,7 @@ const char *CIFSDNSRESOLVER_KEYTYPE = "dns_resolver";
>   * ret: 0 - success, others - failure
>  */
>  int handle_krb5_mech(const char *oid, const char *principal,
> -		     DATA_BLOB * secblob, DATA_BLOB * sess_key)
> +		     DATA_BLOB *secblob, DATA_BLOB *sess_key)
>  {
>  	int retval;
>  	DATA_BLOB tkt, tkt_wrapped;
> @@ -82,16 +82,45 @@ int handle_krb5_mech(const char *oid, const char *principal,
>  	return retval;
>  }
>  
> +static char *keyd_copy_param(const char *start, const char *end)
> +{
> +	int len;
> +	char *buf;
> +
> +	if (end == NULL)
> +		len = strlen(start);
> +	else
> +		len = end - start;
> +
> +	len += 1;
> +	buf = SMB_XMALLOC_ARRAY(char, len);
> +	strlcpy(buf, start, len);
> +	return buf;
> +}
> +
>  #define DKD_HAVE_HOSTNAME	1
>  #define DKD_HAVE_VERSION	2
>  #define DKD_HAVE_SEC		4
>  #define DKD_HAVE_IPV4		8
>  #define DKD_HAVE_IPV6		16
>  #define DKD_HAVE_UID		32
> +#define DKD_HAVE_KEYTAB		64
> +#define DKD_HAVE_USERNAME	128
>  #define DKD_MUSTHAVE_SET (DKD_HAVE_HOSTNAME|DKD_HAVE_VERSION|DKD_HAVE_SEC)
>  
> -int decode_key_description(const char *desc, int *ver, secType_t * sec,
> -			   char **hostname, uid_t * uid)
> +/* decode_key_description - parse key description from the cifs spnego upcall
> + * in:
> + *	desc - key description from keyctl_describe_alloc
> + * out:
> + *	ver	- upcall version
> + *	sec	- type of kerberos mech
> + *	uid	- UID of user who mounted the share
> + *	keytab	- name of keytab file
> + *	user	- principal of the user to authenticate as
> + */
> +int decode_key_description(const char *desc, int *ver, secType_t *sec,
> +			   char **hostname, uid_t *uid,
> +			   char **keytab, char **user)
>  {
>  	int retval = 0;
>  	char *pos;
> @@ -100,17 +129,7 @@ int decode_key_description(const char *desc, int *ver, secType_t * sec,
>  	do {
>  		pos = index(tkn, ';');
>  		if (strncmp(tkn, "host=", 5) == 0) {
> -			int len;
> -
> -			if (pos == NULL) {
> -				len = strlen(tkn);
> -			} else {
> -				len = pos - tkn;
> -			}
> -			len -= 4;
> -			SAFE_FREE(*hostname);
> -			*hostname = SMB_XMALLOC_ARRAY(char, len);
> -			strlcpy(*hostname, tkn + 5, len);
> +			*hostname = keyd_copy_param(tkn+5, pos);
>  			retval |= DKD_HAVE_HOSTNAME;
>  		} else if (strncmp(tkn, "ipv4=", 5) == 0) {
>  			/* BB: do we need it if we have hostname already? */
> @@ -142,6 +161,12 @@ int decode_key_description(const char *desc, int *ver, secType_t * sec,
>  			} else {
>  				retval |= DKD_HAVE_VERSION;
>  			}
> +		} else if (strncmp(tkn, "cred=", 5) == 0) {
> +			*keytab = keyd_copy_param(tkn+5, pos);
> +			retval |= DKD_HAVE_KEYTAB;
> +		} else if (strncmp(tkn, "user=", 5) == 0) {
> +			*user = keyd_copy_param(tkn+5, pos);
> +			retval |= DKD_HAVE_USERNAME;
>  		}
>  		if (pos == NULL)
>  			break;
> @@ -177,14 +202,14 @@ int cifs_resolver(const key_serial_t key, const char *key_descr)
>  	}
>  
>  	/* conver ip to string form */
> -	if (addr->ai_family == AF_INET) {
> +	if (addr->ai_family == AF_INET)
>  		p = &(((struct sockaddr_in *)addr->ai_addr)->sin_addr);
> -	} else {
> +	else
>  		p = &(((struct sockaddr_in6 *)addr->ai_addr)->sin6_addr);
> -	}
> +
>  	if (!inet_ntop(addr->ai_family, p, ip, sizeof(ip))) {
>  		syslog(LOG_WARNING, "%s: inet_ntop: %s",
> -				__FUNCTION__, strerror(errno));
> +				__func__, strerror(errno));
>  		freeaddrinfo(addr);
>  		return 1;
>  	}
> @@ -193,7 +218,7 @@ int cifs_resolver(const key_serial_t key, const char *key_descr)
>  	c = keyctl_instantiate(key, ip, strlen(ip)+1, 0);
>  	if (c == -1) {
>  		syslog(LOG_WARNING, "%s: keyctl_instantiate: %s",
> -				__FUNCTION__, strerror(errno));
> +				__func__, strerror(errno));
>  		freeaddrinfo(addr);
>  		return 1;
>  	}
> @@ -202,6 +227,80 @@ int cifs_resolver(const key_serial_t key, const char *key_descr)
>  	return 0;
>  }
>  
> +static int init_cc_from_keytab(const char *keytab_name, const char *user)
> +{
> +	krb5_context context = NULL;
> +	krb5_error_code ret;
> +	krb5_creds my_creds;
> +	krb5_keytab keytab = NULL;
> +	krb5_principal me = NULL;
> +	krb5_ccache cc = NULL;
> +
> +	memset((char *) &my_creds, 0, sizeof(my_creds));
> +
> +	ret = krb5_init_context(&context);
> +	if (ret) {
> +		syslog(LOG_WARNING, "krb5_init_context: %s",
> +				error_message(ret));
> +		goto icfk_cleanup;
> +	}
> +
> +	ret = smb_krb5_open_keytab(context, keytab_name, False, &keytab);
> +	if (ret) {
> +		syslog(LOG_WARNING, "smb_krb5_open_keytab: %s",
> +				error_message(ret));
> +		goto icfk_cleanup;
> +	}
> +
> +	ret = krb5_parse_name(context, user, &me);
> +	if (ret) {
> +		syslog(LOG_WARNING, "krb5_parse_name: %s", error_message(ret));
> +		goto icfk_cleanup;
> +	}
> +
> +	ret = krb5_get_init_creds_keytab(context, &my_creds, me,
> +			keytab, 0, NULL, NULL);
> +	if (ret) {
> +		syslog(LOG_WARNING, "krb5_get_init_creds_keytab: %s",
> +				error_message(ret));
> +		goto icfk_cleanup;
> +	}
> +
> +	ret = krb5_cc_default(context, &cc);
> +	if (ret) {
> +		syslog(LOG_WARNING, "krb5_cc_default: %s", error_message(ret));
> +		goto icfk_cleanup;
> +	}
> +
> +	ret = krb5_cc_initialize(context, cc, me);
> +	if (ret) {
> +		syslog(LOG_WARNING, "krb5_cc_initialize: %s",
> +				error_message(ret));
> +		goto icfk_cleanup;
> +	}
> +
> +	ret = krb5_cc_store_cred(context, cc, &my_creds);
> +	if (ret) {
> +		syslog(LOG_WARNING, "krb5_cc_store_cred: %s",
> +				error_message(ret));
> +	}
> +
> +icfk_cleanup:
> +	my_creds.client = 0;
> +	krb5_free_cred_contents(context, &my_creds);
> +
> +	if (me)
> +		krb5_free_principal(context, me);
> +	if (cc)
> +		krb5_cc_close(context, cc);
> +	if (keytab)
> +		krb5_kt_close(context, keytab);
> +	if (context)
> +		krb5_free_context(context);
> +	return 0;
> +}
> +
> +
>  int main(const int argc, char *const argv[])
>  {
>  	struct cifs_spnego_msg *keydata = NULL;
> @@ -215,6 +314,7 @@ int main(const int argc, char *const argv[])
>  	int kernel_upcall_version;
>  	int c, use_cifs_service_prefix = 0;
>  	char *buf, *hostname = NULL;
> +	char *keytab, *user = NULL;
>  
>  	openlog(prog, 0, LOG_DAEMON);
>  	if (argc < 1) {
> @@ -230,7 +330,8 @@ int main(const int argc, char *const argv[])
>  			}
>  		case 'v':{
>  			syslog(LOG_WARNING, "version: %s", CIFSSPNEGO_VERSION);
> -			fprintf(stderr, "version: %s", CIFSSPNEGO_VERSION);
> +			fprintf(stderr, "version: %s\n", CIFSSPNEGO_VERSION);
> +			return 0;
>  			break;
>  			}
>  		default:{
> @@ -255,13 +356,14 @@ int main(const int argc, char *const argv[])
>  		goto out;
>  	}
>  
> -	if (strncmp(buf, CIFSDNSRESOLVER_KEYTYPE, sizeof(CIFSDNSRESOLVER_KEYTYPE)-1) == 0) {
> +	if (strncmp(buf, CIFSDNSRESOLVER_KEYTYPE,
> +				sizeof(CIFSDNSRESOLVER_KEYTYPE)-1) == 0) {
>  		rc = cifs_resolver(key, buf);
>  		goto out;
>  	}
>  
>  	rc = decode_key_description(buf, &kernel_upcall_version, &sectype,
> -				    &hostname, &uid);
> +				    &hostname, &uid, &keytab, &user);
>  	if ((rc & DKD_MUSTHAVE_SET) != DKD_MUSTHAVE_SET) {
>  		syslog(LOG_WARNING,
>  		       "unable to get from description necessary params");
> @@ -290,11 +392,12 @@ int main(const int argc, char *const argv[])
>  	/* BB: someday upcall SPNEGO blob could be checked here to decide
>  	 * what mech to use */
>  
> -	// do mech specific authorization
> +	/* do mech specific authorization */
>  	switch (sectype) {
>  	case KRB5:{
>  			char *princ;
>  			size_t len;
> +			int keytab_done = 0;
>  
>  			/* for "cifs/" service name + terminating 0 */
>  			len = strlen(hostname) + 5 + 1;
> @@ -303,15 +406,20 @@ int main(const int argc, char *const argv[])
>  				rc = 1;
>  				break;
>  			}
> -			if (use_cifs_service_prefix) {
> +			if (use_cifs_service_prefix)
>  				strlcpy(princ, "cifs/", len);
> -			} else {
> +			else
>  				strlcpy(princ, "host/", len);
> -			}
> -			strlcpy(princ + 5, hostname, len - 5);
>  
> +			strlcpy(princ + 5, hostname, len - 5);
> +TRY_KRB5_AGAIN:
>  			rc = handle_krb5_mech(OID_KERBEROS5, princ,
>  					      &secblob, &sess_key);
> +			if (rc && user && !keytab_done) {
> +				init_cc_from_keytab(keytab, user);
> +				keytab_done = 1;
> +				goto TRY_KRB5_AGAIN;
> +			}
>  			SAFE_FREE(princ);
>  			break;
>  		}
> @@ -323,14 +431,14 @@ int main(const int argc, char *const argv[])
>  		}
>  	}
>  
> -	if (rc) {
> +	if (rc)
>  		goto out;
> -	}
> +
>  
>  	/* pack SecurityBLob and SessionKey into downcall packet */
>  	datalen =
>  	    sizeof(struct cifs_spnego_msg) + secblob.length + sess_key.length;
> -	keydata = (struct cifs_spnego_msg*)SMB_XMALLOC_ARRAY(char, datalen);
> +	keydata = (struct cifs_spnego_msg *)SMB_XMALLOC_ARRAY(char, datalen);
>  	if (!keydata) {
>  		rc = 1;
>  		goto out;
> @@ -353,10 +461,13 @@ int main(const int argc, char *const argv[])
>  	/* BB: maybe we need use timeout for key: for example no more then
>  	 * ticket lifietime? */
>  	/* keyctl_set_timeout( key, 60); */
> -      out:
> +out:
>  	data_blob_free(&secblob);
>  	data_blob_free(&sess_key);
>  	SAFE_FREE(hostname);
> +	SAFE_FREE(keytab);
> +	SAFE_FREE(user);
>  	SAFE_FREE(keydata);
> +	closelog();
>  	return rc;
>  }
> -- 
> 1.5.3.7
> 
> 

The rest looks reasonable to me at first glance...

-- 
Jeff Layton <jlayton at redhat.com>


More information about the linux-cifs-client mailing list