From cdabee2bb45f51f49a0c7148fe5b761d1c13658c Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 16 Jul 2008 17:27:05 -0700 Subject: This patchset comprises a number of cleanups for the cifs upcall binary. The biggest change is that it renames it from cifs.spnego to cifs.upcall since the cifs.spnego name really isn't applicable anymore. It also fixes a segfault when the program is run without any args and adds a manpage. Comments and/or suggestions appreciated. This set should apply cleanly to the 3.3 test branch. Signed-off-by: Jeff Layton Jeremy. (This used to be commit c633f10d9e78327664e6bca51f66756bcf0505a6) --- docs-xml/manpages-3/cifs.upcall.8.xml | 115 +++++++++++ source3/Makefile.in | 20 +- source3/client/cifs.spnego.c | 360 --------------------------------- source3/client/cifs.upcall.c | 369 ++++++++++++++++++++++++++++++++++ source3/configure.in | 32 +-- 5 files changed, 510 insertions(+), 386 deletions(-) create mode 100644 docs-xml/manpages-3/cifs.upcall.8.xml delete mode 100644 source3/client/cifs.spnego.c create mode 100644 source3/client/cifs.upcall.c diff --git a/docs-xml/manpages-3/cifs.upcall.8.xml b/docs-xml/manpages-3/cifs.upcall.8.xml new file mode 100644 index 0000000000..8df776bbd4 --- /dev/null +++ b/docs-xml/manpages-3/cifs.upcall.8.xml @@ -0,0 +1,115 @@ + + + + + + + cifs.upcall + 8 + Samba + System Administration tools + 3.2 + + + + cifs.upcall + Userspace upcall helper for Common Internet File System (CIFS) + + + + + cifs.upcall + -c + -v + keyid + + + + + + DESCRIPTION + + This tool is part of the samba + 7 suite. + +cifs.upcall is a userspace helper program for the linux CIFS client +filesystem. There are a number of activities that the kernel cannot easily +do itself. This program is a callout program that does these things for the +kernel and then returns the result. + +cifs.upcall is generally intended to be run when the kernel calls +request-key8 for a particular key type. While it +can be run directly from the command-line, it's not generally intended +to be run that way. + + + + OPTIONS + + + -c + When handling a kerberos upcall, use a service principal that starts with "cifs/". The default is to use the "host/" service principal. + + + + + -v + Print version number and exit. + + + + + + + CONFIGURATION FOR KEYCTL + cifs.upcall is designed to be called from the kernel via the request-key callout program. This requres that request-key be told where and how to call this program. The current cifs.upcall program handles two different key types: + + + + cifs.spnego + This keytype is for retrieving kerberos session keys + + + + + cifs.resolve + This key type is for resolving hostnames into IP addresses + + + + + To make this program useful for CIFS, you'll need to set up entries for them in request-key.conf5. Here's an example of an entry for each key type: + +#OPERATION TYPE D C PROGRAM ARG1 ARG2... +#========= ============= = = ========================================== +create cifs.spnego * * /usr/local/sbin/cifs.upcall -c %k +create cifs.resolver * * /usr/local/sbin/cifs.upcall %k + + +See request-key.conf5 for more info on each field. + + + + + SEE ALSO + + request-key.conf + 5, + mount.cifs + 8 + + + + + AUTHOR + + Igor Mammedov wrote the cifs.upcall program. + Jeff Layton authored this manpage. + The maintainer of the Linux CIFS VFS is Steve French. + The Linux + CIFS Mailing list is the preferred place to ask + questions regarding these programs. + + + + diff --git a/source3/Makefile.in b/source3/Makefile.in index cd70183711..85837a8943 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -178,7 +178,7 @@ PATH_FLAGS = -DSMB_PASSWD_FILE=\"$(SMB_PASSWD_FILE)\" \ SBIN_PROGS = bin/smbd@EXEEXT@ bin/nmbd@EXEEXT@ @SWAT_SBIN_TARGETS@ @EXTRA_SBIN_PROGS@ -ROOT_SBIN_PROGS = @CIFSMOUNT_PROGS@ @CIFSSPNEGO_PROGS@ +ROOT_SBIN_PROGS = @CIFSMOUNT_PROGS@ @CIFSUPCALL_PROGS@ BIN_PROGS1 = bin/smbclient@EXEEXT@ bin/net@EXEEXT@ bin/smbspool@EXEEXT@ \ bin/testparm@EXEEXT@ bin/smbstatus@EXEEXT@ bin/smbget@EXEEXT@ @@ -878,7 +878,7 @@ CIFS_MOUNT_OBJ = client/mount.cifs.o CIFS_UMOUNT_OBJ = client/umount.cifs.o -CIFS_SPNEGO_OBJ = client/cifs.spnego.o +CIFS_UPCALL_OBJ = client/cifs.upcall.o NMBLOOKUP_OBJ = utils/nmblookup.o $(PARAM_OBJ) $(LIBNMB_OBJ) \ $(LIB_NONSMBD_OBJ) $(POPT_LIB_OBJ) $(LIBSAMBA_OBJ) @@ -1340,9 +1340,9 @@ bin/umount.cifs@EXEEXT@: $(BINARY_PREREQS) $(CIFS_UMOUNT_OBJ) @BUILD_POPT@ @echo Linking $@ @$(CC) $(FLAGS) -o $@ $(CIFS_UMOUNT_OBJ) $(DYNEXP) $(LDFLAGS) $(POPT_LIBS) -bin/cifs.spnego@EXEEXT@: $(BINARY_PREREQS) $(CIFS_SPNEGO_OBJ) $(LIBSMBCLIENT_OBJ1) @BUILD_POPT@ @LIBTALLOC_SHARED@ @LIBTDB_SHARED@ @LIBWBCLIENT_SHARED@ +bin/cifs.upcall@EXEEXT@: $(BINARY_PREREQS) $(CIFS_UPCALL_OBJ) $(LIBSMBCLIENT_OBJ1) @BUILD_POPT@ @LIBTALLOC_SHARED@ @LIBTDB_SHARED@ @LIBWBCLIENT_SHARED@ @echo Linking $@ - @$(CC) $(FLAGS) -o $@ $(CIFS_SPNEGO_OBJ) $(DYNEXP) $(LDFLAGS) \ + @$(CC) $(FLAGS) -o $@ $(CIFS_UPCALL_OBJ) $(DYNEXP) $(LDFLAGS) \ -lkeyutils $(LIBS) $(LIBSMBCLIENT_OBJ1) $(KRB5LIBS) \ $(LDAP_LIBS) $(POPT_LIBS) $(LIBTALLOC_LIBS) $(WINBIND_LIBS) \ $(LIBTDB_LIBS) @@ -2449,7 +2449,7 @@ bin/rpc_open_tcp@EXEEXT@: $(BINARY_PREREQS) $(RPC_OPEN_TCP_OBJ) @LIBTALLOC_SHARE $(LIBS) $(LIBTALLOC_LIBS) @LIBTDB_SHARED@ $(WINBIND_LIBS) \ $(LDAP_LIBS) $(KRB5LIBS) $(NSCD_LIBS) -install:: installservers installbin @INSTALL_CIFSMOUNT@ @INSTALL_CIFSSPNEGO@ installman \ +install:: installservers installbin @INSTALL_CIFSMOUNT@ @INSTALL_CIFSUPCALL@ installman \ installscripts installdat installmodules @SWAT_INSTALL_TARGETS@ \ @INSTALL_PAM_MODULES@ installlibs @@ -2476,9 +2476,9 @@ installcifsmount:: @CIFSMOUNT_PROGS@ @$(SHELL) $(srcdir)/script/installdirs.sh $(INSTALLPERMS_BIN) $(DESTDIR) $(ROOTSBINDIR) @$(SHELL) script/installbin.sh $(INSTALLPERMS_BIN) $(DESTDIR) $(prefix) $(ROOTSBINDIR) @CIFSMOUNT_PROGS@ -installcifsspnego:: @CIFSSPNEGO_PROGS@ +installcifsupcall:: @CIFSUPCALL_PROGS@ @$(SHELL) $(srcdir)/script/installdirs.sh $(INSTALLPERMS_BIN) $(DESTDIR) $(ROOTSBINDIR) - @$(SHELL) script/installbin.sh $(INSTALLPERMS_BIN) $(DESTDIR) $(prefix) $(ROOTSBINDIR) @CIFSSPNEGO_PROGS@ + @$(SHELL) script/installbin.sh $(INSTALLPERMS_BIN) $(DESTDIR) $(prefix) $(ROOTSBINDIR) @CIFSUPCALL_PROGS@ # Some symlinks are required for the 'probing' of modules. # This mechanism should go at some point.. @@ -2545,7 +2545,7 @@ showlayout:: @echo " swatdir: $(SWATDIR)" -uninstall:: uninstallman uninstallservers uninstallbin @UNINSTALL_CIFSMOUNT@ @UNINSTALL_CIFSSPNEGO@ uninstallscripts uninstalldat uninstallswat uninstallmodules uninstalllibs @UNINSTALL_PAM_MODULES@ +uninstall:: uninstallman uninstallservers uninstallbin @UNINSTALL_CIFSMOUNT@ @UNINSTALL_CIFSUPCALL@ uninstallscripts uninstalldat uninstallswat uninstallmodules uninstalllibs @UNINSTALL_PAM_MODULES@ uninstallman:: @$(SHELL) $(srcdir)/script/uninstallman.sh $(DESTDIR)$(MANDIR) $(srcdir) C @@ -2559,8 +2559,8 @@ uninstallbin:: uninstallcifsmount:: @$(SHELL) script/uninstallbin.sh $(INSTALLPERMS_BIN) $(DESTDIR) $(prefix) $(ROOTSBINDIR) @CIFSMOUNT_PROGS@ -uninstallcifsspnego:: - @$(SHELL) script/uninstallbin.sh $(INSTALLPERMS_BIN) $(DESTDIR) $(prefix) $(ROOTSBINDIR) @CIFSSPNEGO_PROGS@ +uninstallcifsupcall:: + @$(SHELL) script/uninstallbin.sh $(INSTALLPERMS_BIN) $(DESTDIR) $(prefix) $(ROOTSBINDIR) @CIFSUPCALL_PROGS@ uninstallmodules:: @$(SHELL) $(srcdir)/script/uninstallmodules.sh $(INSTALLPERMS_BIN) $(DESTDIR) $(prefix) $(VFSLIBDIR) $(VFS_MODULES) diff --git a/source3/client/cifs.spnego.c b/source3/client/cifs.spnego.c deleted file mode 100644 index d10d19da96..0000000000 --- a/source3/client/cifs.spnego.c +++ /dev/null @@ -1,360 +0,0 @@ -/* -* CIFS SPNEGO user-space helper. -* Copyright (C) Igor Mammedov (niallain@gmail.com) 2007 -* -* Used by /sbin/request-key for handling -* cifs upcall for kerberos authorization of access to share and -* cifs upcall for DFS srver name resolving (IPv4/IPv6 aware). -* You should have keyutils installed and add following line to -* /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 - -* 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 -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include "includes.h" -#include - -#include "cifs_spnego.h" - -const char *CIFSSPNEGO_VERSION = "1.1"; -static const char *prog = "cifs.spnego"; -typedef enum _secType { - KRB5, - MS_KRB5 -} secType_t; - -/* - * Prepares AP-REQ data for mechToken and gets session key - * Uses credentials from cache. It will not ask for password - * you should receive credentials for yuor name manually using - * kinit or whatever you wish. - * - * in: - * oid - string with OID/ Could be OID_KERBEROS5 - * or OID_KERBEROS5_OLD - * principal - Service name. - * Could be "cifs/FQDN" for KRB5 OID - * or for MS_KRB5 OID style server principal - * like "pdc$@YOUR.REALM.NAME" - * - * out: - * secblob - pointer for spnego wrapped AP-REQ data to be stored - * sess_key- pointer for SessionKey data to be stored - * - * ret: 0 - success, others - failure -*/ -int handle_krb5_mech(const char *oid, const char *principal, - DATA_BLOB * secblob, DATA_BLOB * sess_key) -{ - int retval; - DATA_BLOB tkt, tkt_wrapped; - - /* get a kerberos ticket for the service and extract the session key */ - retval = cli_krb5_get_ticket(principal, 0, - &tkt, sess_key, 0, NULL, NULL); - - if (retval) - return retval; - - /* wrap that up in a nice GSS-API wrapping */ - tkt_wrapped = spnego_gen_krb5_wrap(tkt, TOK_ID_KRB_AP_REQ); - - /* and wrap that in a shiny SPNEGO wrapper */ - *secblob = gen_negTokenInit(OID_KERBEROS5, tkt_wrapped); - - data_blob_free(&tkt_wrapped); - data_blob_free(&tkt); - return retval; -} - -#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_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) -{ - int retval = 0; - char *pos; - const char *tkn = desc; - - 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); - retval |= DKD_HAVE_HOSTNAME; - } else if (strncmp(tkn, "ipv4=", 5) == 0) { - /* BB: do we need it if we have hostname already? */ - } else if (strncmp(tkn, "ipv6=", 5) == 0) { - /* BB: do we need it if we have hostname already? */ - } else if (strncmp(tkn, "sec=", 4) == 0) { - if (strncmp(tkn + 4, "krb5", 4) == 0) { - retval |= DKD_HAVE_SEC; - *sec = KRB5; - } - } else if (strncmp(tkn, "uid=", 4) == 0) { - errno = 0; - *uid = strtol(tkn + 4, NULL, 16); - if (errno != 0) { - syslog(LOG_WARNING, "Invalid uid format: %s", - strerror(errno)); - return 1; - } else { - retval |= DKD_HAVE_UID; - } - } else if (strncmp(tkn, "ver=", 4) == 0) { /* if version */ - errno = 0; - *ver = strtol(tkn + 4, NULL, 16); - if (errno != 0) { - syslog(LOG_WARNING, - "Invalid version format: %s", - strerror(errno)); - return 1; - } else { - retval |= DKD_HAVE_VERSION; - } - } - if (pos == NULL) - break; - tkn = pos + 1; - } while (tkn); - return retval; -} - -int cifs_resolver(const key_serial_t key, const char *key_descr) -{ - int c; - struct addrinfo *addr; - char ip[INET6_ADDRSTRLEN]; - void *p; - const char *keyend = key_descr; - /* skip next 4 ';' delimiters to get to description */ - for (c = 1; c <= 4; c++) { - keyend = index(keyend+1, ';'); - if (!keyend) { - syslog(LOG_WARNING, "invalid key description: %s", - key_descr); - return 1; - } - } - keyend++; - - /* resolve name to ip */ - c = getaddrinfo(keyend, NULL, NULL, &addr); - if (c) { - syslog(LOG_WARNING, "unable to resolve hostname: %s [%s]", - keyend, gai_strerror(c)); - return 1; - } - - /* conver ip to string form */ - if (addr->ai_family == AF_INET) { - p = &(((struct sockaddr_in *)addr->ai_addr)->sin_addr); - } 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)); - freeaddrinfo(addr); - return 1; - } - - /* setup key */ - c = keyctl_instantiate(key, ip, strlen(ip)+1, 0); - if (c == -1) { - syslog(LOG_WARNING, "%s: keyctl_instantiate: %s", - __FUNCTION__, strerror(errno)); - freeaddrinfo(addr); - return 1; - } - - freeaddrinfo(addr); - return 0; -} - -int main(const int argc, char *const argv[]) -{ - struct cifs_spnego_msg *keydata = NULL; - DATA_BLOB secblob = data_blob_null; - DATA_BLOB sess_key = data_blob_null; - secType_t sectype; - key_serial_t key; - size_t datalen; - long rc = 1; - uid_t uid; - int kernel_upcall_version; - int c, use_cifs_service_prefix = 0; - char *buf, *hostname = NULL; - - openlog(prog, 0, LOG_DAEMON); - if (argc < 1) { - syslog(LOG_WARNING, "Usage: %s [-c] key_serial", prog); - goto out; - } - - while ((c = getopt(argc, argv, "cv")) != -1) { - switch (c) { - case 'c':{ - use_cifs_service_prefix = 1; - break; - } - case 'v':{ - syslog(LOG_WARNING, "version: %s", CIFSSPNEGO_VERSION); - fprintf(stderr, "version: %s", CIFSSPNEGO_VERSION); - break; - } - default:{ - syslog(LOG_WARNING, "unknow option: %c", c); - goto out; - } - } - } - /* get key and keyring values */ - errno = 0; - key = strtol(argv[optind], NULL, 10); - if (errno != 0) { - syslog(LOG_WARNING, "Invalid key format: %s", strerror(errno)); - goto out; - } - - rc = keyctl_describe_alloc(key, &buf); - if (rc == -1) { - syslog(LOG_WARNING, "keyctl_describe_alloc failed: %s", - strerror(errno)); - rc = 1; - goto out; - } - - if (strncmp(buf, "cifs.resolver", sizeof("cifs.resolver")-1) == 0) { - rc = cifs_resolver(key, buf); - goto out; - } - - rc = decode_key_description(buf, &kernel_upcall_version, §ype, - &hostname, &uid); - if ((rc & DKD_MUSTHAVE_SET) != DKD_MUSTHAVE_SET) { - syslog(LOG_WARNING, - "unable to get from description necessary params"); - rc = 1; - SAFE_FREE(buf); - goto out; - } - SAFE_FREE(buf); - - if (kernel_upcall_version != CIFS_SPNEGO_UPCALL_VERSION) { - syslog(LOG_WARNING, - "incompatible kernel upcall version: 0x%x", - kernel_upcall_version); - rc = 1; - goto out; - } - - if (rc & DKD_HAVE_UID) { - rc = setuid(uid); - if (rc == -1) { - syslog(LOG_WARNING, "setuid: %s", strerror(errno)); - goto out; - } - } - - /* BB: someday upcall SPNEGO blob could be checked here to decide - * what mech to use */ - - // do mech specific authorization - switch (sectype) { - case KRB5:{ - char *princ; - size_t len; - - /* for "cifs/" service name + terminating 0 */ - len = strlen(hostname) + 5 + 1; - princ = SMB_XMALLOC_ARRAY(char, len); - if (!princ) { - rc = 1; - break; - } - if (use_cifs_service_prefix) { - strlcpy(princ, "cifs/", len); - } else { - strlcpy(princ, "host/", len); - } - strlcpy(princ + 5, hostname, len - 5); - - rc = handle_krb5_mech(OID_KERBEROS5, princ, - &secblob, &sess_key); - SAFE_FREE(princ); - break; - } - default:{ - syslog(LOG_WARNING, "sectype: %d is not implemented", - sectype); - rc = 1; - break; - } - } - - 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); - if (!keydata) { - rc = 1; - goto out; - } - keydata->version = CIFS_SPNEGO_UPCALL_VERSION; - keydata->flags = 0; - keydata->sesskey_len = sess_key.length; - keydata->secblob_len = secblob.length; - memcpy(&(keydata->data), sess_key.data, sess_key.length); - memcpy(&(keydata->data) + keydata->sesskey_len, - secblob.data, secblob.length); - - /* setup key */ - rc = keyctl_instantiate(key, keydata, datalen, 0); - if (rc == -1) { - syslog(LOG_WARNING, "keyctl_instantiate: %s", strerror(errno)); - goto out; - } - - /* BB: maybe we need use timeout for key: for example no more then - * ticket lifietime? */ - /* keyctl_set_timeout( key, 60); */ - out: - data_blob_free(&secblob); - data_blob_free(&sess_key); - SAFE_FREE(hostname); - SAFE_FREE(keydata); - return rc; -} diff --git a/source3/client/cifs.upcall.c b/source3/client/cifs.upcall.c new file mode 100644 index 0000000000..3860f33e38 --- /dev/null +++ b/source3/client/cifs.upcall.c @@ -0,0 +1,369 @@ +/* +* CIFS user-space helper. +* Copyright (C) Igor Mammedov (niallain@gmail.com) 2007 +* +* Used by /sbin/request-key for handling +* cifs upcall for kerberos authorization of access to share and +* cifs upcall for DFS srver name resolving (IPv4/IPv6 aware). +* You should have keyutils installed and add following line to +* /etc/request-key.conf file + +create cifs.spnego * * /usr/local/sbin/cifs.upcall [-v][-c] %k +create cifs.resolver * * /usr/local/sbin/cifs.upcall [-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 +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "includes.h" +#include + +#include "cifs_spnego.h" + +const char *CIFSSPNEGO_VERSION = "1.1"; +static const char *prog = "cifs.upcall"; +typedef enum _secType { + KRB5, + MS_KRB5 +} secType_t; + +/* + * Prepares AP-REQ data for mechToken and gets session key + * Uses credentials from cache. It will not ask for password + * you should receive credentials for yuor name manually using + * kinit or whatever you wish. + * + * in: + * oid - string with OID/ Could be OID_KERBEROS5 + * or OID_KERBEROS5_OLD + * principal - Service name. + * Could be "cifs/FQDN" for KRB5 OID + * or for MS_KRB5 OID style server principal + * like "pdc$@YOUR.REALM.NAME" + * + * out: + * secblob - pointer for spnego wrapped AP-REQ data to be stored + * sess_key- pointer for SessionKey data to be stored + * + * ret: 0 - success, others - failure +*/ +int handle_krb5_mech(const char *oid, const char *principal, + DATA_BLOB * secblob, DATA_BLOB * sess_key) +{ + int retval; + DATA_BLOB tkt, tkt_wrapped; + + /* get a kerberos ticket for the service and extract the session key */ + retval = cli_krb5_get_ticket(principal, 0, + &tkt, sess_key, 0, NULL, NULL); + + if (retval) + return retval; + + /* wrap that up in a nice GSS-API wrapping */ + tkt_wrapped = spnego_gen_krb5_wrap(tkt, TOK_ID_KRB_AP_REQ); + + /* and wrap that in a shiny SPNEGO wrapper */ + *secblob = gen_negTokenInit(OID_KERBEROS5, tkt_wrapped); + + data_blob_free(&tkt_wrapped); + data_blob_free(&tkt); + return retval; +} + +#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_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) +{ + int retval = 0; + char *pos; + const char *tkn = desc; + + 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); + retval |= DKD_HAVE_HOSTNAME; + } else if (strncmp(tkn, "ipv4=", 5) == 0) { + /* BB: do we need it if we have hostname already? */ + } else if (strncmp(tkn, "ipv6=", 5) == 0) { + /* BB: do we need it if we have hostname already? */ + } else if (strncmp(tkn, "sec=", 4) == 0) { + if (strncmp(tkn + 4, "krb5", 4) == 0) { + retval |= DKD_HAVE_SEC; + *sec = KRB5; + } + } else if (strncmp(tkn, "uid=", 4) == 0) { + errno = 0; + *uid = strtol(tkn + 4, NULL, 16); + if (errno != 0) { + syslog(LOG_WARNING, "Invalid uid format: %s", + strerror(errno)); + return 1; + } else { + retval |= DKD_HAVE_UID; + } + } else if (strncmp(tkn, "ver=", 4) == 0) { /* if version */ + errno = 0; + *ver = strtol(tkn + 4, NULL, 16); + if (errno != 0) { + syslog(LOG_WARNING, + "Invalid version format: %s", + strerror(errno)); + return 1; + } else { + retval |= DKD_HAVE_VERSION; + } + } + if (pos == NULL) + break; + tkn = pos + 1; + } while (tkn); + return retval; +} + +int cifs_resolver(const key_serial_t key, const char *key_descr) +{ + int c; + struct addrinfo *addr; + char ip[INET6_ADDRSTRLEN]; + void *p; + const char *keyend = key_descr; + /* skip next 4 ';' delimiters to get to description */ + for (c = 1; c <= 4; c++) { + keyend = index(keyend+1, ';'); + if (!keyend) { + syslog(LOG_WARNING, "invalid key description: %s", + key_descr); + return 1; + } + } + keyend++; + + /* resolve name to ip */ + c = getaddrinfo(keyend, NULL, NULL, &addr); + if (c) { + syslog(LOG_WARNING, "unable to resolve hostname: %s [%s]", + keyend, gai_strerror(c)); + return 1; + } + + /* conver ip to string form */ + if (addr->ai_family == AF_INET) { + p = &(((struct sockaddr_in *)addr->ai_addr)->sin_addr); + } 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)); + freeaddrinfo(addr); + return 1; + } + + /* setup key */ + c = keyctl_instantiate(key, ip, strlen(ip)+1, 0); + if (c == -1) { + syslog(LOG_WARNING, "%s: keyctl_instantiate: %s", + __FUNCTION__, strerror(errno)); + freeaddrinfo(addr); + return 1; + } + + freeaddrinfo(addr); + return 0; +} + +void +usage(const char *prog) +{ + syslog(LOG_WARNING, "Usage: %s [-c] [-v] key_serial", prog); + fprintf(stderr, "Usage: %s [-c] [-v] key_serial\n", prog); +} + +int main(const int argc, char *const argv[]) +{ + struct cifs_spnego_msg *keydata = NULL; + DATA_BLOB secblob = data_blob_null; + DATA_BLOB sess_key = data_blob_null; + secType_t sectype; + key_serial_t key; + size_t datalen; + long rc = 1; + uid_t uid; + int kernel_upcall_version; + int c, use_cifs_service_prefix = 0; + char *buf, *hostname = NULL; + + openlog(prog, 0, LOG_DAEMON); + + while ((c = getopt(argc, argv, "cv")) != -1) { + switch (c) { + case 'c':{ + use_cifs_service_prefix = 1; + break; + } + case 'v':{ + printf("version: %s\n", CIFSSPNEGO_VERSION); + goto out; + } + default:{ + syslog(LOG_WARNING, "unknow option: %c", c); + goto out; + } + } + } + + /* is there a key? */ + if (argc <= optind) { + usage(prog); + goto out; + } + + /* get key and keyring values */ + errno = 0; + key = strtol(argv[optind], NULL, 10); + if (errno != 0) { + syslog(LOG_WARNING, "Invalid key format: %s", strerror(errno)); + goto out; + } + + rc = keyctl_describe_alloc(key, &buf); + if (rc == -1) { + syslog(LOG_WARNING, "keyctl_describe_alloc failed: %s", + strerror(errno)); + rc = 1; + goto out; + } + + if (strncmp(buf, "cifs.resolver", sizeof("cifs.resolver")-1) == 0) { + rc = cifs_resolver(key, buf); + goto out; + } + + rc = decode_key_description(buf, &kernel_upcall_version, §ype, + &hostname, &uid); + if ((rc & DKD_MUSTHAVE_SET) != DKD_MUSTHAVE_SET) { + syslog(LOG_WARNING, + "unable to get from description necessary params"); + rc = 1; + SAFE_FREE(buf); + goto out; + } + SAFE_FREE(buf); + + if (kernel_upcall_version != CIFS_SPNEGO_UPCALL_VERSION) { + syslog(LOG_WARNING, + "incompatible kernel upcall version: 0x%x", + kernel_upcall_version); + rc = 1; + goto out; + } + + if (rc & DKD_HAVE_UID) { + rc = setuid(uid); + if (rc == -1) { + syslog(LOG_WARNING, "setuid: %s", strerror(errno)); + goto out; + } + } + + /* BB: someday upcall SPNEGO blob could be checked here to decide + * what mech to use */ + + // do mech specific authorization + switch (sectype) { + case KRB5:{ + char *princ; + size_t len; + + /* for "cifs/" service name + terminating 0 */ + len = strlen(hostname) + 5 + 1; + princ = SMB_XMALLOC_ARRAY(char, len); + if (!princ) { + rc = 1; + break; + } + if (use_cifs_service_prefix) { + strlcpy(princ, "cifs/", len); + } else { + strlcpy(princ, "host/", len); + } + strlcpy(princ + 5, hostname, len - 5); + + rc = handle_krb5_mech(OID_KERBEROS5, princ, + &secblob, &sess_key); + SAFE_FREE(princ); + break; + } + default:{ + syslog(LOG_WARNING, "sectype: %d is not implemented", + sectype); + rc = 1; + break; + } + } + + 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); + if (!keydata) { + rc = 1; + goto out; + } + keydata->version = CIFS_SPNEGO_UPCALL_VERSION; + keydata->flags = 0; + keydata->sesskey_len = sess_key.length; + keydata->secblob_len = secblob.length; + memcpy(&(keydata->data), sess_key.data, sess_key.length); + memcpy(&(keydata->data) + keydata->sesskey_len, + secblob.data, secblob.length); + + /* setup key */ + rc = keyctl_instantiate(key, keydata, datalen, 0); + if (rc == -1) { + syslog(LOG_WARNING, "keyctl_instantiate: %s", strerror(errno)); + goto out; + } + + /* BB: maybe we need use timeout for key: for example no more then + * ticket lifietime? */ + /* keyctl_set_timeout( key, 60); */ + out: + data_blob_free(&secblob); + data_blob_free(&sess_key); + SAFE_FREE(hostname); + SAFE_FREE(keydata); + return rc; +} diff --git a/source3/configure.in b/source3/configure.in index 2ae5e35295..cb0e37e4a2 100644 --- a/source3/configure.in +++ b/source3/configure.in @@ -109,9 +109,9 @@ AC_SUBST(EXTRA_BIN_PROGS) AC_SUBST(CIFSMOUNT_PROGS) AC_SUBST(INSTALL_CIFSMOUNT) AC_SUBST(UNINSTALL_CIFSMOUNT) -AC_SUBST(CIFSSPNEGO_PROGS) -AC_SUBST(INSTALL_CIFSSPNEGO) -AC_SUBST(UNINSTALL_CIFSSPNEGO) +AC_SUBST(CIFSUPCALL_PROGS) +AC_SUBST(INSTALL_CIFSUPCALL) +AC_SUBST(UNINSTALL_CIFSUPCALL) AC_SUBST(EXTRA_SBIN_PROGS) AC_SUBST(EXTRA_ALL_TARGETS) AC_SUBST(CONFIG_LIBS) @@ -4035,14 +4035,14 @@ AC_ARG_WITH(cifsmount, ) ################################################# -# check for cifs.spnego support +# check for cifs.upcall support AC_CHECK_HEADERS([keyutils.h], [HAVE_KEYUTILS_H=1], [HAVE_KEYUTILS_H=0]) -CIFSSPNEGO_PROGS="" -INSTALL_CIFSSPNEGO="" -UNINSTALL_CIFSSPNEGO="" -AC_MSG_CHECKING(whether to build cifs.spnego) -AC_ARG_WITH(cifsspnego, -[AS_HELP_STRING([--with-cifsspnego], [Include cifs.spnego (Linux only) support (default=no)])], +CIFSUPCALL_PROGS="" +INSTALL_CIFSUPCALL="" +UNINSTALL_CIFSUPCALL="" +AC_MSG_CHECKING(whether to build cifs.upcall) +AC_ARG_WITH(cifsupcall, +[AS_HELP_STRING([--with-cifsupcall], [Include cifs.upcall (Linux only) support (default=no)])], [ case "$withval" in no) AC_MSG_RESULT(no) @@ -4051,15 +4051,15 @@ AC_ARG_WITH(cifsspnego, case "$host_os" in *linux*) if test x"$use_ads" != x"yes"; then - AC_MSG_ERROR(ADS support should be enabled for building cifs.spnego) + AC_MSG_ERROR(ADS support should be enabled for building cifs.upcall) elif test x"$HAVE_KEYUTILS_H" != "x1"; then - AC_MSG_ERROR(keyutils package is required for cifs.spnego) + AC_MSG_ERROR(keyutils package is required for cifs.upcall) else AC_MSG_RESULT(yes) - AC_DEFINE(WITH_CIFSSPNEGO,1,[whether to build cifs.spnego]) - CIFSSPNEGO_PROGS="bin/cifs.spnego" - INSTALL_CIFSSPNEGO="installcifsspnego" - UNINSTALL_CIFSSPNEGO="uninstallcifsspnego" + AC_DEFINE(WITH_CIFSUPCALL,1,[whether to build cifs.upcall]) + CIFSUPCALL_PROGS="bin/cifs.upcall" + INSTALL_CIFSUPCALL="installcifsupcall" + UNINSTALL_CIFSUPCALL="uninstallcifsupcall" fi ;; *) -- cgit