[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, §ype,
> - &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