linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCHv2 0/11] staging: usbip: Userland crypto and ACLs
@ 2013-09-13  9:55 Dominik Paulus
  2013-09-13  9:55 ` [PATCHv2 01/11] staging: usbip: Fix IPv6 support in usbipd Dominik Paulus
                   ` (11 more replies)
  0 siblings, 12 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-13  9:55 UTC (permalink / raw)
  To: linux-kernel
  Cc: Greg Kroah-Hartman, Dominik Paulus, Ilija Hadzic, Anthony Foiani,
	Masanari Iida, linux-kernel, devel

Hi,

this patch series includes an updated version of the IPv6 support patch (a call
to freeaddrinfo() was missing) as well as:

 - The client/server authentication support using GnuTLS Tobias already
   announced on the usbip-devel mailing list some time ago[1]

 - Support for restricting the access to devices to specific IP address ranges

 - Improved error reporting and new error codes to be passed over the TCP
   protocol.

We think that the added features justify a version bump to 1.2.0. The
corresponding patch is also included. All protocol changes are
backwards-compatible, thus, we don't increment the protocol version.

We've already sent this patch series, but forgot to specify a subject line.
linux-usb apparently received it[2], the LKML didn't. Sorry if you received it
twice now.

Regards,
	Tobias Polzer and Dominik Paulus

[1] See <6aeb926e1c4572e79488a91a827333c9.squirrel@faumail.uni-erlangen.de>
[2] http://www.mail-archive.com/linux-usb@vger.kernel.org/msg28689.html


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCHv2 01/11] staging: usbip: Fix IPv6 support in usbipd
  2013-09-13  9:55 [PATCHv2 0/11] staging: usbip: Userland crypto and ACLs Dominik Paulus
@ 2013-09-13  9:55 ` Dominik Paulus
  2013-09-13  9:55 ` [PATCHv2 02/11] staging: usbip: Add support for client authentication Dominik Paulus
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-13  9:55 UTC (permalink / raw)
  To: linux-kernel
  Cc: Dominik Paulus, Tobias Polzer, Greg Kroah-Hartman, Ilija Hadzic,
	Anthony Foiani, Masanari Iida, linux-kernel, devel

getaddrinfo() leaves the order of the returned addrinfo structs
unspecified. On systems with bindv6only disabled (this is the default),
PF_INET6 sockets bind to IPv4, too. Thus, IPv6 support in usbipd was
broken when getaddrinfo returned first IPv4 and then IPv6 addrinfos, as
the IPv6 bind failed with EADDRINUSE.

This patch uses seperate sockets for IPv4 and IPv6 and sets IPV6_V6ONLY
on all IPv6 sockets. Two command line arguments, -4 and -6 were added to
manually select the socket family.

Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
---
 .../staging/usbip/userspace/src/usbip_network.c    | 12 ++++
 .../staging/usbip/userspace/src/usbip_network.h    |  1 +
 drivers/staging/usbip/userspace/src/usbipd.c       | 69 ++++++++++++++++------
 3 files changed, 64 insertions(+), 18 deletions(-)

diff --git a/drivers/staging/usbip/userspace/src/usbip_network.c b/drivers/staging/usbip/userspace/src/usbip_network.c
index c39a07f..e78279c 100644
--- a/drivers/staging/usbip/userspace/src/usbip_network.c
+++ b/drivers/staging/usbip/userspace/src/usbip_network.c
@@ -239,6 +239,18 @@ int usbip_net_set_keepalive(int sockfd)
 	return ret;
 }
 
+int usbip_net_set_v6only(int sockfd)
+{
+	const int val = 1;
+	int ret;
+
+	ret = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
+	if (ret < 0)
+		dbg("setsockopt: IPV6_V6ONLY");
+
+	return ret;
+}
+
 /*
  * IPv6 Ready
  */
diff --git a/drivers/staging/usbip/userspace/src/usbip_network.h b/drivers/staging/usbip/userspace/src/usbip_network.h
index 2d0e427..f19ae19 100644
--- a/drivers/staging/usbip/userspace/src/usbip_network.h
+++ b/drivers/staging/usbip/userspace/src/usbip_network.h
@@ -180,6 +180,7 @@ int usbip_net_recv_op_common(int sockfd, uint16_t *code);
 int usbip_net_set_reuseaddr(int sockfd);
 int usbip_net_set_nodelay(int sockfd);
 int usbip_net_set_keepalive(int sockfd);
+int usbip_net_set_v6only(int sockfd);
 int usbip_net_tcp_connect(char *hostname, char *port);
 
 #endif /* __USBIP_NETWORK_H */
diff --git a/drivers/staging/usbip/userspace/src/usbipd.c b/drivers/staging/usbip/userspace/src/usbipd.c
index 1c76cfd..7980f8b 100644
--- a/drivers/staging/usbip/userspace/src/usbipd.c
+++ b/drivers/staging/usbip/userspace/src/usbipd.c
@@ -56,6 +56,13 @@ static const char usbip_version_string[] = PACKAGE_STRING;
 
 static const char usbipd_help_string[] =
 	"usage: usbipd [options]\n"
+	"\n"
+	"	-4, --ipv4\n"
+	"		Bind to IPv4. Default is both.\n"
+	"\n"
+	"	-6, --ipv6\n"
+	"		Bind to IPv6. Default is both.\n"
+	"\n"
 	"	-D, --daemon\n"
 	"		Run as a daemon process.\n"
 	"\n"
@@ -354,14 +361,15 @@ static void addrinfo_to_text(struct addrinfo *ai, char buf[],
 	snprintf(buf, buf_size, "%s:%s", hbuf, sbuf);
 }
 
-static int listen_all_addrinfo(struct addrinfo *ai_head, int sockfdlist[])
+static int listen_all_addrinfo(struct addrinfo *ai_head, int sockfdlist[],
+			     int maxsockfd)
 {
 	struct addrinfo *ai;
 	int ret, nsockfd = 0;
 	const size_t ai_buf_size = NI_MAXHOST + NI_MAXSERV + 2;
 	char ai_buf[ai_buf_size];
 
-	for (ai = ai_head; ai && nsockfd < MAXSOCKFD; ai = ai->ai_next) {
+	for (ai = ai_head; ai && nsockfd < maxsockfd; ai = ai->ai_next) {
 		int sock;
 		addrinfo_to_text(ai, ai_buf, ai_buf_size);
 		dbg("opening %s", ai_buf);
@@ -374,6 +382,9 @@ static int listen_all_addrinfo(struct addrinfo *ai_head, int sockfdlist[])
 
 		usbip_net_set_reuseaddr(sock);
 		usbip_net_set_nodelay(sock);
+		/* We use seperate sockets for IPv4 and IPv6
+		 * (see do_standalone_mode()) */
+		usbip_net_set_v6only(sock);
 
 		if (sock >= FD_SETSIZE) {
 			err("FD_SETSIZE: %s: sock=%d, max=%d",
@@ -402,11 +413,6 @@ static int listen_all_addrinfo(struct addrinfo *ai_head, int sockfdlist[])
 		sockfdlist[nsockfd++] = sock;
 	}
 
-	if (nsockfd == 0)
-		return -1;
-
-	dbg("listening on %d address%s", nsockfd, (nsockfd == 1) ? "" : "es");
-
 	return nsockfd;
 }
 
@@ -473,11 +479,11 @@ static void remove_pid_file()
 	}
 }
 
-static int do_standalone_mode(int daemonize)
+static int do_standalone_mode(int daemonize, int ipv4, int ipv6)
 {
 	struct addrinfo *ai_head;
 	int sockfdlist[MAXSOCKFD];
-	int nsockfd;
+	int nsockfd, family;
 	int i, terminate;
 	struct pollfd *fds;
 	struct timespec timeout;
@@ -501,21 +507,36 @@ static int do_standalone_mode(int daemonize)
 	set_signal();
 	write_pid_file();
 
-	ai_head = do_getaddrinfo(NULL, PF_UNSPEC);
+	info("starting " PROGNAME " (%s)", usbip_version_string);
+
+	/*
+	 * To suppress warnings on systems with bindv6only disabled
+	 * (default), we use seperate sockets for IPv6 and IPv4 and set
+	 * IPV6_V6ONLY on the IPv6 sockets.
+	 */
+	if (ipv4 && ipv6)
+		family = AF_UNSPEC;
+	else if (ipv4)
+		family = AF_INET;
+	else
+		family = AF_INET6;
+
+	ai_head = do_getaddrinfo(NULL, family);
 	if (!ai_head) {
 		usbip_host_driver_close();
 		return -1;
 	}
-
-	info("starting " PROGNAME " (%s)", usbip_version_string);
-
-	nsockfd = listen_all_addrinfo(ai_head, sockfdlist);
+	nsockfd = listen_all_addrinfo(ai_head, sockfdlist,
+		sizeof(sockfdlist) / sizeof(*sockfdlist));
+	freeaddrinfo(ai_head);
 	if (nsockfd <= 0) {
 		err("failed to open a listening socket");
-		freeaddrinfo(ai_head);
 		usbip_host_driver_close();
 		return -1;
 	}
+
+	dbg("listening on %d address%s", nsockfd, (nsockfd == 1) ? "" : "es");
+
 	fds = calloc(nsockfd, sizeof(struct pollfd));
 	for (i = 0; i < nsockfd; i++) {
 		fds[i].fd = sockfdlist[i];
@@ -551,7 +572,6 @@ static int do_standalone_mode(int daemonize)
 
 	info("shutting down " PROGNAME);
 	free(fds);
-	freeaddrinfo(ai_head);
 	usbip_host_driver_close();
 
 	return 0;
@@ -560,6 +580,9 @@ static int do_standalone_mode(int daemonize)
 int main(int argc, char *argv[])
 {
 	static const struct option longopts[] = {
+		{ "ipv4",     no_argument,       NULL, '4' },
+		{ "ipv6",     no_argument,       NULL, '6' },
+		{ "daemon",   no_argument,       NULL, 'D' },
 		{ "daemon",   no_argument,       NULL, 'D' },
 		{ "debug",    no_argument,       NULL, 'd' },
 		{ "pid",      optional_argument, NULL, 'P' },
@@ -576,6 +599,7 @@ int main(int argc, char *argv[])
 	} cmd;
 
 	int daemonize = 0;
+	int ipv4 = 0, ipv6 = 0;
 	int opt, rc = -1;
 	pid_file = NULL;
 
@@ -587,12 +611,18 @@ int main(int argc, char *argv[])
 
 	cmd = cmd_standalone_mode;
 	for (;;) {
-		opt = getopt_long(argc, argv, "DdP::t:hv", longopts, NULL);
+		opt = getopt_long(argc, argv, "46DdP::t:hv", longopts, NULL);
 
 		if (opt == -1)
 			break;
 
 		switch (opt) {
+		case '4':
+			ipv4 = 1;
+			break;
+		case '6':
+			ipv6 = 1;
+			break;
 		case 'D':
 			daemonize = 1;
 			break;
@@ -618,9 +648,12 @@ int main(int argc, char *argv[])
 		}
 	}
 
+	if (!ipv4 && !ipv6)
+		ipv4 = ipv6 = 1;
+
 	switch (cmd) {
 	case cmd_standalone_mode:
-		rc = do_standalone_mode(daemonize);
+		rc = do_standalone_mode(daemonize, ipv4, ipv6);
 		remove_pid_file();
 		break;
 	case cmd_version:
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv2 02/11] staging: usbip: Add support for client authentication
  2013-09-13  9:55 [PATCHv2 0/11] staging: usbip: Userland crypto and ACLs Dominik Paulus
  2013-09-13  9:55 ` [PATCHv2 01/11] staging: usbip: Fix IPv6 support in usbipd Dominik Paulus
@ 2013-09-13  9:55 ` Dominik Paulus
  2013-09-13  9:55 ` [PATCHv2 03/11] staging: usbip: Add kernel support for client ACLs Dominik Paulus
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-13  9:55 UTC (permalink / raw)
  To: linux-kernel
  Cc: Dominik Paulus, Tobias Polzer, Greg Kroah-Hartman, Ilija Hadzic,
	Anthony Foiani, Masanari Iida, linux-kernel, devel

This patch adds support for authenticating both client and server using
a pre-shared passphrase using SRP (Secure Remote Password) over TLS (see
RFC 5054) using GnuTLS. Both usbip and usbipd now accept a shared secret
as a command line argument. Currently, the established TLS connection is
only used to perform a secure handshake and dropped before the socket is
passed to the kernel. The code may be extended to exchange a session key
over TLS and pass it to the kernel to perform IPsec.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/configure.ac       |  14 ++
 drivers/staging/usbip/userspace/doc/usbip.8        |   6 +
 drivers/staging/usbip/userspace/doc/usbipd.8       |   6 +
 drivers/staging/usbip/userspace/src/usbip.c        |  30 ++-
 drivers/staging/usbip/userspace/src/usbip_attach.c |   2 +-
 drivers/staging/usbip/userspace/src/usbip_list.c   |   2 +-
 .../staging/usbip/userspace/src/usbip_network.c    |  82 ++++++++
 .../staging/usbip/userspace/src/usbip_network.h    |   9 +-
 drivers/staging/usbip/userspace/src/usbipd.c       | 217 ++++++++++++++++++---
 9 files changed, 335 insertions(+), 33 deletions(-)

diff --git a/drivers/staging/usbip/userspace/configure.ac b/drivers/staging/usbip/userspace/configure.ac
index 2be4060..7bba496 100644
--- a/drivers/staging/usbip/userspace/configure.ac
+++ b/drivers/staging/usbip/userspace/configure.ac
@@ -84,6 +84,20 @@ AC_ARG_WITH([tcp-wrappers],
 		AC_DEFINE([HAVE_LIBWRAP], [1], [use tcp wrapper])],
 	       [AC_MSG_RESULT([no]); LIBS="$saved_LIBS"])])
 
+# Checks for the GnuTLS library
+AC_ARG_WITH([gnutls],
+			[AS_HELP_STRING([--with-gnutls],
+							[use the GnuTLS library for authentication])],
+			dnl [ACTION-IF-GIVEN]
+			[if test "$withval" = "yes"; then
+			 PKG_CHECK_MODULES([GNUTLS], [gnutls])
+			 AC_DEFINE([HAVE_GNUTLS], [1], [use gnutls])
+			 CFLAGS="$CFLAGS $GNUTLS_CFLAGS"
+			 LDFLAGS="$LDFLAGS $GNUTLS_LIBS"
+			 fi
+			],
+			)
+
 # Sets directory containing usb.ids.
 AC_ARG_WITH([usbids-dir],
 	    [AS_HELP_STRING([--with-usbids-dir=DIR],
diff --git a/drivers/staging/usbip/userspace/doc/usbip.8 b/drivers/staging/usbip/userspace/doc/usbip.8
index a6097be..b5050ed 100644
--- a/drivers/staging/usbip/userspace/doc/usbip.8
+++ b/drivers/staging/usbip/userspace/doc/usbip.8
@@ -29,6 +29,12 @@ Log to syslog.
 Connect to PORT on remote host (used for attach and list --remote).
 .PP
 
+.HP
+\fB\-\-auth\fR
+.IP
+Set the password to be used for client authentication. See usbipd(8) for more information.
+.PP
+
 .SH COMMANDS
 .HP
 \fBversion\fR
diff --git a/drivers/staging/usbip/userspace/doc/usbipd.8 b/drivers/staging/usbip/userspace/doc/usbipd.8
index ac4635d..b2b9eee 100644
--- a/drivers/staging/usbip/userspace/doc/usbipd.8
+++ b/drivers/staging/usbip/userspace/doc/usbipd.8
@@ -54,6 +54,12 @@ If no FILE specified, use /var/run/usbipd.pid
 Listen on TCP/IP port PORT.
 .PP
 
+.HP
+\fB\-s\fR, \fB\-\-auth\fR
+.IP
+Sets the password to be used for client authentication. If -a is used, the server will only accept connections from authenticated clients. Note: USB traffic will still be unencrypted, this currently only serves for authentication.
+.PP
+
 \fB\-h\fR, \fB\-\-help\fR
 .IP
 Print the program help message and exit.
diff --git a/drivers/staging/usbip/userspace/src/usbip.c b/drivers/staging/usbip/userspace/src/usbip.c
index 04a5f20..8a5de83 100644
--- a/drivers/staging/usbip/userspace/src/usbip.c
+++ b/drivers/staging/usbip/userspace/src/usbip.c
@@ -25,6 +25,12 @@
 #include <getopt.h>
 #include <syslog.h>
 
+#include "../config.h"
+
+#ifdef HAVE_GNUTLS
+#include <gnutls/gnutls.h>
+#endif
+
 #include "usbip_common.h"
 #include "usbip_network.h"
 #include "usbip.h"
@@ -35,8 +41,12 @@ static int usbip_version(int argc, char *argv[]);
 static const char usbip_version_string[] = PACKAGE_STRING;
 
 static const char usbip_usage_string[] =
-	"usbip [--debug] [--log] [--tcp-port PORT] [version]\n"
-	"             [help] <command> <args>\n";
+	"usbip "
+#ifdef HAVE_GNUTLS
+	"[--auth PASSWORD] "
+#endif
+	"[--debug] [--log] [--tcp-port PORT]\n"
+	"             [version] [help] <command> <args>\n";
 
 static void usbip_usage(void)
 {
@@ -142,6 +152,7 @@ int main(int argc, char *argv[])
 		{ "debug",    no_argument,       NULL, 'd' },
 		{ "log",      no_argument,       NULL, 'l' },
 		{ "tcp-port", required_argument, NULL, 't' },
+		{ "auth",     required_argument, NULL, 's' },
 		{ NULL,       0,                 NULL,  0  }
 	};
 
@@ -152,12 +163,25 @@ int main(int argc, char *argv[])
 	usbip_use_stderr = 1;
 	opterr = 0;
 	for (;;) {
-		opt = getopt_long(argc, argv, "+dlt:", opts, NULL);
+		opt = getopt_long(argc, argv, "+dls:t:", opts, NULL);
 
 		if (opt == -1)
 			break;
 
 		switch (opt) {
+		case 's':
+#ifdef HAVE_GNUTLS
+			usbip_srp_password = optarg;
+			rc = gnutls_global_init();
+			if (rc < 0) {
+				err("Unable to initialize GnuTLS library: %s",
+					gnutls_strerror(rc));
+				return EXIT_FAILURE;
+			}
+#else
+			err("usbip has been compiled without GnuTLS support");
+#endif
+			break;
 		case 'd':
 			usbip_use_debug = 1;
 			break;
diff --git a/drivers/staging/usbip/userspace/src/usbip_attach.c b/drivers/staging/usbip/userspace/src/usbip_attach.c
index 0858411..2363e56 100644
--- a/drivers/staging/usbip/userspace/src/usbip_attach.c
+++ b/drivers/staging/usbip/userspace/src/usbip_attach.c
@@ -175,7 +175,7 @@ static int attach_device(char *host, char *busid)
 	int rc;
 	int rhport;
 
-	sockfd = usbip_net_tcp_connect(host, usbip_port_string);
+	sockfd = usbip_net_connect(host);
 	if (sockfd < 0) {
 		err("tcp connect");
 		return -1;
diff --git a/drivers/staging/usbip/userspace/src/usbip_list.c b/drivers/staging/usbip/userspace/src/usbip_list.c
index 237e099..e4fa5b8 100644
--- a/drivers/staging/usbip/userspace/src/usbip_list.c
+++ b/drivers/staging/usbip/userspace/src/usbip_list.c
@@ -131,7 +131,7 @@ static int list_exported_devices(char *host)
 	int rc;
 	int sockfd;
 
-	sockfd = usbip_net_tcp_connect(host, usbip_port_string);
+	sockfd = usbip_net_connect(host);
 	if (sockfd < 0) {
 		err("could not connect to %s:%s: %s", host,
 		    usbip_port_string, gai_strerror(sockfd));
diff --git a/drivers/staging/usbip/userspace/src/usbip_network.c b/drivers/staging/usbip/userspace/src/usbip_network.c
index e78279c..eda641f 100644
--- a/drivers/staging/usbip/userspace/src/usbip_network.c
+++ b/drivers/staging/usbip/userspace/src/usbip_network.c
@@ -25,11 +25,20 @@
 #include <netinet/tcp.h>
 #include <unistd.h>
 
+#include "../config.h"
+
+#ifdef HAVE_GNUTLS
+#include <gnutls/gnutls.h>
+#endif
+
 #include "usbip_common.h"
 #include "usbip_network.h"
 
 int usbip_port = 3240;
 char *usbip_port_string = "3240";
+#ifdef HAVE_GNUTLS
+char *usbip_srp_password;
+#endif
 
 void usbip_setup_port_number(char *arg)
 {
@@ -297,3 +306,76 @@ int usbip_net_tcp_connect(char *hostname, char *service)
 
 	return sockfd;
 }
+
+#ifdef HAVE_GNUTLS
+int usbip_net_srp_handshake(int sockfd)
+{
+	int ret;
+	gnutls_session_t session;
+	gnutls_srp_client_credentials_t srp_cred;
+
+	ret = gnutls_srp_allocate_client_credentials(&srp_cred);
+	if (ret < 0)
+		return ret;
+
+	gnutls_srp_set_client_credentials(srp_cred, "dummyuser",
+		usbip_srp_password);
+
+	ret = gnutls_init(&session, GNUTLS_CLIENT);
+	if (ret < 0) {
+		gnutls_srp_free_client_credentials(srp_cred);
+		return ret;
+	}
+
+	gnutls_priority_set_direct(session, "NORMAL:+SRP", NULL);
+
+	gnutls_credentials_set(session, GNUTLS_CRD_SRP, srp_cred);
+	gnutls_transport_set_int (session, sockfd);
+
+	do {
+		ret = gnutls_handshake(session);
+	} while (ret < 0 && !gnutls_error_is_fatal(ret));
+
+	gnutls_bye(session, GNUTLS_SHUT_RDWR);
+
+	gnutls_deinit(session);
+	gnutls_srp_free_client_credentials(srp_cred);
+
+	return ret;
+}
+#endif
+
+/*
+ * Connect to the server. Performs the TCP connection attempt
+ * and - if necessary - the TLS handshake used for authentication.
+ */
+int usbip_net_connect(char *hostname)
+{
+	int sockfd;
+
+	sockfd = usbip_net_tcp_connect(hostname, usbip_port_string);
+	if (sockfd < 0)
+		return sockfd;
+
+#ifdef HAVE_GNUTLS
+	if (usbip_srp_password) {
+		int rc;
+
+		rc = usbip_net_send_op_common(sockfd, OP_REQ_STARTTLS, 0);
+		if (rc < 0) {
+			err("usbip_net_send_op_common failed");
+			return EAI_SYSTEM;
+		}
+
+		rc = usbip_net_srp_handshake(sockfd);
+		if (rc < 0) {
+			err("Unable to perform TLS handshake (wrong password?): %s",
+				gnutls_strerror(rc));
+			close(sockfd);
+			return EAI_SYSTEM;
+		}
+	}
+#endif
+
+	return sockfd;
+}
diff --git a/drivers/staging/usbip/userspace/src/usbip_network.h b/drivers/staging/usbip/userspace/src/usbip_network.h
index f19ae19..758656b 100644
--- a/drivers/staging/usbip/userspace/src/usbip_network.h
+++ b/drivers/staging/usbip/userspace/src/usbip_network.h
@@ -16,6 +16,7 @@
 
 extern int usbip_port;
 extern char *usbip_port_string;
+extern char *usbip_srp_password;
 void usbip_setup_port_number(char *arg);
 
 /* ---------------------------------------------------------------------- */
@@ -168,6 +169,12 @@ struct op_devlist_reply_extra {
 	usbip_net_pack_uint32_t(pack, &(reply)->ndev);\
 } while (0)
 
+/* ---------------------------------------------------------------------- */
+/* Initiate encrypted connection. */
+#define OP_STARTTLS 0x06
+#define OP_REQ_STARTTLS   (OP_REQUEST | OP_STARTTLS)
+#define OP_REP_STARTTLS   (OP_REPLY   | OP_STARTTLS)
+
 void usbip_net_pack_uint32_t(int pack, uint32_t *num);
 void usbip_net_pack_uint16_t(int pack, uint16_t *num);
 void usbip_net_pack_usb_device(int pack, struct usbip_usb_device *udev);
@@ -181,6 +188,6 @@ int usbip_net_set_reuseaddr(int sockfd);
 int usbip_net_set_nodelay(int sockfd);
 int usbip_net_set_keepalive(int sockfd);
 int usbip_net_set_v6only(int sockfd);
-int usbip_net_tcp_connect(char *hostname, char *port);
+int usbip_net_connect(char *hostname);
 
 #endif /* __USBIP_NETWORK_H */
diff --git a/drivers/staging/usbip/userspace/src/usbipd.c b/drivers/staging/usbip/userspace/src/usbipd.c
index 7980f8b..8db2f27 100644
--- a/drivers/staging/usbip/userspace/src/usbipd.c
+++ b/drivers/staging/usbip/userspace/src/usbipd.c
@@ -32,6 +32,11 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 
+#ifdef HAVE_GNUTLS
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#endif
+
 #ifdef HAVE_LIBWRAP
 #include <tcpd.h>
 #endif
@@ -63,6 +68,11 @@ static const char usbipd_help_string[] =
 	"	-6, --ipv6\n"
 	"		Bind to IPv6. Default is both.\n"
 	"\n"
+#ifdef HAVE_GNUTLS
+	"	-sPASSWORD, --auth PASSWORD\n"
+	"		Set PASSWORD as key used for authentication.\n"
+	"\n"
+#endif
 	"	-D, --daemon\n"
 	"		Run as a daemon process.\n"
 	"\n"
@@ -82,6 +92,78 @@ static const char usbipd_help_string[] =
 	"	-v, --version\n"
 	"		Show version.\n";
 
+static int need_auth;
+#ifdef HAVE_GNUTLS
+static gnutls_datum_t srp_salt, srp_verifier;
+static gnutls_srp_server_credentials_t srp_cred;
+
+#define SRP_GROUP gnutls_srp_2048_group_generator
+#define SRP_PRIME gnutls_srp_2048_group_prime
+
+static int net_srp_callback(gnutls_session_t sess, const char *username,
+	gnutls_datum_t *nsalt, gnutls_datum_t *nverifier, gnutls_datum_t *g,
+	gnutls_datum_t *n)
+{
+	/*
+	 * GnuTLS expects us to allocate all data returned from callbacks
+	 * using gnutls_malloc(), thus, we have to create a fresh copy of
+	 * our static credentials for every connection.
+	 */
+	nsalt->data = gnutls_malloc(srp_salt.size);
+	nverifier->data = gnutls_malloc(srp_verifier.size);
+	if (nsalt->data == NULL || nverifier->data == NULL) {
+		gnutls_free(nsalt->data);
+		gnutls_free(nverifier->data);
+		return -1;
+	}
+	nsalt->size = srp_salt.size;
+	nverifier->size = srp_verifier.size;
+	memcpy(nverifier->data, srp_verifier.data, srp_verifier.size);
+	memcpy(nsalt->data, srp_salt.data, srp_salt.size);
+
+	*g = SRP_GROUP;
+	*n = SRP_PRIME;
+
+	/* We only have a single session, thus, ignore it */
+	(void) sess;
+
+	if (strcmp(username, "dummyuser"))
+		/* User invalid, stored dummy data in g and n. */
+		return 1;
+
+	return 0;
+}
+
+static int net_srp_server_handshake(int connfd)
+{
+	int ret;
+	gnutls_session_t session;
+
+	if (gnutls_init(&session, GNUTLS_SERVER) != 0)
+		return -1;
+	gnutls_priority_set_direct(session, "NORMAL:-KX-ALL:+SRP", NULL);
+	if (gnutls_credentials_set(session, GNUTLS_CRD_SRP, srp_cred) != 0)
+		return -1;
+
+	gnutls_transport_set_int(session, connfd);
+
+	do {
+		ret = gnutls_handshake(session);
+	} while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+	if (ret < 0)
+		err("GnuTLS handshake failed (%s)", gnutls_strerror(ret));
+	else
+		info("GnuTLS handshake completed");
+
+	if (gnutls_bye(session, GNUTLS_SHUT_RDWR) != 0)
+		err("Unable to shutdown TLS connection.");
+	gnutls_deinit(session);
+
+	return ret;
+}
+#endif
+
 static void usbipd_help(void)
 {
 	printf("%s\n", usbipd_help_string);
@@ -238,14 +320,7 @@ static int recv_request_devlist(int connfd)
 
 static int recv_pdu(int connfd)
 {
-	uint16_t code = OP_UNSPEC;
-	int ret;
-
-	ret = usbip_net_recv_op_common(connfd, &code);
-	if (ret < 0) {
-		dbg("could not receive opcode: %#0x", code);
-		return -1;
-	}
+	int auth = !need_auth, cont = 1, ret;
 
 	ret = usbip_host_refresh_device_list();
 	if (ret < 0) {
@@ -253,25 +328,64 @@ static int recv_pdu(int connfd)
 		return -1;
 	}
 
-	info("received request: %#0x(%d)", code, connfd);
-	switch (code) {
-	case OP_REQ_DEVLIST:
-		ret = recv_request_devlist(connfd);
-		break;
-	case OP_REQ_IMPORT:
-		ret = recv_request_import(connfd);
-		break;
-	case OP_REQ_DEVINFO:
-	case OP_REQ_CRYPKEY:
-	default:
-		err("received an unknown opcode: %#0x", code);
-		ret = -1;
-	}
+	/*
+	 * Process opcodes. We might receive more than one, as the
+	 * client might send STARTTLS first
+	 */
+	while (cont) {
+		uint16_t code = OP_UNSPEC;
 
-	if (ret == 0)
-		info("request %#0x(%d): complete", code, connfd);
-	else
-		info("request %#0x(%d): failed", code, connfd);
+		ret = usbip_net_recv_op_common(connfd, &code);
+		if (ret < 0) {
+			dbg("could not receive opcode: %#0x", code);
+			return -1;
+		}
+
+		info("received request: %#0x(%d)", code, connfd);
+
+		/* We require an authenticated encryption */
+		if (!auth && code != OP_REQ_STARTTLS) {
+			usbip_net_send_op_common(connfd, OP_REPLY, ST_NA);
+			return -1;
+		}
+
+		switch (code) {
+#ifdef HAVE_GNUTLS
+		case OP_REQ_STARTTLS:
+			if (!need_auth) {
+				ret = -1;
+				err("Unexpected TLS handshake attempt (client "
+					"uses password, server doesn't)");
+			} else {
+				ret = net_srp_server_handshake(connfd);
+				if (ret != 0)
+					err("TLS handshake failed");
+				auth = 1;
+			}
+			break;
+#endif
+		case OP_REQ_DEVLIST:
+			ret = recv_request_devlist(connfd);
+			cont = 0;
+			break;
+		case OP_REQ_IMPORT:
+			ret = recv_request_import(connfd);
+			cont = 0;
+			break;
+		case OP_REQ_DEVINFO:
+		case OP_REQ_CRYPKEY:
+		default:
+			err("received an unknown opcode: %#0x", code);
+			ret = -1;
+		}
+
+		if (ret == 0)
+			info("request %#0x(%d): complete", code, connfd);
+		else {
+			info("request %#0x(%d): failed", code, connfd);
+			break;
+		}
+	}
 
 	return ret;
 }
@@ -577,6 +691,37 @@ static int do_standalone_mode(int daemonize, int ipv4, int ipv6)
 	return 0;
 }
 
+#ifdef HAVE_GNUTLS
+static int usbip_init_gnutls(void)
+{
+	int ret;
+
+	gnutls_global_init();
+
+	srp_salt.data = gnutls_malloc(16);
+	if (!srp_salt.data)
+		return GNUTLS_E_MEMORY_ERROR;
+
+	ret = gnutls_rnd(GNUTLS_RND_NONCE, srp_salt.data, 16);
+	if (ret < 0)
+		return ret;
+	srp_salt.size = 16;
+
+	ret = gnutls_srp_allocate_server_credentials(&srp_cred);
+	if (ret < 0)
+		return ret;
+
+	ret = gnutls_srp_verifier("dummyuser", optarg, &srp_salt, &SRP_GROUP,
+		&SRP_PRIME, &srp_verifier);
+	if (ret < 0)
+		return ret;
+
+	gnutls_srp_set_server_credentials_function(srp_cred, net_srp_callback);
+
+	return GNUTLS_E_SUCCESS;
+}
+#endif
+
 int main(int argc, char *argv[])
 {
 	static const struct option longopts[] = {
@@ -589,6 +734,7 @@ int main(int argc, char *argv[])
 		{ "tcp-port", required_argument, NULL, 't' },
 		{ "help",     no_argument,       NULL, 'h' },
 		{ "version",  no_argument,       NULL, 'v' },
+		{ "auth",     required_argument, NULL, 's' },
 		{ NULL,	      0,                 NULL,  0  }
 	};
 
@@ -601,6 +747,9 @@ int main(int argc, char *argv[])
 	int daemonize = 0;
 	int ipv4 = 0, ipv6 = 0;
 	int opt, rc = -1;
+#ifdef HAVE_GNUTLS
+	int ret;
+#endif
 	pid_file = NULL;
 
 	usbip_use_stderr = 1;
@@ -611,7 +760,7 @@ int main(int argc, char *argv[])
 
 	cmd = cmd_standalone_mode;
 	for (;;) {
-		opt = getopt_long(argc, argv, "46DdP::t:hv", longopts, NULL);
+		opt = getopt_long(argc, argv, "46s:DdP::t:hv", longopts, NULL);
 
 		if (opt == -1)
 			break;
@@ -623,6 +772,20 @@ int main(int argc, char *argv[])
 		case '6':
 			ipv6 = 1;
 			break;
+		case 's':
+#ifdef HAVE_GNUTLS
+			need_auth = 1;
+			ret = usbip_init_gnutls();
+			if (ret < 0) {
+				err("Unable to initialize GnuTLS: %s",
+					gnutls_strerror(ret));
+				return EXIT_FAILURE;
+			}
+			break;
+#else
+			err("usbipd has been compiled without GnuTLS support");
+			break;
+#endif
 		case 'D':
 			daemonize = 1;
 			break;
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv2 03/11] staging: usbip: Add kernel support for client ACLs
  2013-09-13  9:55 [PATCHv2 0/11] staging: usbip: Userland crypto and ACLs Dominik Paulus
  2013-09-13  9:55 ` [PATCHv2 01/11] staging: usbip: Fix IPv6 support in usbipd Dominik Paulus
  2013-09-13  9:55 ` [PATCHv2 02/11] staging: usbip: Add support for client authentication Dominik Paulus
@ 2013-09-13  9:55 ` Dominik Paulus
  2013-09-13  9:55 ` [PATCHv2 04/11] staging: usbip: Add CIDR matching helper functions Dominik Paulus
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-13  9:55 UTC (permalink / raw)
  To: linux-kernel
  Cc: Dominik Paulus, Kurt Kanzenbach, Tobias Polzer,
	Greg Kroah-Hartman, Ilija Hadzic, Anthony Foiani, Masanari Iida,
	linux-kernel, devel

This patch adds the possibility to stored ACLs for allowed clients for
each stub device in sysfs. It adds a new sysfs entry called "usbip_acl"
for each stub device, containing a list of CIDR masks of allowed
clients. This file will be used by usbip and usbipd to store the ACL.

Signed-off-by: Kurt Kanzenbach <ly80toro@cip.cs.fau.de>
Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/stub.h     |  5 +++
 drivers/staging/usbip/stub_dev.c | 68 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 72 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/usbip/stub.h b/drivers/staging/usbip/stub.h
index a73e437..cfe75d1 100644
--- a/drivers/staging/usbip/stub.h
+++ b/drivers/staging/usbip/stub.h
@@ -60,6 +60,11 @@ struct stub_device {
 	struct list_head unlink_free;
 
 	wait_queue_head_t tx_waitq;
+
+	/* list of allowed IP addrs */
+	char *acls;
+	/* for locking list operations */
+	spinlock_t ip_lock;
 };
 
 /* private data into urb->priv */
diff --git a/drivers/staging/usbip/stub_dev.c b/drivers/staging/usbip/stub_dev.c
index d8957a5..c44d5f2 100644
--- a/drivers/staging/usbip/stub_dev.c
+++ b/drivers/staging/usbip/stub_dev.c
@@ -142,6 +142,62 @@ err:
 }
 static DEVICE_ATTR(usbip_sockfd, S_IWUSR, NULL, store_sockfd);
 
+/*
+ * This function replaces the current ACL list
+ */
+static ssize_t store_acl(struct device *dev, struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct stub_device *sdev = dev_get_drvdata(dev);
+	int retval = 0;
+
+	if (!sdev)
+		return -ENODEV;
+
+	if (count >= PAGE_SIZE)
+		/* Prevent storing oversized ACLs in kernel memory */
+		return -EINVAL;
+
+	/* Store ACL */
+	spin_lock_irq(&sdev->ip_lock);
+	kfree(sdev->acls);
+	sdev->acls = kstrdup(buf, GFP_KERNEL);
+	if (IS_ERR(sdev->acls)) {
+		retval = PTR_ERR(sdev->acls);
+		sdev->acls = NULL;
+	} else {
+		retval = strlen(sdev->acls);
+	}
+	spin_unlock_irq(&sdev->ip_lock);
+
+	return retval;
+}
+
+/*
+ * This functions prints all allowed IP addrs for this dev
+ */
+static ssize_t show_acl(struct device *dev, struct device_attribute *attr,
+		       char *buf)
+{
+	struct stub_device *sdev = dev_get_drvdata(dev);
+	int retval = 0;
+
+	if (!sdev)
+		return -ENODEV;
+
+	spin_lock_irq(&sdev->ip_lock);
+	if (sdev->acls == NULL) {
+		retval = 0;
+	} else {
+		strcpy(buf, sdev->acls);
+		retval = strlen(buf);
+	}
+	spin_unlock_irq(&sdev->ip_lock);
+
+	return retval;
+}
+static DEVICE_ATTR(usbip_acl, S_IWUSR | S_IRUGO, show_acl, store_acl);
+
 static int stub_add_files(struct device *dev)
 {
 	int err = 0;
@@ -157,9 +213,13 @@ static int stub_add_files(struct device *dev)
 	err = device_create_file(dev, &dev_attr_usbip_debug);
 	if (err)
 		goto err_debug;
+	err = device_create_file(dev, &dev_attr_usbip_acl);
+	if (err)
+		goto err_ip;
 
 	return 0;
-
+err_ip:
+	device_remove_file(dev, &dev_attr_usbip_debug);
 err_debug:
 	device_remove_file(dev, &dev_attr_usbip_sockfd);
 err_sockfd:
@@ -173,6 +233,7 @@ static void stub_remove_files(struct device *dev)
 	device_remove_file(dev, &dev_attr_usbip_status);
 	device_remove_file(dev, &dev_attr_usbip_sockfd);
 	device_remove_file(dev, &dev_attr_usbip_debug);
+	device_remove_file(dev, &dev_attr_usbip_acl);
 }
 
 static void stub_shutdown_connection(struct usbip_device *ud)
@@ -306,12 +367,14 @@ static struct stub_device *stub_device_alloc(struct usb_device *udev,
 	sdev->ud.status		= SDEV_ST_AVAILABLE;
 	spin_lock_init(&sdev->ud.lock);
 	sdev->ud.tcp_socket	= NULL;
+	sdev->acls = NULL;
 
 	INIT_LIST_HEAD(&sdev->priv_init);
 	INIT_LIST_HEAD(&sdev->priv_tx);
 	INIT_LIST_HEAD(&sdev->priv_free);
 	INIT_LIST_HEAD(&sdev->unlink_free);
 	INIT_LIST_HEAD(&sdev->unlink_tx);
+	spin_lock_init(&sdev->ip_lock);
 	spin_lock_init(&sdev->priv_lock);
 
 	init_waitqueue_head(&sdev->tx_waitq);
@@ -507,6 +570,9 @@ static void stub_disconnect(struct usb_interface *interface)
 	usb_put_dev(sdev->udev);
 	usb_put_intf(interface);
 
+	/* free ACL list */
+	kfree(sdev->acls);
+
 	/* free sdev */
 	busid_priv->sdev = NULL;
 	stub_device_free(sdev);
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv2 04/11] staging: usbip: Add CIDR matching helper functions
  2013-09-13  9:55 [PATCHv2 0/11] staging: usbip: Userland crypto and ACLs Dominik Paulus
                   ` (2 preceding siblings ...)
  2013-09-13  9:55 ` [PATCHv2 03/11] staging: usbip: Add kernel support for client ACLs Dominik Paulus
@ 2013-09-13  9:55 ` Dominik Paulus
  2013-09-13  9:55 ` [PATCHv2 05/11] staging: usbip: Add ACL support to usbip bind Dominik Paulus
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-13  9:55 UTC (permalink / raw)
  To: linux-kernel
  Cc: Dominik Paulus, Tobias Polzer, Greg Kroah-Hartman, Ilija Hadzic,
	Anthony Foiani, Masanari Iida, linux-kernel, devel

This patch adds a few utility functions to match IP addresses against
CIDR masks.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/src/utils.c | 84 +++++++++++++++++++++++++++++
 drivers/staging/usbip/userspace/src/utils.h | 15 ++++++
 2 files changed, 99 insertions(+)

diff --git a/drivers/staging/usbip/userspace/src/utils.c b/drivers/staging/usbip/userspace/src/utils.c
index 2d4966e..df40817 100644
--- a/drivers/staging/usbip/userspace/src/utils.c
+++ b/drivers/staging/usbip/userspace/src/utils.c
@@ -74,3 +74,87 @@ int modify_match_busid(char *busid, int add)
 
 	return ret;
 }
+
+/*
+ * Parses a string of form "ip/prefix" into a subnet mask to dest.
+ * Returns -1 on error, 0 on success
+ */
+int parse_cidr(const char *src, struct subnet *dest)
+{
+	char *ip, *prefix, *saveptr;
+	char *endptr;
+	struct in6_addr ip6;
+	struct in_addr ip4;
+	int bits;
+	long int tmp;
+	char buf[128]; /* For strtok */
+
+	strncpy(buf, src, sizeof(buf));
+	buf[sizeof(buf)-1] = 0;
+
+	ip = strtok_r(buf, "/", &saveptr);
+	prefix = strtok_r(NULL, "/", &saveptr);
+	if (strtok_r(NULL, "/", &saveptr) || !ip ||
+			strlen(src) > sizeof(buf) - 1)
+		return -1;
+
+	if (inet_pton(AF_INET6, ip, &ip6) == 1) {
+		dest->ai_family = AF_INET6;
+		bits = 128;
+		dest->address.ip6 = ip6;
+	} else if (inet_pton(AF_INET, ip, &ip4) == 1) {
+		dest->ai_family = AF_INET;
+		bits = 32;
+		dest->address.ip4 = ip4;
+	} else {
+		return -1;
+	}
+
+	/*
+	 * We also accept single IPs without an explicitely
+	 * specified prefix
+	 */
+	if (prefix) {
+		tmp = strtol(prefix, &endptr, 10);
+		if (tmp < 0 || tmp > bits || *endptr != '\0')
+			return -1;
+		dest->prefix = tmp;
+	} else {
+		dest->prefix = bits;
+	}
+
+	return 0;
+}
+
+/*
+ * Checks if addr is in range. Expects addr to be a struct in6_addr* if
+ * ai_family == AF_INET6, else struct in_addr*.
+ * Returns 1 if in range, 0 otherwise.
+ */
+int in_range(struct sockaddr_storage *addr, struct subnet range)
+{
+	if (addr->ss_family != range.ai_family)
+		return 0;
+	if (addr->ss_family == AF_INET6) {
+		int i;
+		struct sockaddr_in6 *in6addr = (struct sockaddr_in6 *) addr;
+		unsigned char *ip = in6addr->sin6_addr.s6_addr;
+		for (i = 0; i < range.prefix; ++i) {
+			int idx = i/8, mask = 1 << (7 - i%8);
+			if ((ip[idx] & mask) != (range.address.ip6.s6_addr[idx]
+			  & mask))
+				return 0;
+		}
+	} else {
+		int i;
+		struct sockaddr_in *inaddr = (struct sockaddr_in *) addr;
+		uint32_t ip = ntohl(inaddr->sin_addr.s_addr);
+		uint32_t comp = ntohl(range.address.ip4.s_addr);
+		for (i = 0; i < range.prefix; ++i) {
+			int mask = 1 << (31-i);
+			if ((ip & mask) != (comp & mask))
+				return 0;
+		}
+	}
+	return 1;
+}
diff --git a/drivers/staging/usbip/userspace/src/utils.h b/drivers/staging/usbip/userspace/src/utils.h
index 5916fd3..a3704ef 100644
--- a/drivers/staging/usbip/userspace/src/utils.h
+++ b/drivers/staging/usbip/userspace/src/utils.h
@@ -19,7 +19,22 @@
 #ifndef __UTILS_H
 #define __UTILS_H
 
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netinet/ip.h>
+
+struct subnet {
+	int ai_family;
+	int prefix;
+	union {
+		struct in6_addr ip6;
+		struct in_addr ip4;
+	} address;
+};
+
 int modify_match_busid(char *busid, int add);
+int parse_cidr(const char *src, struct subnet *dest);
+int in_range(struct sockaddr_storage *addr, struct subnet range);
 
 #endif /* __UTILS_H */
 
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv2 05/11] staging: usbip: Add ACL support to usbip bind
  2013-09-13  9:55 [PATCHv2 0/11] staging: usbip: Userland crypto and ACLs Dominik Paulus
                   ` (3 preceding siblings ...)
  2013-09-13  9:55 ` [PATCHv2 04/11] staging: usbip: Add CIDR matching helper functions Dominik Paulus
@ 2013-09-13  9:55 ` Dominik Paulus
  2013-09-13  9:55 ` [PATCHv2 06/11] staging: usbip: Add support for ACLs in usbipd Dominik Paulus
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-13  9:55 UTC (permalink / raw)
  To: linux-kernel
  Cc: Dominik Paulus, Kurt Kanzenbach, Tobias Polzer,
	Greg Kroah-Hartman, Ilija Hadzic, Anthony Foiani, Masanari Iida,
	linux-kernel, devel

Add the command line argument -a (--allow) to usbip bind to specify
networks allowed to attach to the device and code to store the ACLs in
sysfs.

Signed-off-by: Kurt Kanzenbach <ly80toro@cip.cs.fau.de>
Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/doc/usbip.8      |  8 ++-
 drivers/staging/usbip/userspace/src/usbip_bind.c | 74 ++++++++++++++++++++----
 2 files changed, 69 insertions(+), 13 deletions(-)

diff --git a/drivers/staging/usbip/userspace/doc/usbip.8 b/drivers/staging/usbip/userspace/doc/usbip.8
index b5050ed..b818bde 100644
--- a/drivers/staging/usbip/userspace/doc/usbip.8
+++ b/drivers/staging/usbip/userspace/doc/usbip.8
@@ -62,9 +62,15 @@ Detach an imported USB device.
 .PP
 
 .HP
-\fBbind\fR \-\-busid=<\fIbusid\fR>
+\fBbind\fR \-\-busid=<\fIbusid\fR> [\-\-allow=<\fICIDR mask\fR>...]
 .IP
 Make a device exportable.
+.br
+\-\-allow accepts CIDR masks like 127.0.0.0/8 or fd00::/64
+.br
+Only hosts in (at least) one of the allowed ranges are accepted. If
+\-\-allow is omitted, 0.0.0.0/0 and ::/0 are added to the list. The list can
+be read/written from corresponding \fBusbip_acl\fR file in sysfs after bind.
 .PP
 
 .HP
diff --git a/drivers/staging/usbip/userspace/src/usbip_bind.c b/drivers/staging/usbip/userspace/src/usbip_bind.c
index 9ecaf6e..d2739fc 100644
--- a/drivers/staging/usbip/userspace/src/usbip_bind.c
+++ b/drivers/staging/usbip/userspace/src/usbip_bind.c
@@ -37,8 +37,9 @@ enum unbind_status {
 
 static const char usbip_bind_usage_string[] =
 	"usbip bind <args>\n"
-	"    -b, --busid=<busid>    Bind " USBIP_HOST_DRV_NAME ".ko to device "
-	"on <busid>\n";
+	"    -b, --busid=<busid>        Bind " USBIP_HOST_DRV_NAME ".ko to "
+	"device on <busid>\n"
+	"    -a, --allow=<CIDR mask>    Restrict device access to <CIDR mask>\n";
 
 void usbip_bind_usage(void)
 {
@@ -46,17 +47,19 @@ void usbip_bind_usage(void)
 }
 
 /* call at unbound state */
-static int bind_usbip(char *busid)
+static int bind_usbip(char *busid, char *allow)
 {
 	char bus_type[] = "usb";
 	char attr_name[] = "bind";
 	char sysfs_mntpath[SYSFS_PATH_MAX];
 	char bind_attr_path[SYSFS_PATH_MAX];
 	char intf_busid[SYSFS_BUS_ID_SIZE];
+	char ip_attr_path[SYSFS_PATH_MAX];
 	struct sysfs_device *busid_dev;
 	struct sysfs_attribute *bind_attr;
 	struct sysfs_attribute *bConfValue;
 	struct sysfs_attribute *bNumIntfs;
+	struct sysfs_attribute *usbip_ip;
 	int i, failed = 0;
 	int rc, ret = -1;
 
@@ -101,8 +104,32 @@ static int bind_usbip(char *busid)
 			dbg("bind driver at %s failed", intf_busid);
 			failed = 1;
 		}
+
+	}
+
+	/*
+	 * store allowed IP ranges
+	 * specified by `usbip bind -b <busid> --allow <CIDR mask>`
+	 */
+	snprintf(ip_attr_path, sizeof(ip_attr_path),
+			"%s/%s/%s/%s/%s/%s:%.1s.%d/%s",
+			sysfs_mntpath, SYSFS_BUS_NAME, bus_type,
+			SYSFS_DRIVERS_NAME, USBIP_HOST_DRV_NAME, busid,
+			bConfValue->value, 0, "usbip_acl");
+
+	usbip_ip = sysfs_open_attribute(ip_attr_path);
+	if (!usbip_ip) {
+		err("sysfs_open_attribute failed: path=%s",
+				ip_attr_path);
+		goto err_close_busid_dev;
 	}
 
+	rc = sysfs_write_attribute(usbip_ip, allow, strlen(allow));
+	if (rc)
+		err("sysfs_write_attribute failed");
+
+	sysfs_close_attribute(usbip_ip);
+
 	if (!failed)
 		ret = 0;
 
@@ -213,7 +240,7 @@ out:
 	return status;
 }
 
-static int bind_device(char *busid)
+static int bind_device(char *busid, char *allow)
 {
 	int rc;
 
@@ -233,7 +260,7 @@ static int bind_device(char *busid)
 		return -1;
 	}
 
-	rc = bind_usbip(busid);
+	rc = bind_usbip(busid, allow);
 	if (rc < 0) {
 		err("could not bind device to %s", USBIP_HOST_DRV_NAME);
 		modify_match_busid(busid, 0);
@@ -249,29 +276,52 @@ int usbip_bind(int argc, char *argv[])
 {
 	static const struct option opts[] = {
 		{ "busid", required_argument, NULL, 'b' },
+		{ "allow", required_argument, NULL, 'a' },
 		{ NULL,    0,                 NULL,  0  }
 	};
 
-	int opt;
-	int ret = -1;
+	int opt, rc;
+	char allow[4096];
+	char *device = NULL;
+	struct subnet subnet;
+
+	allow[0] = 0;
 
 	for (;;) {
-		opt = getopt_long(argc, argv, "b:", opts, NULL);
+		opt = getopt_long(argc, argv, "a:b:", opts, NULL);
 
 		if (opt == -1)
 			break;
 
 		switch (opt) {
+		case 'a':
+			rc = parse_cidr(optarg, &subnet);
+			if (rc < 0) {
+				err("Invalid subnet specified: %s", optarg);
+				goto err_out;
+			}
+
+			if (strlen(allow) < sizeof(allow) - strlen(optarg) - 2)
+				sprintf(allow + strlen(allow), "%s\n", optarg);
+			else
+				err("ACL length too long.");
+			break;
 		case 'b':
-			ret = bind_device(optarg);
-			goto out;
+			device = optarg;
+			break;
 		default:
 			goto err_out;
 		}
 	}
 
+	/* By default, allow access from all IPs */
+	if (!allow[0])
+		strcpy(allow, "::/0\n0.0.0.0/0\n");
+
+	if (device)
+		return bind_device(device, allow);
+
 err_out:
 	usbip_bind_usage();
-out:
-	return ret;
+	return -1;
 }
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv2 06/11] staging: usbip: Add support for ACLs in usbipd
  2013-09-13  9:55 [PATCHv2 0/11] staging: usbip: Userland crypto and ACLs Dominik Paulus
                   ` (4 preceding siblings ...)
  2013-09-13  9:55 ` [PATCHv2 05/11] staging: usbip: Add ACL support to usbip bind Dominik Paulus
@ 2013-09-13  9:55 ` Dominik Paulus
  2013-09-13  9:55 ` [PATCHv2 07/11] staging: usbip: Add proper error reporting Dominik Paulus
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-13  9:55 UTC (permalink / raw)
  To: linux-kernel
  Cc: Dominik Paulus, Kurt Kanzenbach, Tobias Polzer,
	Greg Kroah-Hartman, Ilija Hadzic, Anthony Foiani, Masanari Iida,
	linux-kernel, devel

Interpret the ACLs stored in sysfs in usbipd and reject clients not
matching one of the ACLs.

Signed-off-by: Kurt Kanzenbach <ly80toro@cip.cs.fau.de>
Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/src/Makefile.am |  2 +-
 drivers/staging/usbip/userspace/src/usbipd.c    | 79 +++++++++++++++++++++++++
 2 files changed, 80 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/usbip/userspace/src/Makefile.am b/drivers/staging/usbip/userspace/src/Makefile.am
index a113003..5161bae 100644
--- a/drivers/staging/usbip/userspace/src/Makefile.am
+++ b/drivers/staging/usbip/userspace/src/Makefile.am
@@ -9,4 +9,4 @@ usbip_SOURCES := usbip.h utils.h usbip.c utils.c usbip_network.c \
 		 usbip_bind.c usbip_unbind.c
 
 
-usbipd_SOURCES := usbip_network.h usbipd.c usbip_network.c
+usbipd_SOURCES := usbip_network.h usbipd.c usbip_network.c utils.c
diff --git a/drivers/staging/usbip/userspace/src/usbipd.c b/drivers/staging/usbip/userspace/src/usbipd.c
index 8db2f27..bc1fd19 100644
--- a/drivers/staging/usbip/userspace/src/usbipd.c
+++ b/drivers/staging/usbip/userspace/src/usbipd.c
@@ -48,6 +48,7 @@
 #include "usbip_host_driver.h"
 #include "usbip_common.h"
 #include "usbip_network.h"
+#include "utils.h"
 
 #undef  PROGNAME
 #define PROGNAME "usbipd"
@@ -169,12 +170,69 @@ static void usbipd_help(void)
 	printf("%s\n", usbipd_help_string);
 }
 
+/*
+ * Checks whether client IP matches at least one
+ * ACL entry
+ *
+ * Returns:
+ *		1  if matches
+ *		0  if not
+ *		-1 on error
+ */
+static int check_allowed(char *acls, int sockfd)
+{
+	int rc, match;
+	struct sockaddr_storage sa;
+	char *acl_cpy, *iter, *saveptr;
+	socklen_t sa_len = sizeof(sa);
+
+	rc = getpeername(sockfd, (struct sockaddr *) &sa, &sa_len);
+	if (rc || sa_len > sizeof(sa)) {
+		err("getpeername failed: %s", strerror(errno));
+		return -1;
+	}
+
+	/*
+	 * We are going to modify our argument,
+	 * thus, we need to duplicate it.
+	 */
+	acl_cpy = strdup(acls);
+	if (!acl_cpy) {
+		err("strdup(): %s", strerror(errno));
+		return -1;
+	}
+
+	match = 0;
+	iter = strtok_r(acl_cpy, "\n", &saveptr);
+	/*
+	 * Iterate over ACL entries and check for
+	 * matching one.
+	 */
+	while (iter) {
+		struct subnet net;
+
+		if (parse_cidr(iter, &net) < 0) {
+			dbg("parse_cidr() failed");
+		} else if (in_range(&sa, net)) {
+			match = 1;
+			break;
+		}
+
+		iter = strtok_r(NULL, "\n", &saveptr);
+	}
+
+	free(acl_cpy);
+	return match;
+}
+
 static int recv_request_import(int sockfd)
 {
 	struct op_import_request req;
 	struct op_common reply;
 	struct usbip_exported_device *edev;
 	struct usbip_usb_device pdu_udev;
+	struct sysfs_attribute *usbip_acl;
+	char ip_attr_path[SYSFS_PATH_MAX];
 	int found = 0;
 	int error = 0;
 	int rc;
@@ -206,6 +264,27 @@ static int recv_request_import(int sockfd)
 		rc = usbip_host_export_device(edev, sockfd);
 		if (rc < 0)
 			error = 1;
+
+		/* check for allowed IPs */
+		snprintf(ip_attr_path, sizeof(ip_attr_path), "%s/%s:%d.%d/%s",
+			edev->udev.path, edev->udev.busid,
+			edev->udev.bConfigurationValue, 0, "usbip_acl");
+
+		usbip_acl = sysfs_open_attribute(ip_attr_path);
+		if (usbip_acl) {
+			rc = sysfs_read_attribute(usbip_acl);
+			if (rc < 0) {
+				err("Unable to open sysfs");
+				error = 1;
+			} else if (check_allowed(usbip_acl->value, sockfd) != 1) {
+				info("Access denied to device %s",
+					edev->udev.busid);
+				error = 1;
+			}
+			sysfs_close_attribute(usbip_acl);
+		} else {
+			err("failed to get ip list");
+		}
 	} else {
 		info("requested device not found: %s", req.busid);
 		error = 1;
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv2 07/11] staging: usbip: Add proper error reporting
  2013-09-13  9:55 [PATCHv2 0/11] staging: usbip: Userland crypto and ACLs Dominik Paulus
                   ` (5 preceding siblings ...)
  2013-09-13  9:55 ` [PATCHv2 06/11] staging: usbip: Add support for ACLs in usbipd Dominik Paulus
@ 2013-09-13  9:55 ` Dominik Paulus
  2013-09-13  9:55 ` [PATCHv2 08/11] staging: usbip: Handle usbip being started as user Dominik Paulus
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-13  9:55 UTC (permalink / raw)
  To: linux-kernel
  Cc: Dominik Paulus, Tobias Polzer, Greg Kroah-Hartman, Ilija Hadzic,
	Anthony Foiani, Masanari Iida, linux-kernel, devel

This patch adds new error codes and features extended error reporting in
op_common packets.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/src/usbip_attach.c |  4 +-
 drivers/staging/usbip/userspace/src/usbip_list.c   |  3 +-
 .../staging/usbip/userspace/src/usbip_network.c    | 50 ++++++++++++++++------
 .../staging/usbip/userspace/src/usbip_network.h    | 17 +++++++-
 drivers/staging/usbip/userspace/src/usbipd.c       | 29 +++++++------
 5 files changed, 74 insertions(+), 29 deletions(-)

diff --git a/drivers/staging/usbip/userspace/src/usbip_attach.c b/drivers/staging/usbip/userspace/src/usbip_attach.c
index 2363e56..2a3f313 100644
--- a/drivers/staging/usbip/userspace/src/usbip_attach.c
+++ b/drivers/staging/usbip/userspace/src/usbip_attach.c
@@ -147,7 +147,7 @@ static int query_import_device(int sockfd, char *busid)
 	/* receive a reply */
 	rc = usbip_net_recv_op_common(sockfd, &code);
 	if (rc < 0) {
-		err("recv op_common");
+		err("recv op_common: %s", usbip_net_strerror(rc));
 		return -1;
 	}
 
@@ -177,7 +177,7 @@ static int attach_device(char *host, char *busid)
 
 	sockfd = usbip_net_connect(host);
 	if (sockfd < 0) {
-		err("tcp connect");
+		err("connection attempt failed");
 		return -1;
 	}
 
diff --git a/drivers/staging/usbip/userspace/src/usbip_list.c b/drivers/staging/usbip/userspace/src/usbip_list.c
index e4fa5b8..ff7acf8 100644
--- a/drivers/staging/usbip/userspace/src/usbip_list.c
+++ b/drivers/staging/usbip/userspace/src/usbip_list.c
@@ -64,7 +64,8 @@ static int get_exported_devices(char *host, int sockfd)
 
 	rc = usbip_net_recv_op_common(sockfd, &code);
 	if (rc < 0) {
-		dbg("usbip_net_recv_op_common failed");
+		err("usbip_net_recv_op_common failed: %s",
+			usbip_net_strerror(rc));
 		return -1;
 	}
 
diff --git a/drivers/staging/usbip/userspace/src/usbip_network.c b/drivers/staging/usbip/userspace/src/usbip_network.c
index eda641f..61cd8db 100644
--- a/drivers/staging/usbip/userspace/src/usbip_network.c
+++ b/drivers/staging/usbip/userspace/src/usbip_network.c
@@ -178,7 +178,7 @@ int usbip_net_recv_op_common(int sockfd, uint16_t *code)
 	rc = usbip_net_recv(sockfd, &op_common, sizeof(op_common));
 	if (rc < 0) {
 		dbg("usbip_net_recv failed: %d", rc);
-		goto err;
+		return -ERR_SYSERR;
 	}
 
 	PACK_OP_COMMON(0, &op_common);
@@ -186,30 +186,48 @@ int usbip_net_recv_op_common(int sockfd, uint16_t *code)
 	if (op_common.version != USBIP_VERSION) {
 		dbg("version mismatch: %d %d", op_common.version,
 		    USBIP_VERSION);
-		goto err;
+		return -ERR_MISMATCH;
 	}
 
 	switch (*code) {
 	case OP_UNSPEC:
 		break;
 	default:
-		if (op_common.code != *code) {
+		/*
+		 * Only accept expected opcode. Exception: OP_REPLY
+		 * flag set may be sent as a reply to all requests,
+		 * if only used for status reporting.
+		 */
+		if (op_common.code != *code && op_common.code != OP_REPLY) {
 			dbg("unexpected pdu %#0x for %#0x", op_common.code,
 			    *code);
-			goto err;
+			return -ERR_UNEXPECTED;
 		}
 	}
 
-	if (op_common.status != ST_OK) {
-		dbg("request failed at peer: %d", op_common.status);
-		goto err;
-	}
-
 	*code = op_common.code;
 
-	return 0;
-err:
-	return -1;
+	return -op_common.status;
+}
+
+const char *usbip_net_strerror(int status)
+{
+	static const char *const errs[] = {
+		/* ERR_OK */ "Success",
+		/* ERR_NA */ "Command failed",
+		/* ERR_MISMATCH */ "Protocol version mismatch",
+		/* ERR_SYSERR */ "System error",
+		/* ERR_UNEXPECTED */ "Unexpected opcode received",
+		/* ERR_AUTHREQ */ "Server requires authentication",
+		/* ERR_PERM */ "Permission denied",
+		/* ERR_NOTFOUND */ "Requested device not found",
+		/* ERR_NOAUTH */ "Server doesn't support authentication"
+	};
+	if (status < 0)
+		status = -status;
+	if (status >= (int) (sizeof(errs) / sizeof(*errs)))
+		return "Invalid";
+	return errs[status];
 }
 
 int usbip_net_set_reuseaddr(int sockfd)
@@ -360,6 +378,7 @@ int usbip_net_connect(char *hostname)
 #ifdef HAVE_GNUTLS
 	if (usbip_srp_password) {
 		int rc;
+		uint16_t code = OP_REP_STARTTLS;
 
 		rc = usbip_net_send_op_common(sockfd, OP_REQ_STARTTLS, 0);
 		if (rc < 0) {
@@ -367,6 +386,13 @@ int usbip_net_connect(char *hostname)
 			return EAI_SYSTEM;
 		}
 
+		rc = usbip_net_recv_op_common(sockfd, &code);
+		if (rc < 0) {
+			err("STARTTLS attempt failed: %s",
+				usbip_net_strerror(rc));
+			return -1;
+		}
+
 		rc = usbip_net_srp_handshake(sockfd);
 		if (rc < 0) {
 			err("Unable to perform TLS handshake (wrong password?): %s",
diff --git a/drivers/staging/usbip/userspace/src/usbip_network.h b/drivers/staging/usbip/userspace/src/usbip_network.h
index 758656b..d3c1b71 100644
--- a/drivers/staging/usbip/userspace/src/usbip_network.h
+++ b/drivers/staging/usbip/userspace/src/usbip_network.h
@@ -29,8 +29,15 @@ struct op_common {
 	uint16_t code;
 
 	/* add more error code */
-#define ST_OK	0x00
-#define ST_NA	0x01
+#define ERR_OK         0x00
+#define ERR_NA         0x01
+#define ERR_MISMATCH   0x02
+#define ERR_SYSERR     0x03
+#define ERR_UNEXPECTED 0x04
+#define ERR_AUTHREQ    0x05
+#define ERR_PERM       0x06
+#define ERR_NOTFOUND   0x07
+#define ERR_NOAUTH     0x08
 	uint32_t status; /* op_code status (for reply) */
 
 } __attribute__((packed));
@@ -179,10 +186,16 @@ void usbip_net_pack_uint32_t(int pack, uint32_t *num);
 void usbip_net_pack_uint16_t(int pack, uint16_t *num);
 void usbip_net_pack_usb_device(int pack, struct usbip_usb_device *udev);
 void usbip_net_pack_usb_interface(int pack, struct usbip_usb_interface *uinf);
+const char *usbip_net_strerror(int status);
 
 ssize_t usbip_net_recv(int sockfd, void *buff, size_t bufflen);
 ssize_t usbip_net_send(int sockfd, void *buff, size_t bufflen);
 int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status);
+/*
+ * Receive opcode.
+ * Returns: 0 on success, negative error code (that may be passed to
+ * usbip_net_strerror) on failure.
+ */
 int usbip_net_recv_op_common(int sockfd, uint16_t *code);
 int usbip_net_set_reuseaddr(int sockfd);
 int usbip_net_set_nodelay(int sockfd);
diff --git a/drivers/staging/usbip/userspace/src/usbipd.c b/drivers/staging/usbip/userspace/src/usbipd.c
index bc1fd19..ae572c6 100644
--- a/drivers/staging/usbip/userspace/src/usbipd.c
+++ b/drivers/staging/usbip/userspace/src/usbipd.c
@@ -234,7 +234,7 @@ static int recv_request_import(int sockfd)
 	struct sysfs_attribute *usbip_acl;
 	char ip_attr_path[SYSFS_PATH_MAX];
 	int found = 0;
-	int error = 0;
+	int error = ERR_OK;
 	int rc;
 
 	memset(&req, 0, sizeof(req));
@@ -263,7 +263,7 @@ static int recv_request_import(int sockfd)
 		/* export device needs a TCP/IP socket descriptor */
 		rc = usbip_host_export_device(edev, sockfd);
 		if (rc < 0)
-			error = 1;
+			error = ERR_SYSERR;
 
 		/* check for allowed IPs */
 		snprintf(ip_attr_path, sizeof(ip_attr_path), "%s/%s:%d.%d/%s",
@@ -275,11 +275,11 @@ static int recv_request_import(int sockfd)
 			rc = sysfs_read_attribute(usbip_acl);
 			if (rc < 0) {
 				err("Unable to open sysfs");
-				error = 1;
+				error = ERR_SYSERR;
 			} else if (check_allowed(usbip_acl->value, sockfd) != 1) {
 				info("Access denied to device %s",
 					edev->udev.busid);
-				error = 1;
+				error = ERR_PERM;
 			}
 			sysfs_close_attribute(usbip_acl);
 		} else {
@@ -287,17 +287,16 @@ static int recv_request_import(int sockfd)
 		}
 	} else {
 		info("requested device not found: %s", req.busid);
-		error = 1;
+		error = ERR_NOTFOUND;
 	}
 
-	rc = usbip_net_send_op_common(sockfd, OP_REP_IMPORT,
-				      (!error ? ST_OK : ST_NA));
+	rc = usbip_net_send_op_common(sockfd, OP_REP_IMPORT, error);
 	if (rc < 0) {
 		dbg("usbip_net_send_op_common failed: %#0x", OP_REP_IMPORT);
 		return -1;
 	}
 
-	if (error) {
+	if (error != 0) {
 		dbg("import request busid %s: failed", req.busid);
 		return -1;
 	}
@@ -333,7 +332,7 @@ static int send_reply_devlist(int connfd)
 	}
 	info("exportable devices: %d", reply.ndev);
 
-	rc = usbip_net_send_op_common(connfd, OP_REP_DEVLIST, ST_OK);
+	rc = usbip_net_send_op_common(connfd, OP_REP_DEVLIST, ERR_OK);
 	if (rc < 0) {
 		dbg("usbip_net_send_op_common failed: %#0x", OP_REP_DEVLIST);
 		return -1;
@@ -416,7 +415,8 @@ static int recv_pdu(int connfd)
 
 		ret = usbip_net_recv_op_common(connfd, &code);
 		if (ret < 0) {
-			dbg("could not receive opcode: %#0x", code);
+			dbg("could not receive opcode: %#0x: %s", code,
+				usbip_net_strerror(ret));
 			return -1;
 		}
 
@@ -424,7 +424,8 @@ static int recv_pdu(int connfd)
 
 		/* We require an authenticated encryption */
 		if (!auth && code != OP_REQ_STARTTLS) {
-			usbip_net_send_op_common(connfd, OP_REPLY, ST_NA);
+			info("Unauthenticated connection attempt");
+			usbip_net_send_op_common(connfd, OP_REPLY, ERR_AUTHREQ);
 			return -1;
 		}
 
@@ -432,10 +433,14 @@ static int recv_pdu(int connfd)
 #ifdef HAVE_GNUTLS
 		case OP_REQ_STARTTLS:
 			if (!need_auth) {
-				ret = -1;
+				usbip_net_send_op_common(connfd, OP_REPLY,
+					ERR_NOAUTH);
 				err("Unexpected TLS handshake attempt (client "
 					"uses password, server doesn't)");
+				ret = -1;
 			} else {
+				usbip_net_send_op_common(connfd, OP_REPLY,
+					ERR_OK);
 				ret = net_srp_server_handshake(connfd);
 				if (ret != 0)
 					err("TLS handshake failed");
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv2 08/11] staging: usbip: Handle usbip being started as user
  2013-09-13  9:55 [PATCHv2 0/11] staging: usbip: Userland crypto and ACLs Dominik Paulus
                   ` (6 preceding siblings ...)
  2013-09-13  9:55 ` [PATCHv2 07/11] staging: usbip: Add proper error reporting Dominik Paulus
@ 2013-09-13  9:55 ` Dominik Paulus
  2013-09-13  9:55 ` [PATCHv2 09/11] staging: usbip: Improve debug output Dominik Paulus
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-13  9:55 UTC (permalink / raw)
  To: linux-kernel
  Cc: Dominik Paulus, Tobias Polzer, Greg Kroah-Hartman, Ilija Hadzic,
	Anthony Foiani, Masanari Iida, linux-kernel, devel

usbip now prints an error message when started as user and requiring
root access. Also, some debug messages are changed to error messages so
the command line utilities now print less confusing (and more verbose)
error messages when not used correctly.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/src/usbip_attach.c |  3 +++
 drivers/staging/usbip/userspace/src/usbip_bind.c   | 16 ++++++++++------
 2 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/drivers/staging/usbip/userspace/src/usbip_attach.c b/drivers/staging/usbip/userspace/src/usbip_attach.c
index 2a3f313..651e93a 100644
--- a/drivers/staging/usbip/userspace/src/usbip_attach.c
+++ b/drivers/staging/usbip/userspace/src/usbip_attach.c
@@ -210,6 +210,9 @@ int usbip_attach(int argc, char *argv[])
 	int opt;
 	int ret = -1;
 
+	if (geteuid() != 0)
+		err("not running as root?");
+
 	for (;;) {
 		opt = getopt_long(argc, argv, "r:b:", opts, NULL);
 
diff --git a/drivers/staging/usbip/userspace/src/usbip_bind.c b/drivers/staging/usbip/userspace/src/usbip_bind.c
index d2739fc..ab26b30f 100644
--- a/drivers/staging/usbip/userspace/src/usbip_bind.c
+++ b/drivers/staging/usbip/userspace/src/usbip_bind.c
@@ -158,7 +158,7 @@ static int unbind_other(char *busid)
 
 	busid_dev = sysfs_open_device(bus_type, busid);
 	if (!busid_dev) {
-		dbg("sysfs_open_device %s failed: %s", busid, strerror(errno));
+		err("sysfs_open_device %s failed: %s", busid, strerror(errno));
 		return -1;
 	}
 
@@ -166,7 +166,7 @@ static int unbind_other(char *busid)
 	bDevClass  = sysfs_get_device_attr(busid_dev, "bDeviceClass");
 	bNumIntfs  = sysfs_get_device_attr(busid_dev, "bNumInterfaces");
 	if (!bConfValue || !bDevClass || !bNumIntfs) {
-		dbg("problem getting device attributes: %s",
+		err("problem getting device attributes: %s",
 		    strerror(errno));
 		goto err_close_busid_dev;
 	}
@@ -181,7 +181,7 @@ static int unbind_other(char *busid)
 			 bConfValue->value, i);
 		intf_dev = sysfs_open_device(bus_type, intf_busid);
 		if (!intf_dev) {
-			dbg("could not open interface device: %s",
+			err("could not open interface device: %s",
 			    strerror(errno));
 			goto err_close_busid_dev;
 		}
@@ -202,14 +202,14 @@ static int unbind_other(char *busid)
 		/* unbinding */
 		intf_drv = sysfs_open_driver(bus_type, intf_dev->driver_name);
 		if (!intf_drv) {
-			dbg("could not open interface driver on %s: %s",
+			err("could not open interface driver on %s: %s",
 			    intf_dev->name, strerror(errno));
 			goto err_close_intf_dev;
 		}
 
 		unbind_attr = sysfs_get_driver_attr(intf_drv, "unbind");
 		if (!unbind_attr) {
-			dbg("problem getting interface driver attribute: %s",
+			err("problem getting interface driver attribute: %s",
 			    strerror(errno));
 			goto err_close_intf_drv;
 		}
@@ -218,7 +218,8 @@ static int unbind_other(char *busid)
 					   SYSFS_BUS_ID_SIZE);
 		if (rc < 0) {
 			/* NOTE: why keep unbinding other interfaces? */
-			dbg("unbind driver at %s failed", intf_dev->bus_id);
+			err("unbind driver at %s failed: %s", intf_dev->bus_id,
+					strerror(errno));
 			status = UNBIND_ST_FAILED;
 		}
 
@@ -287,6 +288,9 @@ int usbip_bind(int argc, char *argv[])
 
 	allow[0] = 0;
 
+	if (geteuid() != 0)
+		err("not running as root?");
+
 	for (;;) {
 		opt = getopt_long(argc, argv, "a:b:", opts, NULL);
 
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv2 09/11] staging: usbip: Improve debug output
  2013-09-13  9:55 [PATCHv2 0/11] staging: usbip: Userland crypto and ACLs Dominik Paulus
                   ` (7 preceding siblings ...)
  2013-09-13  9:55 ` [PATCHv2 08/11] staging: usbip: Handle usbip being started as user Dominik Paulus
@ 2013-09-13  9:55 ` Dominik Paulus
  2013-09-13  9:56 ` [PATCHv2 10/11] staging: usbip: Separate protocol/program version Dominik Paulus
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-13  9:55 UTC (permalink / raw)
  To: linux-kernel
  Cc: Dominik Paulus, Tobias Polzer, Greg Kroah-Hartman, Ilija Hadzic,
	Anthony Foiani, Masanari Iida, linux-kernel, devel

For IPv6, IP:Port is unreadable.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/src/usbipd.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/staging/usbip/userspace/src/usbipd.c b/drivers/staging/usbip/userspace/src/usbipd.c
index ae572c6..6550460 100644
--- a/drivers/staging/usbip/userspace/src/usbipd.c
+++ b/drivers/staging/usbip/userspace/src/usbipd.c
@@ -519,7 +519,7 @@ static int do_accept(int listenfd)
 		return -1;
 	}
 #endif
-	info("connection from %s:%s", host, port);
+	info("connection from %s, port %s", host, port);
 
 	return connfd;
 }
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv2 10/11] staging: usbip: Separate protocol/program version
  2013-09-13  9:55 [PATCHv2 0/11] staging: usbip: Userland crypto and ACLs Dominik Paulus
                   ` (8 preceding siblings ...)
  2013-09-13  9:55 ` [PATCHv2 09/11] staging: usbip: Improve debug output Dominik Paulus
@ 2013-09-13  9:56 ` Dominik Paulus
  2013-09-13  9:56 ` [PATCHv2 11/11] staging: usbip: Increment version to 1.2.0 Dominik Paulus
  2013-09-25 23:36 ` [PATCHv2 0/11] staging: usbip: Userland crypto and ACLs Greg Kroah-Hartman
  11 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-13  9:56 UTC (permalink / raw)
  To: linux-kernel
  Cc: Dominik Paulus, Tobias Polzer, Greg Kroah-Hartman, Ilija Hadzic,
	Anthony Foiani, Masanari Iida, linux-kernel, devel

Not all new program versions necessarily introduce
non-backwards-compatible protocol changes. We thus move the definition
of the protocol version from configure.ac to usbip_network.h, where it
logically belongs to.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/configure.ac        | 1 -
 drivers/staging/usbip/userspace/src/usbip_network.c | 6 +++---
 drivers/staging/usbip/userspace/src/usbip_network.h | 6 ++++++
 3 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/drivers/staging/usbip/userspace/configure.ac b/drivers/staging/usbip/userspace/configure.ac
index 7bba496..099d24b 100644
--- a/drivers/staging/usbip/userspace/configure.ac
+++ b/drivers/staging/usbip/userspace/configure.ac
@@ -2,7 +2,6 @@ dnl Process this file with autoconf to produce a configure script.
 
 AC_PREREQ(2.59)
 AC_INIT([usbip-utils], [1.1.1], [linux-usb@vger.kernel.org])
-AC_DEFINE([USBIP_VERSION], [0x00000111], [binary-coded decimal version number])
 
 CURRENT=0
 REVISION=1
diff --git a/drivers/staging/usbip/userspace/src/usbip_network.c b/drivers/staging/usbip/userspace/src/usbip_network.c
index 61cd8db..f5955c2 100644
--- a/drivers/staging/usbip/userspace/src/usbip_network.c
+++ b/drivers/staging/usbip/userspace/src/usbip_network.c
@@ -153,7 +153,7 @@ int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status)
 
 	memset(&op_common, 0, sizeof(op_common));
 
-	op_common.version = USBIP_VERSION;
+	op_common.version = PROTOCOL_VERSION;
 	op_common.code    = code;
 	op_common.status  = status;
 
@@ -183,9 +183,9 @@ int usbip_net_recv_op_common(int sockfd, uint16_t *code)
 
 	PACK_OP_COMMON(0, &op_common);
 
-	if (op_common.version != USBIP_VERSION) {
+	if (op_common.version != PROTOCOL_VERSION) {
 		dbg("version mismatch: %d %d", op_common.version,
-		    USBIP_VERSION);
+		    PROTOCOL_VERSION);
 		return -ERR_MISMATCH;
 	}
 
diff --git a/drivers/staging/usbip/userspace/src/usbip_network.h b/drivers/staging/usbip/userspace/src/usbip_network.h
index d3c1b71..6a41fd8 100644
--- a/drivers/staging/usbip/userspace/src/usbip_network.h
+++ b/drivers/staging/usbip/userspace/src/usbip_network.h
@@ -14,6 +14,12 @@
 
 #include <stdint.h>
 
+/*
+ * Protocol version. Incremented only on non-backwards-compatible
+ * changes.
+ */
+#define PROTOCOL_VERSION 0x111
+
 extern int usbip_port;
 extern char *usbip_port_string;
 extern char *usbip_srp_password;
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv2 11/11] staging: usbip: Increment version to 1.2.0
  2013-09-13  9:55 [PATCHv2 0/11] staging: usbip: Userland crypto and ACLs Dominik Paulus
                   ` (9 preceding siblings ...)
  2013-09-13  9:56 ` [PATCHv2 10/11] staging: usbip: Separate protocol/program version Dominik Paulus
@ 2013-09-13  9:56 ` Dominik Paulus
  2013-09-25 23:36 ` [PATCHv2 0/11] staging: usbip: Userland crypto and ACLs Greg Kroah-Hartman
  11 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-13  9:56 UTC (permalink / raw)
  To: linux-kernel
  Cc: Dominik Paulus, Tobias Polzer, Greg Kroah-Hartman, Ilija Hadzic,
	Anthony Foiani, Masanari Iida, linux-kernel, devel

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/configure.ac | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/staging/usbip/userspace/configure.ac b/drivers/staging/usbip/userspace/configure.ac
index 099d24b..0b0e035 100644
--- a/drivers/staging/usbip/userspace/configure.ac
+++ b/drivers/staging/usbip/userspace/configure.ac
@@ -1,7 +1,7 @@
 dnl Process this file with autoconf to produce a configure script.
 
 AC_PREREQ(2.59)
-AC_INIT([usbip-utils], [1.1.1], [linux-usb@vger.kernel.org])
+AC_INIT([usbip-utils], [1.2.0], [linux-usb@vger.kernel.org])
 
 CURRENT=0
 REVISION=1
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCHv2 0/11] staging: usbip: Userland crypto and ACLs
  2013-09-13  9:55 [PATCHv2 0/11] staging: usbip: Userland crypto and ACLs Dominik Paulus
                   ` (10 preceding siblings ...)
  2013-09-13  9:56 ` [PATCHv2 11/11] staging: usbip: Increment version to 1.2.0 Dominik Paulus
@ 2013-09-25 23:36 ` Greg Kroah-Hartman
  2013-09-28 17:41   ` Dominik Paulus
  2013-09-28 17:42   ` [PATCHv3 01/16] staging: usbip: Add support for client authentication Dominik Paulus
  11 siblings, 2 replies; 53+ messages in thread
From: Greg Kroah-Hartman @ 2013-09-25 23:36 UTC (permalink / raw)
  To: Dominik Paulus
  Cc: linux-kernel, Ilija Hadzic, Anthony Foiani, Masanari Iida,
	linux-kernel, devel

On Fri, Sep 13, 2013 at 11:55:50AM +0200, Dominik Paulus wrote:
> Hi,
> 
> this patch series includes an updated version of the IPv6 support patch (a call
> to freeaddrinfo() was missing) as well as:
> 
>  - The client/server authentication support using GnuTLS Tobias already
>    announced on the usbip-devel mailing list some time ago[1]
> 
>  - Support for restricting the access to devices to specific IP address ranges
> 
>  - Improved error reporting and new error codes to be passed over the TCP
>    protocol.
> 
> We think that the added features justify a version bump to 1.2.0. The
> corresponding patch is also included. All protocol changes are
> backwards-compatible, thus, we don't increment the protocol version.
> 
> We've already sent this patch series, but forgot to specify a subject line.
> linux-usb apparently received it[2], the LKML didn't. Sorry if you received it
> twice now.

Only the first patch could be applied. Can you refresh this series, and
your other one (fixing the issues pointed out there), and resend them
please?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCHv2 0/11] staging: usbip: Userland crypto and ACLs
  2013-09-25 23:36 ` [PATCHv2 0/11] staging: usbip: Userland crypto and ACLs Greg Kroah-Hartman
@ 2013-09-28 17:41   ` Dominik Paulus
  2013-09-28 17:42   ` [PATCHv3 01/16] staging: usbip: Add support for client authentication Dominik Paulus
  1 sibling, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-28 17:41 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Dominik Paulus, linux-kernel, Ilija Hadzic, Anthony Foiani,
	Masanari Iida, linux-kernel, devel, tobias.polzer

On Wed, Sep 25, 2013 at 04:36:38PM -0700, Greg Kroah-Hartman wrote:
> Only the first patch could be applied. Can you refresh this series, and
> your other one (fixing the issues pointed out there), and resend them
> please?

We've incorporated all of Dan's fixes and suggestions and rebased the
whole series on staging-next.
We left the protocol version at 0x111, since the code is backwards
compatible with current linux-linus if encryption is disabled.
After reading "NIST Special Publication 800-38D" Appendix C we got some
doubts about the security of our usage of gcm with short (4 bytes) tags,
so we incremented USBIP_AUTHSIZE to 16, just to be safe.

Thanks,
Tobias and Dominik

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCHv3  01/16] staging: usbip: Add support for client authentication
  2013-09-25 23:36 ` [PATCHv2 0/11] staging: usbip: Userland crypto and ACLs Greg Kroah-Hartman
  2013-09-28 17:41   ` Dominik Paulus
@ 2013-09-28 17:42   ` Dominik Paulus
  2013-09-28 17:42     ` [PATCHv3 02/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
                       ` (14 more replies)
  1 sibling, 15 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-28 17:42 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: linux-kernel, Ilija Hadzic, Anthony Foiani, Masanari Iida,
	linux-kernel, devel, tobias.polzer, Dominik Paulus

This patch adds support for authenticating both client and server using
a pre-shared passphrase using SRP (Secure Remote Password) over TLS (see
RFC 5054) using GnuTLS. Both usbip and usbipd now accept a shared secret
as a command line argument. Currently, the established TLS connection is
only used to perform a secure handshake and dropped before the socket is
passed to the kernel. The code may be extended to exchange a session key
over TLS and pass it to the kernel to perform IPsec.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/configure.ac       |  14 ++
 drivers/staging/usbip/userspace/doc/usbip.8        |   4 +
 drivers/staging/usbip/userspace/doc/usbipd.8       |   5 +
 drivers/staging/usbip/userspace/src/usbip.c        |  30 ++-
 drivers/staging/usbip/userspace/src/usbip_attach.c |   2 +-
 drivers/staging/usbip/userspace/src/usbip_list.c   |   2 +-
 .../staging/usbip/userspace/src/usbip_network.c    |  81 ++++++++
 .../staging/usbip/userspace/src/usbip_network.h    |   9 +-
 drivers/staging/usbip/userspace/src/usbipd.c       | 217 ++++++++++++++++++---
 9 files changed, 331 insertions(+), 33 deletions(-)

diff --git a/drivers/staging/usbip/userspace/configure.ac b/drivers/staging/usbip/userspace/configure.ac
index 0ee5d92..2208516 100644
--- a/drivers/staging/usbip/userspace/configure.ac
+++ b/drivers/staging/usbip/userspace/configure.ac
@@ -83,6 +83,20 @@ AC_ARG_WITH([tcp-wrappers],
 		AC_DEFINE([HAVE_LIBWRAP], [1], [use tcp wrapper])],
 	       [AC_MSG_RESULT([no]); LIBS="$saved_LIBS"])])
 
+# Checks for the GnuTLS library
+AC_ARG_WITH([gnutls],
+			[AS_HELP_STRING([--with-gnutls],
+							[use the GnuTLS library for authentication])],
+			dnl [ACTION-IF-GIVEN]
+			[if test "$withval" = "yes"; then
+			 PKG_CHECK_MODULES([GNUTLS], [gnutls])
+			 AC_DEFINE([HAVE_GNUTLS], [1], [use gnutls])
+			 CFLAGS="$CFLAGS $GNUTLS_CFLAGS"
+			 LDFLAGS="$LDFLAGS $GNUTLS_LIBS"
+			 fi
+			],
+			)
+
 # Sets directory containing usb.ids.
 AC_ARG_WITH([usbids-dir],
 	    [AS_HELP_STRING([--with-usbids-dir=DIR],
diff --git a/drivers/staging/usbip/userspace/doc/usbip.8 b/drivers/staging/usbip/userspace/doc/usbip.8
index a6097be..847aa40 100644
--- a/drivers/staging/usbip/userspace/doc/usbip.8
+++ b/drivers/staging/usbip/userspace/doc/usbip.8
@@ -27,6 +27,10 @@ Log to syslog.
 \fB\-\-tcp-port PORT\fR
 .IP
 Connect to PORT on remote host (used for attach and list --remote).
+
+\fB\-\-auth\fR
+.IP
+Set the password to be used for client authentication. See usbipd(8) for more information.
 .PP
 
 .SH COMMANDS
diff --git a/drivers/staging/usbip/userspace/doc/usbipd.8 b/drivers/staging/usbip/userspace/doc/usbipd.8
index ac4635d..8beb95a 100644
--- a/drivers/staging/usbip/userspace/doc/usbipd.8
+++ b/drivers/staging/usbip/userspace/doc/usbipd.8
@@ -52,6 +52,11 @@ If no FILE specified, use /var/run/usbipd.pid
 \fB\-tPORT\fR, \fB\-\-tcp\-port PORT\fR
 .IP
 Listen on TCP/IP port PORT.
+
+.HP
+\fB\-s\fR, \fB\-\-auth\fR
+.IP
+Sets the password to be used for client authentication. If -a is used, the server will only accept connections from authenticated clients. Note: USB traffic will still be unencrypted, this currently only serves for authentication.
 .PP
 
 \fB\-h\fR, \fB\-\-help\fR
diff --git a/drivers/staging/usbip/userspace/src/usbip.c b/drivers/staging/usbip/userspace/src/usbip.c
index 04a5f20..8a5de83 100644
--- a/drivers/staging/usbip/userspace/src/usbip.c
+++ b/drivers/staging/usbip/userspace/src/usbip.c
@@ -25,6 +25,12 @@
 #include <getopt.h>
 #include <syslog.h>
 
+#include "../config.h"
+
+#ifdef HAVE_GNUTLS
+#include <gnutls/gnutls.h>
+#endif
+
 #include "usbip_common.h"
 #include "usbip_network.h"
 #include "usbip.h"
@@ -35,8 +41,12 @@ static int usbip_version(int argc, char *argv[]);
 static const char usbip_version_string[] = PACKAGE_STRING;
 
 static const char usbip_usage_string[] =
-	"usbip [--debug] [--log] [--tcp-port PORT] [version]\n"
-	"             [help] <command> <args>\n";
+	"usbip "
+#ifdef HAVE_GNUTLS
+	"[--auth PASSWORD] "
+#endif
+	"[--debug] [--log] [--tcp-port PORT]\n"
+	"             [version] [help] <command> <args>\n";
 
 static void usbip_usage(void)
 {
@@ -142,6 +152,7 @@ int main(int argc, char *argv[])
 		{ "debug",    no_argument,       NULL, 'd' },
 		{ "log",      no_argument,       NULL, 'l' },
 		{ "tcp-port", required_argument, NULL, 't' },
+		{ "auth",     required_argument, NULL, 's' },
 		{ NULL,       0,                 NULL,  0  }
 	};
 
@@ -152,12 +163,25 @@ int main(int argc, char *argv[])
 	usbip_use_stderr = 1;
 	opterr = 0;
 	for (;;) {
-		opt = getopt_long(argc, argv, "+dlt:", opts, NULL);
+		opt = getopt_long(argc, argv, "+dls:t:", opts, NULL);
 
 		if (opt == -1)
 			break;
 
 		switch (opt) {
+		case 's':
+#ifdef HAVE_GNUTLS
+			usbip_srp_password = optarg;
+			rc = gnutls_global_init();
+			if (rc < 0) {
+				err("Unable to initialize GnuTLS library: %s",
+					gnutls_strerror(rc));
+				return EXIT_FAILURE;
+			}
+#else
+			err("usbip has been compiled without GnuTLS support");
+#endif
+			break;
 		case 'd':
 			usbip_use_debug = 1;
 			break;
diff --git a/drivers/staging/usbip/userspace/src/usbip_attach.c b/drivers/staging/usbip/userspace/src/usbip_attach.c
index 0858411..2363e56 100644
--- a/drivers/staging/usbip/userspace/src/usbip_attach.c
+++ b/drivers/staging/usbip/userspace/src/usbip_attach.c
@@ -175,7 +175,7 @@ static int attach_device(char *host, char *busid)
 	int rc;
 	int rhport;
 
-	sockfd = usbip_net_tcp_connect(host, usbip_port_string);
+	sockfd = usbip_net_connect(host);
 	if (sockfd < 0) {
 		err("tcp connect");
 		return -1;
diff --git a/drivers/staging/usbip/userspace/src/usbip_list.c b/drivers/staging/usbip/userspace/src/usbip_list.c
index 237e099..e4fa5b8 100644
--- a/drivers/staging/usbip/userspace/src/usbip_list.c
+++ b/drivers/staging/usbip/userspace/src/usbip_list.c
@@ -131,7 +131,7 @@ static int list_exported_devices(char *host)
 	int rc;
 	int sockfd;
 
-	sockfd = usbip_net_tcp_connect(host, usbip_port_string);
+	sockfd = usbip_net_connect(host);
 	if (sockfd < 0) {
 		err("could not connect to %s:%s: %s", host,
 		    usbip_port_string, gai_strerror(sockfd));
diff --git a/drivers/staging/usbip/userspace/src/usbip_network.c b/drivers/staging/usbip/userspace/src/usbip_network.c
index b4c37e7..9425cb6 100644
--- a/drivers/staging/usbip/userspace/src/usbip_network.c
+++ b/drivers/staging/usbip/userspace/src/usbip_network.c
@@ -29,11 +29,19 @@
 #include <tcpd.h>
 #endif
 
+#include "../config.h"
+#ifdef HAVE_GNUTLS
+#include <gnutls/gnutls.h>
+#endif
+
 #include "usbip_common.h"
 #include "usbip_network.h"
 
 int usbip_port = 3240;
 char *usbip_port_string = "3240";
+#ifdef HAVE_GNUTLS
+char *usbip_srp_password;
+#endif
 
 void usbip_setup_port_number(char *arg)
 {
@@ -301,3 +309,76 @@ int usbip_net_tcp_connect(char *hostname, char *service)
 
 	return sockfd;
 }
+
+#ifdef HAVE_GNUTLS
+int usbip_net_srp_handshake(int sockfd)
+{
+	int ret;
+	gnutls_session_t session;
+	gnutls_srp_client_credentials_t srp_cred;
+
+	ret = gnutls_srp_allocate_client_credentials(&srp_cred);
+	if (ret < 0)
+		return ret;
+
+	gnutls_srp_set_client_credentials(srp_cred, "dummyuser",
+		usbip_srp_password);
+
+	ret = gnutls_init(&session, GNUTLS_CLIENT);
+	if (ret < 0) {
+		gnutls_srp_free_client_credentials(srp_cred);
+		return ret;
+	}
+
+	gnutls_priority_set_direct(session, "NORMAL:+SRP", NULL);
+
+	gnutls_credentials_set(session, GNUTLS_CRD_SRP, srp_cred);
+	gnutls_transport_set_int (session, sockfd);
+
+	do {
+		ret = gnutls_handshake(session);
+	} while (ret < 0 && !gnutls_error_is_fatal(ret));
+
+	gnutls_bye(session, GNUTLS_SHUT_RDWR);
+
+	gnutls_deinit(session);
+	gnutls_srp_free_client_credentials(srp_cred);
+
+	return ret;
+}
+#endif
+
+/*
+ * Connect to the server. Performs the TCP connection attempt
+ * and - if necessary - the TLS handshake used for authentication.
+ */
+int usbip_net_connect(char *hostname)
+{
+	int sockfd;
+
+	sockfd = usbip_net_tcp_connect(hostname, usbip_port_string);
+	if (sockfd < 0)
+		return sockfd;
+
+#ifdef HAVE_GNUTLS
+	if (usbip_srp_password) {
+		int rc;
+
+		rc = usbip_net_send_op_common(sockfd, OP_REQ_STARTTLS, 0);
+		if (rc < 0) {
+			err("usbip_net_send_op_common failed");
+			return EAI_SYSTEM;
+		}
+
+		rc = usbip_net_srp_handshake(sockfd);
+		if (rc < 0) {
+			err("Unable to perform TLS handshake (wrong password?): %s",
+				gnutls_strerror(rc));
+			close(sockfd);
+			return EAI_SYSTEM;
+		}
+	}
+#endif
+
+	return sockfd;
+}
diff --git a/drivers/staging/usbip/userspace/src/usbip_network.h b/drivers/staging/usbip/userspace/src/usbip_network.h
index f19ae19..758656b 100644
--- a/drivers/staging/usbip/userspace/src/usbip_network.h
+++ b/drivers/staging/usbip/userspace/src/usbip_network.h
@@ -16,6 +16,7 @@
 
 extern int usbip_port;
 extern char *usbip_port_string;
+extern char *usbip_srp_password;
 void usbip_setup_port_number(char *arg);
 
 /* ---------------------------------------------------------------------- */
@@ -168,6 +169,12 @@ struct op_devlist_reply_extra {
 	usbip_net_pack_uint32_t(pack, &(reply)->ndev);\
 } while (0)
 
+/* ---------------------------------------------------------------------- */
+/* Initiate encrypted connection. */
+#define OP_STARTTLS 0x06
+#define OP_REQ_STARTTLS   (OP_REQUEST | OP_STARTTLS)
+#define OP_REP_STARTTLS   (OP_REPLY   | OP_STARTTLS)
+
 void usbip_net_pack_uint32_t(int pack, uint32_t *num);
 void usbip_net_pack_uint16_t(int pack, uint16_t *num);
 void usbip_net_pack_usb_device(int pack, struct usbip_usb_device *udev);
@@ -181,6 +188,6 @@ int usbip_net_set_reuseaddr(int sockfd);
 int usbip_net_set_nodelay(int sockfd);
 int usbip_net_set_keepalive(int sockfd);
 int usbip_net_set_v6only(int sockfd);
-int usbip_net_tcp_connect(char *hostname, char *port);
+int usbip_net_connect(char *hostname);
 
 #endif /* __USBIP_NETWORK_H */
diff --git a/drivers/staging/usbip/userspace/src/usbipd.c b/drivers/staging/usbip/userspace/src/usbipd.c
index 7980f8b..8db2f27 100644
--- a/drivers/staging/usbip/userspace/src/usbipd.c
+++ b/drivers/staging/usbip/userspace/src/usbipd.c
@@ -32,6 +32,11 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 
+#ifdef HAVE_GNUTLS
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#endif
+
 #ifdef HAVE_LIBWRAP
 #include <tcpd.h>
 #endif
@@ -63,6 +68,11 @@ static const char usbipd_help_string[] =
 	"	-6, --ipv6\n"
 	"		Bind to IPv6. Default is both.\n"
 	"\n"
+#ifdef HAVE_GNUTLS
+	"	-sPASSWORD, --auth PASSWORD\n"
+	"		Set PASSWORD as key used for authentication.\n"
+	"\n"
+#endif
 	"	-D, --daemon\n"
 	"		Run as a daemon process.\n"
 	"\n"
@@ -82,6 +92,78 @@ static const char usbipd_help_string[] =
 	"	-v, --version\n"
 	"		Show version.\n";
 
+static int need_auth;
+#ifdef HAVE_GNUTLS
+static gnutls_datum_t srp_salt, srp_verifier;
+static gnutls_srp_server_credentials_t srp_cred;
+
+#define SRP_GROUP gnutls_srp_2048_group_generator
+#define SRP_PRIME gnutls_srp_2048_group_prime
+
+static int net_srp_callback(gnutls_session_t sess, const char *username,
+	gnutls_datum_t *nsalt, gnutls_datum_t *nverifier, gnutls_datum_t *g,
+	gnutls_datum_t *n)
+{
+	/*
+	 * GnuTLS expects us to allocate all data returned from callbacks
+	 * using gnutls_malloc(), thus, we have to create a fresh copy of
+	 * our static credentials for every connection.
+	 */
+	nsalt->data = gnutls_malloc(srp_salt.size);
+	nverifier->data = gnutls_malloc(srp_verifier.size);
+	if (nsalt->data == NULL || nverifier->data == NULL) {
+		gnutls_free(nsalt->data);
+		gnutls_free(nverifier->data);
+		return -1;
+	}
+	nsalt->size = srp_salt.size;
+	nverifier->size = srp_verifier.size;
+	memcpy(nverifier->data, srp_verifier.data, srp_verifier.size);
+	memcpy(nsalt->data, srp_salt.data, srp_salt.size);
+
+	*g = SRP_GROUP;
+	*n = SRP_PRIME;
+
+	/* We only have a single session, thus, ignore it */
+	(void) sess;
+
+	if (strcmp(username, "dummyuser"))
+		/* User invalid, stored dummy data in g and n. */
+		return 1;
+
+	return 0;
+}
+
+static int net_srp_server_handshake(int connfd)
+{
+	int ret;
+	gnutls_session_t session;
+
+	if (gnutls_init(&session, GNUTLS_SERVER) != 0)
+		return -1;
+	gnutls_priority_set_direct(session, "NORMAL:-KX-ALL:+SRP", NULL);
+	if (gnutls_credentials_set(session, GNUTLS_CRD_SRP, srp_cred) != 0)
+		return -1;
+
+	gnutls_transport_set_int(session, connfd);
+
+	do {
+		ret = gnutls_handshake(session);
+	} while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+	if (ret < 0)
+		err("GnuTLS handshake failed (%s)", gnutls_strerror(ret));
+	else
+		info("GnuTLS handshake completed");
+
+	if (gnutls_bye(session, GNUTLS_SHUT_RDWR) != 0)
+		err("Unable to shutdown TLS connection.");
+	gnutls_deinit(session);
+
+	return ret;
+}
+#endif
+
 static void usbipd_help(void)
 {
 	printf("%s\n", usbipd_help_string);
@@ -238,14 +320,7 @@ static int recv_request_devlist(int connfd)
 
 static int recv_pdu(int connfd)
 {
-	uint16_t code = OP_UNSPEC;
-	int ret;
-
-	ret = usbip_net_recv_op_common(connfd, &code);
-	if (ret < 0) {
-		dbg("could not receive opcode: %#0x", code);
-		return -1;
-	}
+	int auth = !need_auth, cont = 1, ret;
 
 	ret = usbip_host_refresh_device_list();
 	if (ret < 0) {
@@ -253,25 +328,64 @@ static int recv_pdu(int connfd)
 		return -1;
 	}
 
-	info("received request: %#0x(%d)", code, connfd);
-	switch (code) {
-	case OP_REQ_DEVLIST:
-		ret = recv_request_devlist(connfd);
-		break;
-	case OP_REQ_IMPORT:
-		ret = recv_request_import(connfd);
-		break;
-	case OP_REQ_DEVINFO:
-	case OP_REQ_CRYPKEY:
-	default:
-		err("received an unknown opcode: %#0x", code);
-		ret = -1;
-	}
+	/*
+	 * Process opcodes. We might receive more than one, as the
+	 * client might send STARTTLS first
+	 */
+	while (cont) {
+		uint16_t code = OP_UNSPEC;
 
-	if (ret == 0)
-		info("request %#0x(%d): complete", code, connfd);
-	else
-		info("request %#0x(%d): failed", code, connfd);
+		ret = usbip_net_recv_op_common(connfd, &code);
+		if (ret < 0) {
+			dbg("could not receive opcode: %#0x", code);
+			return -1;
+		}
+
+		info("received request: %#0x(%d)", code, connfd);
+
+		/* We require an authenticated encryption */
+		if (!auth && code != OP_REQ_STARTTLS) {
+			usbip_net_send_op_common(connfd, OP_REPLY, ST_NA);
+			return -1;
+		}
+
+		switch (code) {
+#ifdef HAVE_GNUTLS
+		case OP_REQ_STARTTLS:
+			if (!need_auth) {
+				ret = -1;
+				err("Unexpected TLS handshake attempt (client "
+					"uses password, server doesn't)");
+			} else {
+				ret = net_srp_server_handshake(connfd);
+				if (ret != 0)
+					err("TLS handshake failed");
+				auth = 1;
+			}
+			break;
+#endif
+		case OP_REQ_DEVLIST:
+			ret = recv_request_devlist(connfd);
+			cont = 0;
+			break;
+		case OP_REQ_IMPORT:
+			ret = recv_request_import(connfd);
+			cont = 0;
+			break;
+		case OP_REQ_DEVINFO:
+		case OP_REQ_CRYPKEY:
+		default:
+			err("received an unknown opcode: %#0x", code);
+			ret = -1;
+		}
+
+		if (ret == 0)
+			info("request %#0x(%d): complete", code, connfd);
+		else {
+			info("request %#0x(%d): failed", code, connfd);
+			break;
+		}
+	}
 
 	return ret;
 }
@@ -577,6 +691,37 @@ static int do_standalone_mode(int daemonize, int ipv4, int ipv6)
 	return 0;
 }
 
+#ifdef HAVE_GNUTLS
+static int usbip_init_gnutls(void)
+{
+	int ret;
+
+	gnutls_global_init();
+
+	srp_salt.data = gnutls_malloc(16);
+	if (!srp_salt.data)
+		return GNUTLS_E_MEMORY_ERROR;
+
+	ret = gnutls_rnd(GNUTLS_RND_NONCE, srp_salt.data, 16);
+	if (ret < 0)
+		return ret;
+	srp_salt.size = 16;
+
+	ret = gnutls_srp_allocate_server_credentials(&srp_cred);
+	if (ret < 0)
+		return ret;
+
+	ret = gnutls_srp_verifier("dummyuser", optarg, &srp_salt, &SRP_GROUP,
+		&SRP_PRIME, &srp_verifier);
+	if (ret < 0)
+		return ret;
+
+	gnutls_srp_set_server_credentials_function(srp_cred, net_srp_callback);
+
+	return GNUTLS_E_SUCCESS;
+}
+#endif
+
 int main(int argc, char *argv[])
 {
 	static const struct option longopts[] = {
@@ -589,6 +734,7 @@ int main(int argc, char *argv[])
 		{ "tcp-port", required_argument, NULL, 't' },
 		{ "help",     no_argument,       NULL, 'h' },
 		{ "version",  no_argument,       NULL, 'v' },
+		{ "auth",     required_argument, NULL, 's' },
 		{ NULL,	      0,                 NULL,  0  }
 	};
 
@@ -601,6 +747,9 @@ int main(int argc, char *argv[])
 	int daemonize = 0;
 	int ipv4 = 0, ipv6 = 0;
 	int opt, rc = -1;
+#ifdef HAVE_GNUTLS
+	int ret;
+#endif
 	pid_file = NULL;
 
 	usbip_use_stderr = 1;
@@ -611,7 +760,7 @@ int main(int argc, char *argv[])
 
 	cmd = cmd_standalone_mode;
 	for (;;) {
-		opt = getopt_long(argc, argv, "46DdP::t:hv", longopts, NULL);
+		opt = getopt_long(argc, argv, "46s:DdP::t:hv", longopts, NULL);
 
 		if (opt == -1)
 			break;
@@ -623,6 +772,20 @@ int main(int argc, char *argv[])
 		case '6':
 			ipv6 = 1;
 			break;
+		case 's':
+#ifdef HAVE_GNUTLS
+			need_auth = 1;
+			ret = usbip_init_gnutls();
+			if (ret < 0) {
+				err("Unable to initialize GnuTLS: %s",
+					gnutls_strerror(ret));
+				return EXIT_FAILURE;
+			}
+			break;
+#else
+			err("usbipd has been compiled without GnuTLS support");
+			break;
+#endif
 		case 'D':
 			daemonize = 1;
 			break;
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv3  02/16] staging: usbip: Add kernel support for client ACLs
  2013-09-28 17:42   ` [PATCHv3 01/16] staging: usbip: Add support for client authentication Dominik Paulus
@ 2013-09-28 17:42     ` Dominik Paulus
  2013-09-30  8:29       ` Dan Carpenter
  2013-09-28 17:42     ` [PATCHv3 03/16] staging: usbip: Add CIDR matching helper functions Dominik Paulus
                       ` (13 subsequent siblings)
  14 siblings, 1 reply; 53+ messages in thread
From: Dominik Paulus @ 2013-09-28 17:42 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: linux-kernel, Ilija Hadzic, Anthony Foiani, Masanari Iida,
	linux-kernel, devel, tobias.polzer, Dominik Paulus,
	Kurt Kanzenbach

This patch adds the possibility to stored ACLs for allowed clients for
each stub device in sysfs. It adds a new sysfs entry called "usbip_acl"
for each stub device, containing a list of CIDR masks of allowed
clients. This file will be used by usbip and usbipd to store the ACL.

Signed-off-by: Kurt Kanzenbach <ly80toro@cip.cs.fau.de>
Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/stub.h     |  5 +++
 drivers/staging/usbip/stub_dev.c | 68 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 72 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/usbip/stub.h b/drivers/staging/usbip/stub.h
index a73e437..cfe75d1 100644
--- a/drivers/staging/usbip/stub.h
+++ b/drivers/staging/usbip/stub.h
@@ -60,6 +60,11 @@ struct stub_device {
 	struct list_head unlink_free;
 
 	wait_queue_head_t tx_waitq;
+
+	/* list of allowed IP addrs */
+	char *acls;
+	/* for locking list operations */
+	spinlock_t ip_lock;
 };
 
 /* private data into urb->priv */
diff --git a/drivers/staging/usbip/stub_dev.c b/drivers/staging/usbip/stub_dev.c
index 76a1ff0..3939b60 100644
--- a/drivers/staging/usbip/stub_dev.c
+++ b/drivers/staging/usbip/stub_dev.c
@@ -142,6 +142,62 @@ err:
 }
 static DEVICE_ATTR(usbip_sockfd, S_IWUSR, NULL, store_sockfd);
 
+/*
+ * This function replaces the current ACL list
+ */
+static ssize_t store_acl(struct device *dev, struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct stub_device *sdev = dev_get_drvdata(dev);
+	int retval = 0;
+
+	if (!sdev)
+		return -ENODEV;
+
+	if (count >= PAGE_SIZE)
+		/* Prevent storing oversized ACLs in kernel memory */
+		return -EINVAL;
+
+	/* Store ACL */
+	spin_lock_irq(&sdev->ip_lock);
+	kfree(sdev->acls);
+	sdev->acls = kstrdup(buf, GFP_KERNEL);
+	if (IS_ERR(sdev->acls)) {
+		retval = PTR_ERR(sdev->acls);
+		sdev->acls = NULL;
+	} else {
+		retval = strlen(sdev->acls);
+	}
+	spin_unlock_irq(&sdev->ip_lock);
+
+	return retval;
+}
+
+/*
+ * This functions prints all allowed IP addrs for this dev
+ */
+static ssize_t show_acl(struct device *dev, struct device_attribute *attr,
+		       char *buf)
+{
+	struct stub_device *sdev = dev_get_drvdata(dev);
+	int retval = 0;
+
+	if (!sdev)
+		return -ENODEV;
+
+	spin_lock_irq(&sdev->ip_lock);
+	if (sdev->acls == NULL) {
+		retval = 0;
+	} else {
+		strcpy(buf, sdev->acls);
+		retval = strlen(buf);
+	}
+	spin_unlock_irq(&sdev->ip_lock);
+
+	return retval;
+}
+static DEVICE_ATTR(usbip_acl, S_IWUSR | S_IRUGO, show_acl, store_acl);
+
 static int stub_add_files(struct device *dev)
 {
 	int err = 0;
@@ -157,9 +213,13 @@ static int stub_add_files(struct device *dev)
 	err = device_create_file(dev, &dev_attr_usbip_debug);
 	if (err)
 		goto err_debug;
+	err = device_create_file(dev, &dev_attr_usbip_acl);
+	if (err)
+		goto err_ip;
 
 	return 0;
-
+err_ip:
+	device_remove_file(dev, &dev_attr_usbip_debug);
 err_debug:
 	device_remove_file(dev, &dev_attr_usbip_sockfd);
 err_sockfd:
@@ -173,6 +233,7 @@ static void stub_remove_files(struct device *dev)
 	device_remove_file(dev, &dev_attr_usbip_status);
 	device_remove_file(dev, &dev_attr_usbip_sockfd);
 	device_remove_file(dev, &dev_attr_usbip_debug);
+	device_remove_file(dev, &dev_attr_usbip_acl);
 }
 
 static void stub_shutdown_connection(struct usbip_device *ud)
@@ -306,12 +367,14 @@ static struct stub_device *stub_device_alloc(struct usb_device *udev,
 	sdev->ud.status		= SDEV_ST_AVAILABLE;
 	spin_lock_init(&sdev->ud.lock);
 	sdev->ud.tcp_socket	= NULL;
+	sdev->acls = NULL;
 
 	INIT_LIST_HEAD(&sdev->priv_init);
 	INIT_LIST_HEAD(&sdev->priv_tx);
 	INIT_LIST_HEAD(&sdev->priv_free);
 	INIT_LIST_HEAD(&sdev->unlink_free);
 	INIT_LIST_HEAD(&sdev->unlink_tx);
+	spin_lock_init(&sdev->ip_lock);
 	spin_lock_init(&sdev->priv_lock);
 
 	init_waitqueue_head(&sdev->tx_waitq);
@@ -511,6 +574,9 @@ static void stub_disconnect(struct usb_interface *interface)
 	usb_put_dev(sdev->udev);
 	usb_put_intf(interface);
 
+	/* free ACL list */
+	kfree(sdev->acls);
+
 	/* free sdev */
 	busid_priv->sdev = NULL;
 	stub_device_free(sdev);
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv3  03/16] staging: usbip: Add CIDR matching helper functions
  2013-09-28 17:42   ` [PATCHv3 01/16] staging: usbip: Add support for client authentication Dominik Paulus
  2013-09-28 17:42     ` [PATCHv3 02/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
@ 2013-09-28 17:42     ` Dominik Paulus
  2013-09-28 17:42     ` [PATCHv3 04/16] staging: usbip: Add ACL support to usbip bind Dominik Paulus
                       ` (12 subsequent siblings)
  14 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-28 17:42 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: linux-kernel, Ilija Hadzic, Anthony Foiani, Masanari Iida,
	linux-kernel, devel, tobias.polzer, Dominik Paulus

This patch adds a few utility functions to match IP addresses against
CIDR masks.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/src/utils.c | 84 +++++++++++++++++++++++++++++
 drivers/staging/usbip/userspace/src/utils.h | 15 ++++++
 2 files changed, 99 insertions(+)

diff --git a/drivers/staging/usbip/userspace/src/utils.c b/drivers/staging/usbip/userspace/src/utils.c
index 2d4966e..df40817 100644
--- a/drivers/staging/usbip/userspace/src/utils.c
+++ b/drivers/staging/usbip/userspace/src/utils.c
@@ -74,3 +74,87 @@ int modify_match_busid(char *busid, int add)
 
 	return ret;
 }
+
+/*
+ * Parses a string of form "ip/prefix" into a subnet mask to dest.
+ * Returns -1 on error, 0 on success
+ */
+int parse_cidr(const char *src, struct subnet *dest)
+{
+	char *ip, *prefix, *saveptr;
+	char *endptr;
+	struct in6_addr ip6;
+	struct in_addr ip4;
+	int bits;
+	long int tmp;
+	char buf[128]; /* For strtok */
+
+	strncpy(buf, src, sizeof(buf));
+	buf[sizeof(buf)-1] = 0;
+
+	ip = strtok_r(buf, "/", &saveptr);
+	prefix = strtok_r(NULL, "/", &saveptr);
+	if (strtok_r(NULL, "/", &saveptr) || !ip ||
+			strlen(src) > sizeof(buf) - 1)
+		return -1;
+
+	if (inet_pton(AF_INET6, ip, &ip6) == 1) {
+		dest->ai_family = AF_INET6;
+		bits = 128;
+		dest->address.ip6 = ip6;
+	} else if (inet_pton(AF_INET, ip, &ip4) == 1) {
+		dest->ai_family = AF_INET;
+		bits = 32;
+		dest->address.ip4 = ip4;
+	} else {
+		return -1;
+	}
+
+	/*
+	 * We also accept single IPs without an explicitely
+	 * specified prefix
+	 */
+	if (prefix) {
+		tmp = strtol(prefix, &endptr, 10);
+		if (tmp < 0 || tmp > bits || *endptr != '\0')
+			return -1;
+		dest->prefix = tmp;
+	} else {
+		dest->prefix = bits;
+	}
+
+	return 0;
+}
+
+/*
+ * Checks if addr is in range. Expects addr to be a struct in6_addr* if
+ * ai_family == AF_INET6, else struct in_addr*.
+ * Returns 1 if in range, 0 otherwise.
+ */
+int in_range(struct sockaddr_storage *addr, struct subnet range)
+{
+	if (addr->ss_family != range.ai_family)
+		return 0;
+	if (addr->ss_family == AF_INET6) {
+		int i;
+		struct sockaddr_in6 *in6addr = (struct sockaddr_in6 *) addr;
+		unsigned char *ip = in6addr->sin6_addr.s6_addr;
+		for (i = 0; i < range.prefix; ++i) {
+			int idx = i/8, mask = 1 << (7 - i%8);
+			if ((ip[idx] & mask) != (range.address.ip6.s6_addr[idx]
+			  & mask))
+				return 0;
+		}
+	} else {
+		int i;
+		struct sockaddr_in *inaddr = (struct sockaddr_in *) addr;
+		uint32_t ip = ntohl(inaddr->sin_addr.s_addr);
+		uint32_t comp = ntohl(range.address.ip4.s_addr);
+		for (i = 0; i < range.prefix; ++i) {
+			int mask = 1 << (31-i);
+			if ((ip & mask) != (comp & mask))
+				return 0;
+		}
+	}
+	return 1;
+}
diff --git a/drivers/staging/usbip/userspace/src/utils.h b/drivers/staging/usbip/userspace/src/utils.h
index 5916fd3..a3704ef 100644
--- a/drivers/staging/usbip/userspace/src/utils.h
+++ b/drivers/staging/usbip/userspace/src/utils.h
@@ -19,7 +19,22 @@
 #ifndef __UTILS_H
 #define __UTILS_H
 
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netinet/ip.h>
+
+struct subnet {
+	int ai_family;
+	int prefix;
+	union {
+		struct in6_addr ip6;
+		struct in_addr ip4;
+	} address;
+};
+
 int modify_match_busid(char *busid, int add);
+int parse_cidr(const char *src, struct subnet *dest);
+int in_range(struct sockaddr_storage *addr, struct subnet range);
 
 #endif /* __UTILS_H */
 
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv3  04/16] staging: usbip: Add ACL support to usbip bind
  2013-09-28 17:42   ` [PATCHv3 01/16] staging: usbip: Add support for client authentication Dominik Paulus
  2013-09-28 17:42     ` [PATCHv3 02/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
  2013-09-28 17:42     ` [PATCHv3 03/16] staging: usbip: Add CIDR matching helper functions Dominik Paulus
@ 2013-09-28 17:42     ` Dominik Paulus
  2013-09-28 17:42     ` [PATCHv3 05/16] staging: usbip: Add support for ACLs in usbipd Dominik Paulus
                       ` (11 subsequent siblings)
  14 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-28 17:42 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: linux-kernel, Ilija Hadzic, Anthony Foiani, Masanari Iida,
	linux-kernel, devel, tobias.polzer, Dominik Paulus,
	Kurt Kanzenbach

Add the command line argument -a (--allow) to usbip bind to specify
networks allowed to attach to the device and code to store the ACLs in
sysfs.

Signed-off-by: Kurt Kanzenbach <ly80toro@cip.cs.fau.de>
Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/doc/usbip.8      |  8 ++-
 drivers/staging/usbip/userspace/src/usbip_bind.c | 74 ++++++++++++++++++++----
 2 files changed, 69 insertions(+), 13 deletions(-)

diff --git a/drivers/staging/usbip/userspace/doc/usbip.8 b/drivers/staging/usbip/userspace/doc/usbip.8
index 847aa40..c7ba36f 100644
--- a/drivers/staging/usbip/userspace/doc/usbip.8
+++ b/drivers/staging/usbip/userspace/doc/usbip.8
@@ -60,9 +60,15 @@ Detach an imported USB device.
 .PP
 
 .HP
-\fBbind\fR \-\-busid=<\fIbusid\fR>
+\fBbind\fR \-\-busid=<\fIbusid\fR> [\-\-allow=<\fICIDR mask\fR>...]
 .IP
 Make a device exportable.
+.br
+\-\-allow accepts CIDR masks like 127.0.0.0/8 or fd00::/64
+.br
+Only hosts in (at least) one of the allowed ranges are accepted. If
+\-\-allow is omitted, 0.0.0.0/0 and ::/0 are added to the list. The list can
+be read/written from corresponding \fBusbip_acl\fR file in sysfs after bind.
 .PP
 
 .HP
diff --git a/drivers/staging/usbip/userspace/src/usbip_bind.c b/drivers/staging/usbip/userspace/src/usbip_bind.c
index 9ecaf6e..d2739fc 100644
--- a/drivers/staging/usbip/userspace/src/usbip_bind.c
+++ b/drivers/staging/usbip/userspace/src/usbip_bind.c
@@ -37,8 +37,9 @@ enum unbind_status {
 
 static const char usbip_bind_usage_string[] =
 	"usbip bind <args>\n"
-	"    -b, --busid=<busid>    Bind " USBIP_HOST_DRV_NAME ".ko to device "
-	"on <busid>\n";
+	"    -b, --busid=<busid>        Bind " USBIP_HOST_DRV_NAME ".ko to "
+	"device on <busid>\n"
+	"    -a, --allow=<CIDR mask>    Restrict device access to <CIDR mask>\n";
 
 void usbip_bind_usage(void)
 {
@@ -46,17 +47,19 @@ void usbip_bind_usage(void)
 }
 
 /* call at unbound state */
-static int bind_usbip(char *busid)
+static int bind_usbip(char *busid, char *allow)
 {
 	char bus_type[] = "usb";
 	char attr_name[] = "bind";
 	char sysfs_mntpath[SYSFS_PATH_MAX];
 	char bind_attr_path[SYSFS_PATH_MAX];
 	char intf_busid[SYSFS_BUS_ID_SIZE];
+	char ip_attr_path[SYSFS_PATH_MAX];
 	struct sysfs_device *busid_dev;
 	struct sysfs_attribute *bind_attr;
 	struct sysfs_attribute *bConfValue;
 	struct sysfs_attribute *bNumIntfs;
+	struct sysfs_attribute *usbip_ip;
 	int i, failed = 0;
 	int rc, ret = -1;
 
@@ -101,8 +104,32 @@ static int bind_usbip(char *busid)
 			dbg("bind driver at %s failed", intf_busid);
 			failed = 1;
 		}
+
+	}
+
+	/*
+	 * store allowed IP ranges
+	 * specified by `usbip bind -b <busid> --allow <CIDR mask>`
+	 */
+	snprintf(ip_attr_path, sizeof(ip_attr_path),
+			"%s/%s/%s/%s/%s/%s:%.1s.%d/%s",
+			sysfs_mntpath, SYSFS_BUS_NAME, bus_type,
+			SYSFS_DRIVERS_NAME, USBIP_HOST_DRV_NAME, busid,
+			bConfValue->value, 0, "usbip_acl");
+
+	usbip_ip = sysfs_open_attribute(ip_attr_path);
+	if (!usbip_ip) {
+		err("sysfs_open_attribute failed: path=%s",
+				ip_attr_path);
+		goto err_close_busid_dev;
 	}
 
+	rc = sysfs_write_attribute(usbip_ip, allow, strlen(allow));
+	if (rc)
+		err("sysfs_write_attribute failed");
+
+	sysfs_close_attribute(usbip_ip);
+
 	if (!failed)
 		ret = 0;
 
@@ -213,7 +240,7 @@ out:
 	return status;
 }
 
-static int bind_device(char *busid)
+static int bind_device(char *busid, char *allow)
 {
 	int rc;
 
@@ -233,7 +260,7 @@ static int bind_device(char *busid)
 		return -1;
 	}
 
-	rc = bind_usbip(busid);
+	rc = bind_usbip(busid, allow);
 	if (rc < 0) {
 		err("could not bind device to %s", USBIP_HOST_DRV_NAME);
 		modify_match_busid(busid, 0);
@@ -249,29 +276,52 @@ int usbip_bind(int argc, char *argv[])
 {
 	static const struct option opts[] = {
 		{ "busid", required_argument, NULL, 'b' },
+		{ "allow", required_argument, NULL, 'a' },
 		{ NULL,    0,                 NULL,  0  }
 	};
 
-	int opt;
-	int ret = -1;
+	int opt, rc;
+	char allow[4096];
+	char *device = NULL;
+	struct subnet subnet;
+
+	allow[0] = 0;
 
 	for (;;) {
-		opt = getopt_long(argc, argv, "b:", opts, NULL);
+		opt = getopt_long(argc, argv, "a:b:", opts, NULL);
 
 		if (opt == -1)
 			break;
 
 		switch (opt) {
+		case 'a':
+			rc = parse_cidr(optarg, &subnet);
+			if (rc < 0) {
+				err("Invalid subnet specified: %s", optarg);
+				goto err_out;
+			}
+
+			if (strlen(allow) < sizeof(allow) - strlen(optarg) - 2)
+				sprintf(allow + strlen(allow), "%s\n", optarg);
+			else
+				err("ACL length too long.");
+			break;
 		case 'b':
-			ret = bind_device(optarg);
-			goto out;
+			device = optarg;
+			break;
 		default:
 			goto err_out;
 		}
 	}
 
+	/* By default, allow access from all IPs */
+	if (!allow[0])
+		strcpy(allow, "::/0\n0.0.0.0/0\n");
+
+	if (device)
+		return bind_device(device, allow);
+
 err_out:
 	usbip_bind_usage();
-out:
-	return ret;
+	return -1;
 }
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv3  05/16] staging: usbip: Add support for ACLs in usbipd
  2013-09-28 17:42   ` [PATCHv3 01/16] staging: usbip: Add support for client authentication Dominik Paulus
                       ` (2 preceding siblings ...)
  2013-09-28 17:42     ` [PATCHv3 04/16] staging: usbip: Add ACL support to usbip bind Dominik Paulus
@ 2013-09-28 17:42     ` Dominik Paulus
  2013-09-28 17:42     ` [PATCHv3 06/16] staging: usbip: Add proper error reporting Dominik Paulus
                       ` (10 subsequent siblings)
  14 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-28 17:42 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: linux-kernel, Ilija Hadzic, Anthony Foiani, Masanari Iida,
	linux-kernel, devel, tobias.polzer, Dominik Paulus,
	Kurt Kanzenbach

Interpret the ACLs stored in sysfs in usbipd and reject clients not
matching one of the ACLs.

Signed-off-by: Kurt Kanzenbach <ly80toro@cip.cs.fau.de>
Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/src/Makefile.am |  2 +-
 drivers/staging/usbip/userspace/src/usbipd.c    | 79 +++++++++++++++++++++++++
 2 files changed, 80 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/usbip/userspace/src/Makefile.am b/drivers/staging/usbip/userspace/src/Makefile.am
index a113003..5161bae 100644
--- a/drivers/staging/usbip/userspace/src/Makefile.am
+++ b/drivers/staging/usbip/userspace/src/Makefile.am
@@ -9,4 +9,4 @@ usbip_SOURCES := usbip.h utils.h usbip.c utils.c usbip_network.c \
 		 usbip_bind.c usbip_unbind.c
 
 
-usbipd_SOURCES := usbip_network.h usbipd.c usbip_network.c
+usbipd_SOURCES := usbip_network.h usbipd.c usbip_network.c utils.c
diff --git a/drivers/staging/usbip/userspace/src/usbipd.c b/drivers/staging/usbip/userspace/src/usbipd.c
index 8db2f27..bc1fd19 100644
--- a/drivers/staging/usbip/userspace/src/usbipd.c
+++ b/drivers/staging/usbip/userspace/src/usbipd.c
@@ -48,6 +48,7 @@
 #include "usbip_host_driver.h"
 #include "usbip_common.h"
 #include "usbip_network.h"
+#include "utils.h"
 
 #undef  PROGNAME
 #define PROGNAME "usbipd"
@@ -169,12 +170,69 @@ static void usbipd_help(void)
 	printf("%s\n", usbipd_help_string);
 }
 
+/*
+ * Checks whether client IP matches at least one
+ * ACL entry
+ *
+ * Returns:
+ *		1  if matches
+ *		0  if not
+ *		-1 on error
+ */
+static int check_allowed(char *acls, int sockfd)
+{
+	int rc, match;
+	struct sockaddr_storage sa;
+	char *acl_cpy, *iter, *saveptr;
+	socklen_t sa_len = sizeof(sa);
+
+	rc = getpeername(sockfd, (struct sockaddr *) &sa, &sa_len);
+	if (rc || sa_len > sizeof(sa)) {
+		err("getpeername failed: %s", strerror(errno));
+		return -1;
+	}
+
+	/*
+	 * We are going to modify our argument,
+	 * thus, we need to duplicate it.
+	 */
+	acl_cpy = strdup(acls);
+	if (!acl_cpy) {
+		err("strdup(): %s", strerror(errno));
+		return -1;
+	}
+
+	match = 0;
+	iter = strtok_r(acl_cpy, "\n", &saveptr);
+	/*
+	 * Iterate over ACL entries and check for
+	 * matching one.
+	 */
+	while (iter) {
+		struct subnet net;
+
+		if (parse_cidr(iter, &net) < 0) {
+			dbg("parse_cidr() failed");
+		} else if (in_range(&sa, net)) {
+			match = 1;
+			break;
+		}
+
+		iter = strtok_r(NULL, "\n", &saveptr);
+	}
+
+	free(acl_cpy);
+	return match;
+}
+
 static int recv_request_import(int sockfd)
 {
 	struct op_import_request req;
 	struct op_common reply;
 	struct usbip_exported_device *edev;
 	struct usbip_usb_device pdu_udev;
+	struct sysfs_attribute *usbip_acl;
+	char ip_attr_path[SYSFS_PATH_MAX];
 	int found = 0;
 	int error = 0;
 	int rc;
@@ -206,6 +264,27 @@ static int recv_request_import(int sockfd)
 		rc = usbip_host_export_device(edev, sockfd);
 		if (rc < 0)
 			error = 1;
+
+		/* check for allowed IPs */
+		snprintf(ip_attr_path, sizeof(ip_attr_path), "%s/%s:%d.%d/%s",
+			edev->udev.path, edev->udev.busid,
+			edev->udev.bConfigurationValue, 0, "usbip_acl");
+
+		usbip_acl = sysfs_open_attribute(ip_attr_path);
+		if (usbip_acl) {
+			rc = sysfs_read_attribute(usbip_acl);
+			if (rc < 0) {
+				err("Unable to open sysfs");
+				error = 1;
+			} else if (check_allowed(usbip_acl->value, sockfd) != 1) {
+				info("Access denied to device %s",
+					edev->udev.busid);
+				error = 1;
+			}
+			sysfs_close_attribute(usbip_acl);
+		} else {
+			err("failed to get ip list");
+		}
 	} else {
 		info("requested device not found: %s", req.busid);
 		error = 1;
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv3  06/16] staging: usbip: Add proper error reporting
  2013-09-28 17:42   ` [PATCHv3 01/16] staging: usbip: Add support for client authentication Dominik Paulus
                       ` (3 preceding siblings ...)
  2013-09-28 17:42     ` [PATCHv3 05/16] staging: usbip: Add support for ACLs in usbipd Dominik Paulus
@ 2013-09-28 17:42     ` Dominik Paulus
  2013-09-28 17:42     ` [PATCHv3 07/16] staging: usbip: Handle usbip being started as user Dominik Paulus
                       ` (9 subsequent siblings)
  14 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-28 17:42 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: linux-kernel, Ilija Hadzic, Anthony Foiani, Masanari Iida,
	linux-kernel, devel, tobias.polzer, Dominik Paulus

This patch adds new error codes and features extended error reporting in
op_common packets.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/src/usbip_attach.c |  4 +-
 drivers/staging/usbip/userspace/src/usbip_list.c   |  3 +-
 .../staging/usbip/userspace/src/usbip_network.c    | 50 ++++++++++++++++------
 .../staging/usbip/userspace/src/usbip_network.h    | 17 +++++++-
 drivers/staging/usbip/userspace/src/usbipd.c       | 29 +++++++------
 5 files changed, 74 insertions(+), 29 deletions(-)

diff --git a/drivers/staging/usbip/userspace/src/usbip_attach.c b/drivers/staging/usbip/userspace/src/usbip_attach.c
index 2363e56..2a3f313 100644
--- a/drivers/staging/usbip/userspace/src/usbip_attach.c
+++ b/drivers/staging/usbip/userspace/src/usbip_attach.c
@@ -147,7 +147,7 @@ static int query_import_device(int sockfd, char *busid)
 	/* receive a reply */
 	rc = usbip_net_recv_op_common(sockfd, &code);
 	if (rc < 0) {
-		err("recv op_common");
+		err("recv op_common: %s", usbip_net_strerror(rc));
 		return -1;
 	}
 
@@ -177,7 +177,7 @@ static int attach_device(char *host, char *busid)
 
 	sockfd = usbip_net_connect(host);
 	if (sockfd < 0) {
-		err("tcp connect");
+		err("connection attempt failed");
 		return -1;
 	}
 
diff --git a/drivers/staging/usbip/userspace/src/usbip_list.c b/drivers/staging/usbip/userspace/src/usbip_list.c
index e4fa5b8..ff7acf8 100644
--- a/drivers/staging/usbip/userspace/src/usbip_list.c
+++ b/drivers/staging/usbip/userspace/src/usbip_list.c
@@ -64,7 +64,8 @@ static int get_exported_devices(char *host, int sockfd)
 
 	rc = usbip_net_recv_op_common(sockfd, &code);
 	if (rc < 0) {
-		dbg("usbip_net_recv_op_common failed");
+		err("usbip_net_recv_op_common failed: %s",
+			usbip_net_strerror(rc));
 		return -1;
 	}
 
diff --git a/drivers/staging/usbip/userspace/src/usbip_network.c b/drivers/staging/usbip/userspace/src/usbip_network.c
index 9425cb6..e573cb2 100644
--- a/drivers/staging/usbip/userspace/src/usbip_network.c
+++ b/drivers/staging/usbip/userspace/src/usbip_network.c
@@ -181,7 +181,7 @@ int usbip_net_recv_op_common(int sockfd, uint16_t *code)
 	rc = usbip_net_recv(sockfd, &op_common, sizeof(op_common));
 	if (rc < 0) {
 		dbg("usbip_net_recv failed: %d", rc);
-		goto err;
+		return -ERR_SYSERR;
 	}
 
 	PACK_OP_COMMON(0, &op_common);
@@ -189,30 +189,48 @@ int usbip_net_recv_op_common(int sockfd, uint16_t *code)
 	if (op_common.version != USBIP_VERSION) {
 		dbg("version mismatch: %d %d", op_common.version,
 		    USBIP_VERSION);
-		goto err;
+		return -ERR_MISMATCH;
 	}
 
 	switch (*code) {
 	case OP_UNSPEC:
 		break;
 	default:
-		if (op_common.code != *code) {
+		/*
+		 * Only accept expected opcode. Exception: OP_REPLY
+		 * flag set may be sent as a reply to all requests,
+		 * if only used for status reporting.
+		 */
+		if (op_common.code != *code && op_common.code != OP_REPLY) {
 			dbg("unexpected pdu %#0x for %#0x", op_common.code,
 			    *code);
-			goto err;
+			return -ERR_UNEXPECTED;
 		}
 	}
 
-	if (op_common.status != ST_OK) {
-		dbg("request failed at peer: %d", op_common.status);
-		goto err;
-	}
-
 	*code = op_common.code;
 
-	return 0;
-err:
-	return -1;
+	return -op_common.status;
+}
+
+const char *usbip_net_strerror(int status)
+{
+	static const char *const errs[] = {
+		/* ERR_OK */ "Success",
+		/* ERR_NA */ "Command failed",
+		/* ERR_MISMATCH */ "Protocol version mismatch",
+		/* ERR_SYSERR */ "System error",
+		/* ERR_UNEXPECTED */ "Unexpected opcode received",
+		/* ERR_AUTHREQ */ "Server requires authentication",
+		/* ERR_PERM */ "Permission denied",
+		/* ERR_NOTFOUND */ "Requested device not found",
+		/* ERR_NOAUTH */ "Server doesn't support authentication"
+	};
+	if (status < 0)
+		status = -status;
+	if (status >= (int) (sizeof(errs) / sizeof(*errs)))
+		return "Invalid";
+	return errs[status];
 }
 
 int usbip_net_set_reuseaddr(int sockfd)
@@ -363,6 +381,7 @@ int usbip_net_connect(char *hostname)
 #ifdef HAVE_GNUTLS
 	if (usbip_srp_password) {
 		int rc;
+		uint16_t code = OP_REP_STARTTLS;
 
 		rc = usbip_net_send_op_common(sockfd, OP_REQ_STARTTLS, 0);
 		if (rc < 0) {
@@ -370,6 +389,13 @@ int usbip_net_connect(char *hostname)
 			return EAI_SYSTEM;
 		}
 
+		rc = usbip_net_recv_op_common(sockfd, &code);
+		if (rc < 0) {
+			err("STARTTLS attempt failed: %s",
+				usbip_net_strerror(rc));
+			return -1;
+		}
+
 		rc = usbip_net_srp_handshake(sockfd);
 		if (rc < 0) {
 			err("Unable to perform TLS handshake (wrong password?): %s",
diff --git a/drivers/staging/usbip/userspace/src/usbip_network.h b/drivers/staging/usbip/userspace/src/usbip_network.h
index 758656b..d3c1b71 100644
--- a/drivers/staging/usbip/userspace/src/usbip_network.h
+++ b/drivers/staging/usbip/userspace/src/usbip_network.h
@@ -29,8 +29,15 @@ struct op_common {
 	uint16_t code;
 
 	/* add more error code */
-#define ST_OK	0x00
-#define ST_NA	0x01
+#define ERR_OK         0x00
+#define ERR_NA         0x01
+#define ERR_MISMATCH   0x02
+#define ERR_SYSERR     0x03
+#define ERR_UNEXPECTED 0x04
+#define ERR_AUTHREQ    0x05
+#define ERR_PERM       0x06
+#define ERR_NOTFOUND   0x07
+#define ERR_NOAUTH     0x08
 	uint32_t status; /* op_code status (for reply) */
 
 } __attribute__((packed));
@@ -179,10 +186,16 @@ void usbip_net_pack_uint32_t(int pack, uint32_t *num);
 void usbip_net_pack_uint16_t(int pack, uint16_t *num);
 void usbip_net_pack_usb_device(int pack, struct usbip_usb_device *udev);
 void usbip_net_pack_usb_interface(int pack, struct usbip_usb_interface *uinf);
+const char *usbip_net_strerror(int status);
 
 ssize_t usbip_net_recv(int sockfd, void *buff, size_t bufflen);
 ssize_t usbip_net_send(int sockfd, void *buff, size_t bufflen);
 int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status);
+/*
+ * Receive opcode.
+ * Returns: 0 on success, negative error code (that may be passed to
+ * usbip_net_strerror) on failure.
+ */
 int usbip_net_recv_op_common(int sockfd, uint16_t *code);
 int usbip_net_set_reuseaddr(int sockfd);
 int usbip_net_set_nodelay(int sockfd);
diff --git a/drivers/staging/usbip/userspace/src/usbipd.c b/drivers/staging/usbip/userspace/src/usbipd.c
index bc1fd19..ae572c6 100644
--- a/drivers/staging/usbip/userspace/src/usbipd.c
+++ b/drivers/staging/usbip/userspace/src/usbipd.c
@@ -234,7 +234,7 @@ static int recv_request_import(int sockfd)
 	struct sysfs_attribute *usbip_acl;
 	char ip_attr_path[SYSFS_PATH_MAX];
 	int found = 0;
-	int error = 0;
+	int error = ERR_OK;
 	int rc;
 
 	memset(&req, 0, sizeof(req));
@@ -263,7 +263,7 @@ static int recv_request_import(int sockfd)
 		/* export device needs a TCP/IP socket descriptor */
 		rc = usbip_host_export_device(edev, sockfd);
 		if (rc < 0)
-			error = 1;
+			error = ERR_SYSERR;
 
 		/* check for allowed IPs */
 		snprintf(ip_attr_path, sizeof(ip_attr_path), "%s/%s:%d.%d/%s",
@@ -275,11 +275,11 @@ static int recv_request_import(int sockfd)
 			rc = sysfs_read_attribute(usbip_acl);
 			if (rc < 0) {
 				err("Unable to open sysfs");
-				error = 1;
+				error = ERR_SYSERR;
 			} else if (check_allowed(usbip_acl->value, sockfd) != 1) {
 				info("Access denied to device %s",
 					edev->udev.busid);
-				error = 1;
+				error = ERR_PERM;
 			}
 			sysfs_close_attribute(usbip_acl);
 		} else {
@@ -287,17 +287,16 @@ static int recv_request_import(int sockfd)
 		}
 	} else {
 		info("requested device not found: %s", req.busid);
-		error = 1;
+		error = ERR_NOTFOUND;
 	}
 
-	rc = usbip_net_send_op_common(sockfd, OP_REP_IMPORT,
-				      (!error ? ST_OK : ST_NA));
+	rc = usbip_net_send_op_common(sockfd, OP_REP_IMPORT, error);
 	if (rc < 0) {
 		dbg("usbip_net_send_op_common failed: %#0x", OP_REP_IMPORT);
 		return -1;
 	}
 
-	if (error) {
+	if (error != 0) {
 		dbg("import request busid %s: failed", req.busid);
 		return -1;
 	}
@@ -333,7 +332,7 @@ static int send_reply_devlist(int connfd)
 	}
 	info("exportable devices: %d", reply.ndev);
 
-	rc = usbip_net_send_op_common(connfd, OP_REP_DEVLIST, ST_OK);
+	rc = usbip_net_send_op_common(connfd, OP_REP_DEVLIST, ERR_OK);
 	if (rc < 0) {
 		dbg("usbip_net_send_op_common failed: %#0x", OP_REP_DEVLIST);
 		return -1;
@@ -416,7 +415,8 @@ static int recv_pdu(int connfd)
 
 		ret = usbip_net_recv_op_common(connfd, &code);
 		if (ret < 0) {
-			dbg("could not receive opcode: %#0x", code);
+			dbg("could not receive opcode: %#0x: %s", code,
+				usbip_net_strerror(ret));
 			return -1;
 		}
 
@@ -424,7 +424,8 @@ static int recv_pdu(int connfd)
 
 		/* We require an authenticated encryption */
 		if (!auth && code != OP_REQ_STARTTLS) {
-			usbip_net_send_op_common(connfd, OP_REPLY, ST_NA);
+			info("Unauthenticated connection attempt");
+			usbip_net_send_op_common(connfd, OP_REPLY, ERR_AUTHREQ);
 			return -1;
 		}
 
@@ -432,10 +433,14 @@ static int recv_pdu(int connfd)
 #ifdef HAVE_GNUTLS
 		case OP_REQ_STARTTLS:
 			if (!need_auth) {
-				ret = -1;
+				usbip_net_send_op_common(connfd, OP_REPLY,
+					ERR_NOAUTH);
 				err("Unexpected TLS handshake attempt (client "
 					"uses password, server doesn't)");
+				ret = -1;
 			} else {
+				usbip_net_send_op_common(connfd, OP_REPLY,
+					ERR_OK);
 				ret = net_srp_server_handshake(connfd);
 				if (ret != 0)
 					err("TLS handshake failed");
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv3  07/16] staging: usbip: Handle usbip being started as user
  2013-09-28 17:42   ` [PATCHv3 01/16] staging: usbip: Add support for client authentication Dominik Paulus
                       ` (4 preceding siblings ...)
  2013-09-28 17:42     ` [PATCHv3 06/16] staging: usbip: Add proper error reporting Dominik Paulus
@ 2013-09-28 17:42     ` Dominik Paulus
  2013-09-28 17:42     ` [PATCHv3 08/16] staging: usbip: Improve debug output Dominik Paulus
                       ` (8 subsequent siblings)
  14 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-28 17:42 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: linux-kernel, Ilija Hadzic, Anthony Foiani, Masanari Iida,
	linux-kernel, devel, tobias.polzer, Dominik Paulus

usbip now prints an error message when started as user and requiring
root access. Also, some debug messages are changed to error messages so
the command line utilities now print less confusing (and more verbose)
error messages when not used correctly.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/src/usbip_attach.c |  3 +++
 drivers/staging/usbip/userspace/src/usbip_bind.c   | 16 ++++++++++------
 2 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/drivers/staging/usbip/userspace/src/usbip_attach.c b/drivers/staging/usbip/userspace/src/usbip_attach.c
index 2a3f313..651e93a 100644
--- a/drivers/staging/usbip/userspace/src/usbip_attach.c
+++ b/drivers/staging/usbip/userspace/src/usbip_attach.c
@@ -210,6 +210,9 @@ int usbip_attach(int argc, char *argv[])
 	int opt;
 	int ret = -1;
 
+	if (geteuid() != 0)
+		err("not running as root?");
+
 	for (;;) {
 		opt = getopt_long(argc, argv, "r:b:", opts, NULL);
 
diff --git a/drivers/staging/usbip/userspace/src/usbip_bind.c b/drivers/staging/usbip/userspace/src/usbip_bind.c
index d2739fc..ab26b30f 100644
--- a/drivers/staging/usbip/userspace/src/usbip_bind.c
+++ b/drivers/staging/usbip/userspace/src/usbip_bind.c
@@ -158,7 +158,7 @@ static int unbind_other(char *busid)
 
 	busid_dev = sysfs_open_device(bus_type, busid);
 	if (!busid_dev) {
-		dbg("sysfs_open_device %s failed: %s", busid, strerror(errno));
+		err("sysfs_open_device %s failed: %s", busid, strerror(errno));
 		return -1;
 	}
 
@@ -166,7 +166,7 @@ static int unbind_other(char *busid)
 	bDevClass  = sysfs_get_device_attr(busid_dev, "bDeviceClass");
 	bNumIntfs  = sysfs_get_device_attr(busid_dev, "bNumInterfaces");
 	if (!bConfValue || !bDevClass || !bNumIntfs) {
-		dbg("problem getting device attributes: %s",
+		err("problem getting device attributes: %s",
 		    strerror(errno));
 		goto err_close_busid_dev;
 	}
@@ -181,7 +181,7 @@ static int unbind_other(char *busid)
 			 bConfValue->value, i);
 		intf_dev = sysfs_open_device(bus_type, intf_busid);
 		if (!intf_dev) {
-			dbg("could not open interface device: %s",
+			err("could not open interface device: %s",
 			    strerror(errno));
 			goto err_close_busid_dev;
 		}
@@ -202,14 +202,14 @@ static int unbind_other(char *busid)
 		/* unbinding */
 		intf_drv = sysfs_open_driver(bus_type, intf_dev->driver_name);
 		if (!intf_drv) {
-			dbg("could not open interface driver on %s: %s",
+			err("could not open interface driver on %s: %s",
 			    intf_dev->name, strerror(errno));
 			goto err_close_intf_dev;
 		}
 
 		unbind_attr = sysfs_get_driver_attr(intf_drv, "unbind");
 		if (!unbind_attr) {
-			dbg("problem getting interface driver attribute: %s",
+			err("problem getting interface driver attribute: %s",
 			    strerror(errno));
 			goto err_close_intf_drv;
 		}
@@ -218,7 +218,8 @@ static int unbind_other(char *busid)
 					   SYSFS_BUS_ID_SIZE);
 		if (rc < 0) {
 			/* NOTE: why keep unbinding other interfaces? */
-			dbg("unbind driver at %s failed", intf_dev->bus_id);
+			err("unbind driver at %s failed: %s", intf_dev->bus_id,
+					strerror(errno));
 			status = UNBIND_ST_FAILED;
 		}
 
@@ -287,6 +288,9 @@ int usbip_bind(int argc, char *argv[])
 
 	allow[0] = 0;
 
+	if (geteuid() != 0)
+		err("not running as root?");
+
 	for (;;) {
 		opt = getopt_long(argc, argv, "a:b:", opts, NULL);
 
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv3  08/16] staging: usbip: Improve debug output
  2013-09-28 17:42   ` [PATCHv3 01/16] staging: usbip: Add support for client authentication Dominik Paulus
                       ` (5 preceding siblings ...)
  2013-09-28 17:42     ` [PATCHv3 07/16] staging: usbip: Handle usbip being started as user Dominik Paulus
@ 2013-09-28 17:42     ` Dominik Paulus
  2013-09-28 17:42     ` [PATCHv3 09/16] staging: usbip: Separate protocol/program version Dominik Paulus
                       ` (7 subsequent siblings)
  14 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-28 17:42 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: linux-kernel, Ilija Hadzic, Anthony Foiani, Masanari Iida,
	linux-kernel, devel, tobias.polzer, Dominik Paulus

For IPv6, IP:Port is unreadable.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/src/usbipd.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/staging/usbip/userspace/src/usbipd.c b/drivers/staging/usbip/userspace/src/usbipd.c
index ae572c6..6550460 100644
--- a/drivers/staging/usbip/userspace/src/usbipd.c
+++ b/drivers/staging/usbip/userspace/src/usbipd.c
@@ -519,7 +519,7 @@ static int do_accept(int listenfd)
 		return -1;
 	}
 #endif
-	info("connection from %s:%s", host, port);
+	info("connection from %s, port %s", host, port);
 
 	return connfd;
 }
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv3  09/16] staging: usbip: Separate protocol/program version
  2013-09-28 17:42   ` [PATCHv3 01/16] staging: usbip: Add support for client authentication Dominik Paulus
                       ` (6 preceding siblings ...)
  2013-09-28 17:42     ` [PATCHv3 08/16] staging: usbip: Improve debug output Dominik Paulus
@ 2013-09-28 17:42     ` Dominik Paulus
  2013-09-28 17:42     ` [PATCHv3 10/16] staging: usbip: TLS for all userspace communication Dominik Paulus
                       ` (6 subsequent siblings)
  14 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-28 17:42 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: linux-kernel, Ilija Hadzic, Anthony Foiani, Masanari Iida,
	linux-kernel, devel, tobias.polzer, Dominik Paulus

Not all new program versions necessarily introduce
non-backwards-compatible protocol changes. We thus move the definition
of the protocol version from configure.ac to usbip_network.h, where it
logically belongs to.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/configure.ac        | 1 -
 drivers/staging/usbip/userspace/src/usbip_network.c | 6 +++---
 drivers/staging/usbip/userspace/src/usbip_network.h | 6 ++++++
 3 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/drivers/staging/usbip/userspace/configure.ac b/drivers/staging/usbip/userspace/configure.ac
index 2208516..bc08b81 100644
--- a/drivers/staging/usbip/userspace/configure.ac
+++ b/drivers/staging/usbip/userspace/configure.ac
@@ -2,7 +2,6 @@ dnl Process this file with autoconf to produce a configure script.
 
 AC_PREREQ(2.59)
 AC_INIT([usbip-utils], [1.1.1], [linux-usb@vger.kernel.org])
-AC_DEFINE([USBIP_VERSION], [0x00000111], [binary-coded decimal version number])
 
 CURRENT=0
 REVISION=1
diff --git a/drivers/staging/usbip/userspace/src/usbip_network.c b/drivers/staging/usbip/userspace/src/usbip_network.c
index e573cb2..a606e2b 100644
--- a/drivers/staging/usbip/userspace/src/usbip_network.c
+++ b/drivers/staging/usbip/userspace/src/usbip_network.c
@@ -156,7 +156,7 @@ int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status)
 
 	memset(&op_common, 0, sizeof(op_common));
 
-	op_common.version = USBIP_VERSION;
+	op_common.version = PROTOCOL_VERSION;
 	op_common.code    = code;
 	op_common.status  = status;
 
@@ -186,9 +186,9 @@ int usbip_net_recv_op_common(int sockfd, uint16_t *code)
 
 	PACK_OP_COMMON(0, &op_common);
 
-	if (op_common.version != USBIP_VERSION) {
+	if (op_common.version != PROTOCOL_VERSION) {
 		dbg("version mismatch: %d %d", op_common.version,
-		    USBIP_VERSION);
+		    PROTOCOL_VERSION);
 		return -ERR_MISMATCH;
 	}
 
diff --git a/drivers/staging/usbip/userspace/src/usbip_network.h b/drivers/staging/usbip/userspace/src/usbip_network.h
index d3c1b71..6a41fd8 100644
--- a/drivers/staging/usbip/userspace/src/usbip_network.h
+++ b/drivers/staging/usbip/userspace/src/usbip_network.h
@@ -14,6 +14,12 @@
 
 #include <stdint.h>
 
+/*
+ * Protocol version. Incremented only on non-backwards-compatible
+ * changes.
+ */
+#define PROTOCOL_VERSION 0x111
+
 extern int usbip_port;
 extern char *usbip_port_string;
 extern char *usbip_srp_password;
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv3  10/16] staging: usbip: TLS for all userspace communication
  2013-09-28 17:42   ` [PATCHv3 01/16] staging: usbip: Add support for client authentication Dominik Paulus
                       ` (7 preceding siblings ...)
  2013-09-28 17:42     ` [PATCHv3 09/16] staging: usbip: Separate protocol/program version Dominik Paulus
@ 2013-09-28 17:42     ` Dominik Paulus
  2013-09-28 17:42     ` [PATCHv3 11/16] staging: usbip: Exchange session keys in userspace Dominik Paulus
                       ` (5 subsequent siblings)
  14 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-28 17:42 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: linux-kernel, Ilija Hadzic, Anthony Foiani, Masanari Iida,
	linux-kernel, devel, tobias.polzer, Dominik Paulus

This patch extends the TLS support to cover all communication in
userspace. The TLS connection is released shortly before the socket is
passed to the kernel.

This requires for additional connection state to be passed between
functions. We thus replaced the sockfd by a struct containing the TLS
context as well as the fd.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/src/usbip_attach.c |  24 +-
 drivers/staging/usbip/userspace/src/usbip_list.c   |  24 +-
 .../staging/usbip/userspace/src/usbip_network.c    | 279 +++++++++++++++++----
 .../staging/usbip/userspace/src/usbip_network.h    |  49 +++-
 drivers/staging/usbip/userspace/src/usbipd.c       | 244 ++++--------------
 5 files changed, 358 insertions(+), 262 deletions(-)

diff --git a/drivers/staging/usbip/userspace/src/usbip_attach.c b/drivers/staging/usbip/userspace/src/usbip_attach.c
index 651e93a..25c68e2 100644
--- a/drivers/staging/usbip/userspace/src/usbip_attach.c
+++ b/drivers/staging/usbip/userspace/src/usbip_attach.c
@@ -86,7 +86,8 @@ static int record_connection(char *host, char *port, char *busid, int rhport)
 	return 0;
 }
 
-static int import_device(int sockfd, struct usbip_usb_device *udev)
+static int import_device(struct usbip_connection *conn,
+			 struct usbip_usb_device *udev)
 {
 	int rc;
 	int port;
@@ -104,8 +105,10 @@ static int import_device(int sockfd, struct usbip_usb_device *udev)
 		return -1;
 	}
 
-	rc = usbip_vhci_attach_device(port, sockfd, udev->busnum,
+	usbip_net_bye(conn);
+	rc = usbip_vhci_attach_device(port, conn->sockfd, udev->busnum,
 				      udev->devnum, udev->speed);
+
 	if (rc < 0) {
 		err("import device");
 		usbip_vhci_driver_close();
@@ -117,7 +120,7 @@ static int import_device(int sockfd, struct usbip_usb_device *udev)
 	return port;
 }
 
-static int query_import_device(int sockfd, char *busid)
+static int query_import_device(struct usbip_connection *conn, char *busid)
 {
 	int rc;
 	struct op_import_request request;
@@ -128,7 +131,7 @@ static int query_import_device(int sockfd, char *busid)
 	memset(&reply, 0, sizeof(reply));
 
 	/* send a request */
-	rc = usbip_net_send_op_common(sockfd, OP_REQ_IMPORT, 0);
+	rc = usbip_net_send_op_common(conn, OP_REQ_IMPORT, 0);
 	if (rc < 0) {
 		err("send op_common");
 		return -1;
@@ -138,20 +141,20 @@ static int query_import_device(int sockfd, char *busid)
 
 	PACK_OP_IMPORT_REQUEST(0, &request);
 
-	rc = usbip_net_send(sockfd, (void *) &request, sizeof(request));
+	rc = usbip_net_send(conn, (void *) &request, sizeof(request));
 	if (rc < 0) {
 		err("send op_import_request");
 		return -1;
 	}
 
 	/* receive a reply */
-	rc = usbip_net_recv_op_common(sockfd, &code);
+	rc = usbip_net_recv_op_common(conn, &code);
 	if (rc < 0) {
 		err("recv op_common: %s", usbip_net_strerror(rc));
 		return -1;
 	}
 
-	rc = usbip_net_recv(sockfd, (void *) &reply, sizeof(reply));
+	rc = usbip_net_recv(conn, (void *) &reply, sizeof(reply));
 	if (rc < 0) {
 		err("recv op_import_reply");
 		return -1;
@@ -166,7 +169,7 @@ static int query_import_device(int sockfd, char *busid)
 	}
 
 	/* import a device */
-	return import_device(sockfd, &reply.udev);
+	return import_device(conn, &reply.udev);
 }
 
 static int attach_device(char *host, char *busid)
@@ -174,14 +177,15 @@ static int attach_device(char *host, char *busid)
 	int sockfd;
 	int rc;
 	int rhport;
+	struct usbip_connection conn;
 
-	sockfd = usbip_net_connect(host);
+	sockfd = usbip_net_connect(host, &conn);
 	if (sockfd < 0) {
 		err("connection attempt failed");
 		return -1;
 	}
 
-	rhport = query_import_device(sockfd, busid);
+	rhport = query_import_device(&conn, busid);
 	if (rhport < 0) {
 		err("query");
 		return -1;
diff --git a/drivers/staging/usbip/userspace/src/usbip_list.c b/drivers/staging/usbip/userspace/src/usbip_list.c
index ff7acf8..187eb7d 100644
--- a/drivers/staging/usbip/userspace/src/usbip_list.c
+++ b/drivers/staging/usbip/userspace/src/usbip_list.c
@@ -45,7 +45,7 @@ void usbip_list_usage(void)
 	printf("usage: %s", usbip_list_usage_string);
 }
 
-static int get_exported_devices(char *host, int sockfd)
+static int get_exported_devices(char *host, struct usbip_connection *conn)
 {
 	char product_name[100];
 	char class_name[100];
@@ -56,13 +56,13 @@ static int get_exported_devices(char *host, int sockfd)
 	unsigned int i;
 	int j, rc;
 
-	rc = usbip_net_send_op_common(sockfd, OP_REQ_DEVLIST, 0);
+	rc = usbip_net_send_op_common(conn, OP_REQ_DEVLIST, 0);
 	if (rc < 0) {
 		dbg("usbip_net_send_op_common failed");
 		return -1;
 	}
 
-	rc = usbip_net_recv_op_common(sockfd, &code);
+	rc = usbip_net_recv_op_common(conn, &code);
 	if (rc < 0) {
 		err("usbip_net_recv_op_common failed: %s",
 			usbip_net_strerror(rc));
@@ -70,7 +70,7 @@ static int get_exported_devices(char *host, int sockfd)
 	}
 
 	memset(&reply, 0, sizeof(reply));
-	rc = usbip_net_recv(sockfd, &reply, sizeof(reply));
+	rc = usbip_net_recv(conn, &reply, sizeof(reply));
 	if (rc < 0) {
 		dbg("usbip_net_recv_op_devlist failed");
 		return -1;
@@ -89,7 +89,7 @@ static int get_exported_devices(char *host, int sockfd)
 
 	for (i = 0; i < reply.ndev; i++) {
 		memset(&udev, 0, sizeof(udev));
-		rc = usbip_net_recv(sockfd, &udev, sizeof(udev));
+		rc = usbip_net_recv(conn, &udev, sizeof(udev));
 		if (rc < 0) {
 			dbg("usbip_net_recv failed: usbip_usb_device[%d]", i);
 			return -1;
@@ -106,7 +106,7 @@ static int get_exported_devices(char *host, int sockfd)
 		printf("%11s: %s\n", "", class_name);
 
 		for (j = 0; j < udev.bNumInterfaces; j++) {
-			rc = usbip_net_recv(sockfd, &uintf, sizeof(uintf));
+			rc = usbip_net_recv(conn, &uintf, sizeof(uintf));
 			if (rc < 0) {
 				dbg("usbip_net_recv failed: usbip_usb_intf[%d]",
 				    j);
@@ -130,23 +130,23 @@ static int get_exported_devices(char *host, int sockfd)
 static int list_exported_devices(char *host)
 {
 	int rc;
-	int sockfd;
+	struct usbip_connection conn;
 
-	sockfd = usbip_net_connect(host);
-	if (sockfd < 0) {
+	rc = usbip_net_connect(host, &conn);
+	if (rc < 0) {
 		err("could not connect to %s:%s: %s", host,
-		    usbip_port_string, gai_strerror(sockfd));
+		    usbip_port_string, usbip_net_strerror(rc));
 		return -1;
 	}
 	dbg("connected to %s:%s", host, usbip_port_string);
 
-	rc = get_exported_devices(host, sockfd);
+	rc = get_exported_devices(host, &conn);
 	if (rc < 0) {
 		err("failed to get device list from %s", host);
 		return -1;
 	}
 
-	close(sockfd);
+	usbip_net_bye(&conn);
 
 	return 0;
 }
diff --git a/drivers/staging/usbip/userspace/src/usbip_network.c b/drivers/staging/usbip/userspace/src/usbip_network.c
index a606e2b..22fa680 100644
--- a/drivers/staging/usbip/userspace/src/usbip_network.c
+++ b/drivers/staging/usbip/userspace/src/usbip_network.c
@@ -24,6 +24,7 @@
 #include <netdb.h>
 #include <netinet/tcp.h>
 #include <unistd.h>
+#include <assert.h>
 
 #ifdef HAVE_LIBWRAP
 #include <tcpd.h>
@@ -32,6 +33,7 @@
 #include "../config.h"
 #ifdef HAVE_GNUTLS
 #include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
 #endif
 
 #include "usbip_common.h"
@@ -112,8 +114,8 @@ void usbip_net_pack_usb_interface(int pack __attribute__((unused)),
 	/* uint8_t members need nothing */
 }
 
-static ssize_t usbip_net_xmit(int sockfd, void *buff, size_t bufflen,
-			      int sending)
+static ssize_t usbip_net_xmit(struct usbip_connection *conn, void *buff,
+			      size_t bufflen, int sending)
 {
 	ssize_t nbytes;
 	ssize_t total = 0;
@@ -122,10 +124,22 @@ static ssize_t usbip_net_xmit(int sockfd, void *buff, size_t bufflen,
 		return 0;
 
 	do {
-		if (sending)
-			nbytes = send(sockfd, buff, bufflen, 0);
+		if (!conn->have_crypto && sending)
+			nbytes = send(conn->sockfd, buff, bufflen, 0);
+		else if (!conn->have_crypto && !sending)
+			nbytes = recv(conn->sockfd, buff, bufflen, MSG_WAITALL);
+#ifdef HAVE_GNUTLS
+		else if (sending)
+			nbytes = gnutls_record_send(conn->session, buff, bufflen);
 		else
-			nbytes = recv(sockfd, buff, bufflen, MSG_WAITALL);
+			nbytes = gnutls_record_recv(conn->session, buff, bufflen);
+#else
+		/*
+		 * Assertion to let gcc be able to infer proper initialization
+		 * of nbytes.
+		 */
+		assert(!conn->have_crypto);
+#endif
 
 		if (nbytes <= 0)
 			return -1;
@@ -139,17 +153,20 @@ static ssize_t usbip_net_xmit(int sockfd, void *buff, size_t bufflen,
 	return total;
 }
 
-ssize_t usbip_net_recv(int sockfd, void *buff, size_t bufflen)
+ssize_t usbip_net_recv(struct usbip_connection *conn, void *buff,
+		       size_t bufflen)
 {
-	return usbip_net_xmit(sockfd, buff, bufflen, 0);
+	return usbip_net_xmit(conn, buff, bufflen, 0);
 }
 
-ssize_t usbip_net_send(int sockfd, void *buff, size_t bufflen)
+ssize_t usbip_net_send(struct usbip_connection *conn, void *buff,
+		       size_t bufflen)
 {
-	return usbip_net_xmit(sockfd, buff, bufflen, 1);
+	return usbip_net_xmit(conn, buff, bufflen, 1);
 }
 
-int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status)
+int usbip_net_send_op_common(struct usbip_connection *conn, uint32_t code,
+			     uint32_t status)
 {
 	struct op_common op_common;
 	int rc;
@@ -162,7 +179,7 @@ int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status)
 
 	PACK_OP_COMMON(1, &op_common);
 
-	rc = usbip_net_send(sockfd, &op_common, sizeof(op_common));
+	rc = usbip_net_send(conn, &op_common, sizeof(op_common));
 	if (rc < 0) {
 		dbg("usbip_net_send failed: %d", rc);
 		return -1;
@@ -171,14 +188,15 @@ int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status)
 	return 0;
 }
 
-int usbip_net_recv_op_common(int sockfd, uint16_t *code)
+
+int usbip_net_recv_op_common(struct usbip_connection *conn, uint16_t *code)
 {
 	struct op_common op_common;
 	int rc;
 
 	memset(&op_common, 0, sizeof(op_common));
 
-	rc = usbip_net_recv(sockfd, &op_common, sizeof(op_common));
+	rc = usbip_net_recv(conn, &op_common, sizeof(op_common));
 	if (rc < 0) {
 		dbg("usbip_net_recv failed: %d", rc);
 		return -ERR_SYSERR;
@@ -224,7 +242,8 @@ const char *usbip_net_strerror(int status)
 		/* ERR_AUTHREQ */ "Server requires authentication",
 		/* ERR_PERM */ "Permission denied",
 		/* ERR_NOTFOUND */ "Requested device not found",
-		/* ERR_NOAUTH */ "Server doesn't support authentication"
+		/* ERR_NOAUTH */ "Server doesn't support authentication",
+		/* ERR_INUSE */ "Requested device is already in use"
 	};
 	if (status < 0)
 		status = -status;
@@ -250,7 +269,8 @@ int usbip_net_set_nodelay(int sockfd)
 	const int val = 1;
 	int ret;
 
-	ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
+	ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &val,
+		sizeof(val));
 	if (ret < 0)
 		dbg("setsockopt: TCP_NODELAY");
 
@@ -329,82 +349,249 @@ int usbip_net_tcp_connect(char *hostname, char *service)
 }
 
 #ifdef HAVE_GNUTLS
-int usbip_net_srp_handshake(int sockfd)
+static gnutls_datum_t usbip_net_srp_salt, usbip_net_srp_verifier;
+static gnutls_srp_server_credentials_t usbip_net_srp_cred;
+
+#define SRP_GROUP gnutls_srp_2048_group_generator
+#define SRP_PRIME gnutls_srp_2048_group_prime
+
+int usbip_net_srp_client_handshake(struct usbip_connection *conn)
 {
 	int ret;
-	gnutls_session_t session;
-	gnutls_srp_client_credentials_t srp_cred;
 
-	ret = gnutls_srp_allocate_client_credentials(&srp_cred);
+	ret = gnutls_srp_allocate_client_credentials(&conn->srp_client_cred);
 	if (ret < 0)
 		return ret;
 
-	gnutls_srp_set_client_credentials(srp_cred, "dummyuser",
+	gnutls_srp_set_client_credentials(conn->srp_client_cred, "dummyuser",
 		usbip_srp_password);
 
-	ret = gnutls_init(&session, GNUTLS_CLIENT);
+	ret = gnutls_init(&conn->session, GNUTLS_CLIENT);
 	if (ret < 0) {
-		gnutls_srp_free_client_credentials(srp_cred);
+		gnutls_srp_free_client_credentials(conn->srp_client_cred);
 		return ret;
 	}
 
-	gnutls_priority_set_direct(session, "NORMAL:+SRP", NULL);
+	gnutls_priority_set_direct(conn->session, "NORMAL:+SRP", NULL);
 
-	gnutls_credentials_set(session, GNUTLS_CRD_SRP, srp_cred);
-	gnutls_transport_set_int (session, sockfd);
+	gnutls_credentials_set(conn->session, GNUTLS_CRD_SRP,
+			conn->srp_client_cred);
+	gnutls_transport_set_int (conn->session, conn->sockfd);
 
 	do {
-		ret = gnutls_handshake(session);
+		ret = gnutls_handshake(conn->session);
 	} while (ret < 0 && !gnutls_error_is_fatal(ret));
 
-	gnutls_bye(session, GNUTLS_SHUT_RDWR);
+	return ret;
+}
 
-	gnutls_deinit(session);
-	gnutls_srp_free_client_credentials(srp_cred);
+int usbip_net_srp_server_handshake(struct usbip_connection *conn)
+{
+	int ret;
+
+	if (gnutls_init(&conn->session, GNUTLS_SERVER) != 0)
+		return -1;
+	gnutls_priority_set_direct(conn->session, "NORMAL:-KX-ALL:+SRP", NULL);
+	if (gnutls_credentials_set(conn->session, GNUTLS_CRD_SRP,
+		usbip_net_srp_cred) != 0)
+		return -1;
+
+	gnutls_transport_set_int(conn->session, conn->sockfd);
+
+	do {
+		ret = gnutls_handshake(conn->session);
+	} while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+	if (ret < 0)
+		err("GnuTLS handshake failed (%s)", gnutls_strerror(ret));
+	else
+		info("GnuTLS handshake completed");
+
+	conn->have_crypto = 1;
 
 	return ret;
 }
+
+static int net_srp_callback(gnutls_session_t sess, const char *username,
+	gnutls_datum_t *nsalt, gnutls_datum_t *nverifier, gnutls_datum_t *g,
+	gnutls_datum_t *n)
+{
+	/*
+	 * GnuTLS expects us to allocate all data returned from callbacks
+	 * using gnutls_malloc(), thus, we have to create a fresh copy of
+	 * our static credentials for every connection.
+	 */
+	nsalt->data = gnutls_malloc(usbip_net_srp_salt.size);
+	nverifier->data = gnutls_malloc(usbip_net_srp_verifier.size);
+	if (nsalt->data == NULL || nverifier->data == NULL) {
+		gnutls_free(nsalt->data);
+		gnutls_free(nverifier->data);
+		return -1;
+	}
+	nsalt->size = usbip_net_srp_salt.size;
+	nverifier->size = usbip_net_srp_verifier.size;
+	memcpy(nverifier->data, usbip_net_srp_verifier.data,
+			usbip_net_srp_verifier.size);
+	memcpy(nsalt->data, usbip_net_srp_salt.data, usbip_net_srp_salt.size);
+
+	*g = SRP_GROUP;
+	*n = SRP_PRIME;
+
+	/* We only have a single session, thus, ignore it */
+	(void) sess;
+
+	if (strcmp(username, "dummyuser"))
+		/* User invalid, stored dummy data in g and n. */
+		return 1;
+
+	return 0;
+}
+
+int usbip_net_init_gnutls(void)
+{
+	int ret;
+
+	gnutls_global_init();
+
+	usbip_net_srp_salt.data = gnutls_malloc(16);
+	if (!usbip_net_srp_salt.data)
+		return GNUTLS_E_MEMORY_ERROR;
+
+	ret = gnutls_rnd(GNUTLS_RND_NONCE, usbip_net_srp_salt.data, 16);
+	if (ret < 0)
+		return ret;
+	usbip_net_srp_salt.size = 16;
+
+	ret = gnutls_srp_allocate_server_credentials(&usbip_net_srp_cred);
+	if (ret < 0)
+		return ret;
+
+	ret = gnutls_srp_verifier("dummyuser", optarg, &usbip_net_srp_salt,
+		&SRP_GROUP, &SRP_PRIME, &usbip_net_srp_verifier);
+	if (ret < 0)
+		return ret;
+
+	gnutls_srp_set_server_credentials_function(usbip_net_srp_cred,
+		net_srp_callback);
+
+	return GNUTLS_E_SUCCESS;
+}
 #endif
 
-/*
- * Connect to the server. Performs the TCP connection attempt
- * and - if necessary - the TLS handshake used for authentication.
- */
-int usbip_net_connect(char *hostname)
+#ifdef HAVE_LIBWRAP
+static int tcpd_auth(int connfd)
 {
-	int sockfd;
+	struct request_info request;
+	int rc;
+
+	request_init(&request, RQ_DAEMON, PROGNAME, RQ_FILE, connfd, 0);
+	fromhost(&request);
+	rc = hosts_access(&request);
+	if (rc == 0)
+		return -1;
+
+	return 0;
+}
+#endif
+
+int usbip_net_accept(int listenfd, struct usbip_connection *conn)
+{
+	int connfd;
+	struct sockaddr_storage ss;
+	socklen_t len = sizeof(ss);
+	char host[NI_MAXHOST], port[NI_MAXSERV];
+	int rc;
+
+	memset(&ss, 0, sizeof(ss));
+
+	connfd = accept(listenfd, (struct sockaddr *)&ss, &len);
+	if (connfd < 0) {
+		err("failed to accept connection");
+		return -1;
+	}
+
+	rc = getnameinfo((struct sockaddr *)&ss, len, host, sizeof(host),
+			 port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV);
+	if (rc)
+		err("getnameinfo: %s", gai_strerror(rc));
+
+#ifdef HAVE_LIBWRAP
+	rc = tcpd_auth(connfd);
+	if (rc < 0) {
+		info("denied access from %s", host);
+		close(connfd);
+		return -1;
+	}
+#endif
+	info("connection from %s, port %s", host, port);
+
+	conn->sockfd = connfd;
+	conn->have_crypto = 0;
+	conn->server = 1;
+
+	return 0;
+}
+
+int usbip_net_connect(char *hostname, struct usbip_connection *conn)
+{
+	conn->sockfd = usbip_net_tcp_connect(hostname, usbip_port_string);
+	if (conn->sockfd < 0) {
+		err("TCP connection attempt failed: %s",
+				gai_strerror(conn->sockfd));
+		return ERR_SYSERR;
+	}
 
-	sockfd = usbip_net_tcp_connect(hostname, usbip_port_string);
-	if (sockfd < 0)
-		return sockfd;
+	conn->have_crypto = 0;
+	conn->server = 0;
 
 #ifdef HAVE_GNUTLS
 	if (usbip_srp_password) {
 		int rc;
 		uint16_t code = OP_REP_STARTTLS;
 
-		rc = usbip_net_send_op_common(sockfd, OP_REQ_STARTTLS, 0);
+		rc = usbip_net_send_op_common(conn, OP_REQ_STARTTLS, 0);
 		if (rc < 0) {
+			close(conn->sockfd);
 			err("usbip_net_send_op_common failed");
-			return EAI_SYSTEM;
+			return ERR_SYSERR;
 		}
 
-		rc = usbip_net_recv_op_common(sockfd, &code);
+		rc = usbip_net_recv_op_common(conn, &code);
 		if (rc < 0) {
 			err("STARTTLS attempt failed: %s",
 				usbip_net_strerror(rc));
-			return -1;
+			close(conn->sockfd);
+			return ERR_SYSERR;
 		}
 
-		rc = usbip_net_srp_handshake(sockfd);
+		rc = usbip_net_srp_client_handshake(conn);
 		if (rc < 0) {
 			err("Unable to perform TLS handshake (wrong password?): %s",
 				gnutls_strerror(rc));
-			close(sockfd);
-			return EAI_SYSTEM;
+			close(conn->sockfd);
+			return ERR_SYSERR;
 		}
+
+		conn->have_crypto = 1;
 	}
 #endif
 
-	return sockfd;
+	return 0;
+}
+
+void usbip_net_bye(struct usbip_connection *conn)
+{
+#ifdef HAVE_GNUTLS
+	if (conn->have_crypto) {
+		gnutls_bye(conn->session, GNUTLS_SHUT_RDWR);
+
+		gnutls_deinit(conn->session);
+		if (!conn->server)
+			gnutls_srp_free_client_credentials(conn->srp_client_cred);
+
+		conn->have_crypto = 0;
+	}
+#else
+	(void)conn;
+#endif
 }
diff --git a/drivers/staging/usbip/userspace/src/usbip_network.h b/drivers/staging/usbip/userspace/src/usbip_network.h
index 6a41fd8..cdc6769 100644
--- a/drivers/staging/usbip/userspace/src/usbip_network.h
+++ b/drivers/staging/usbip/userspace/src/usbip_network.h
@@ -9,6 +9,10 @@
 #include "../config.h"
 #endif
 
+#ifdef HAVE_GNUTLS
+#include <gnutls/gnutls.h>
+#endif
+
 #include <sys/types.h>
 #include <sysfs/libsysfs.h>
 
@@ -25,6 +29,19 @@ extern char *usbip_port_string;
 extern char *usbip_srp_password;
 void usbip_setup_port_number(char *arg);
 
+/*
+ * Connection handle
+ */
+struct usbip_connection {
+#ifdef HAVE_GNUTLS
+	gnutls_session_t session;
+	gnutls_srp_client_credentials_t srp_client_cred;
+#endif
+	int have_crypto;
+	int sockfd;
+	int server;
+};
+
 /* ---------------------------------------------------------------------- */
 /* Common header for all the kinds of PDUs. */
 struct op_common {
@@ -44,6 +61,7 @@ struct op_common {
 #define ERR_PERM       0x06
 #define ERR_NOTFOUND   0x07
 #define ERR_NOAUTH     0x08
+#define ERR_INUSE      0x09
 	uint32_t status; /* op_code status (for reply) */
 
 } __attribute__((packed));
@@ -194,19 +212,40 @@ void usbip_net_pack_usb_device(int pack, struct usbip_usb_device *udev);
 void usbip_net_pack_usb_interface(int pack, struct usbip_usb_interface *uinf);
 const char *usbip_net_strerror(int status);
 
-ssize_t usbip_net_recv(int sockfd, void *buff, size_t bufflen);
-ssize_t usbip_net_send(int sockfd, void *buff, size_t bufflen);
-int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status);
+ssize_t usbip_net_recv(struct usbip_connection *conn, void *buff,
+		       size_t bufflen);
+ssize_t usbip_net_send(struct usbip_connection *conn, void *buff,
+		       size_t bufflen);
+int usbip_net_send_op_common(struct usbip_connection *conn, uint32_t code,
+			     uint32_t status);
 /*
  * Receive opcode.
  * Returns: 0 on success, negative error code (that may be passed to
  * usbip_net_strerror) on failure.
  */
-int usbip_net_recv_op_common(int sockfd, uint16_t *code);
+int usbip_net_recv_op_common(struct usbip_connection *conn, uint16_t *code);
 int usbip_net_set_reuseaddr(int sockfd);
 int usbip_net_set_nodelay(int sockfd);
 int usbip_net_set_keepalive(int sockfd);
 int usbip_net_set_v6only(int sockfd);
-int usbip_net_connect(char *hostname);
+/*
+ * Connect to the server. Performs the TCP connection attempt
+ * and - if necessary - the TLS handshake used for authentication.
+ *
+ * Newly generated connection parameters are stored in the - caller
+ * allocated - usbip_connection struct conn.
+ *
+ * Returns:
+ *	0 on success
+ *	negative error code on failure
+ */
+int usbip_net_connect(char *hostname, struct usbip_connection *conn);
+int usbip_net_accept(int listenfd, struct usbip_connection *conn);
+int usbip_net_srp_server_handshake(struct usbip_connection *conn);
+/*
+ * Shuts down the TLS connection, but leaves the socket intact
+ */
+void usbip_net_bye(struct usbip_connection *conn);
+int usbip_net_init_gnutls(void);
 
 #endif /* __USBIP_NETWORK_H */
diff --git a/drivers/staging/usbip/userspace/src/usbipd.c b/drivers/staging/usbip/userspace/src/usbipd.c
index 6550460..6bd97a0 100644
--- a/drivers/staging/usbip/userspace/src/usbipd.c
+++ b/drivers/staging/usbip/userspace/src/usbipd.c
@@ -59,6 +59,7 @@
 #define DEFAULT_PID_FILE "/var/run/" PROGNAME ".pid"
 
 static const char usbip_version_string[] = PACKAGE_STRING;
+static int need_auth;
 
 static const char usbipd_help_string[] =
 	"usage: usbipd [options]\n"
@@ -93,78 +94,6 @@ static const char usbipd_help_string[] =
 	"	-v, --version\n"
 	"		Show version.\n";
 
-static int need_auth;
-#ifdef HAVE_GNUTLS
-static gnutls_datum_t srp_salt, srp_verifier;
-static gnutls_srp_server_credentials_t srp_cred;
-
-#define SRP_GROUP gnutls_srp_2048_group_generator
-#define SRP_PRIME gnutls_srp_2048_group_prime
-
-static int net_srp_callback(gnutls_session_t sess, const char *username,
-	gnutls_datum_t *nsalt, gnutls_datum_t *nverifier, gnutls_datum_t *g,
-	gnutls_datum_t *n)
-{
-	/*
-	 * GnuTLS expects us to allocate all data returned from callbacks
-	 * using gnutls_malloc(), thus, we have to create a fresh copy of
-	 * our static credentials for every connection.
-	 */
-	nsalt->data = gnutls_malloc(srp_salt.size);
-	nverifier->data = gnutls_malloc(srp_verifier.size);
-	if (nsalt->data == NULL || nverifier->data == NULL) {
-		gnutls_free(nsalt->data);
-		gnutls_free(nverifier->data);
-		return -1;
-	}
-	nsalt->size = srp_salt.size;
-	nverifier->size = srp_verifier.size;
-	memcpy(nverifier->data, srp_verifier.data, srp_verifier.size);
-	memcpy(nsalt->data, srp_salt.data, srp_salt.size);
-
-	*g = SRP_GROUP;
-	*n = SRP_PRIME;
-
-	/* We only have a single session, thus, ignore it */
-	(void) sess;
-
-	if (strcmp(username, "dummyuser"))
-		/* User invalid, stored dummy data in g and n. */
-		return 1;
-
-	return 0;
-}
-
-static int net_srp_server_handshake(int connfd)
-{
-	int ret;
-	gnutls_session_t session;
-
-	if (gnutls_init(&session, GNUTLS_SERVER) != 0)
-		return -1;
-	gnutls_priority_set_direct(session, "NORMAL:-KX-ALL:+SRP", NULL);
-	if (gnutls_credentials_set(session, GNUTLS_CRD_SRP, srp_cred) != 0)
-		return -1;
-
-	gnutls_transport_set_int(session, connfd);
-
-	do {
-		ret = gnutls_handshake(session);
-	} while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
-
-	if (ret < 0)
-		err("GnuTLS handshake failed (%s)", gnutls_strerror(ret));
-	else
-		info("GnuTLS handshake completed");
-
-	if (gnutls_bye(session, GNUTLS_SHUT_RDWR) != 0)
-		err("Unable to shutdown TLS connection.");
-	gnutls_deinit(session);
-
-	return ret;
-}
-#endif
-
 static void usbipd_help(void)
 {
 	printf("%s\n", usbipd_help_string);
@@ -225,7 +154,7 @@ static int check_allowed(char *acls, int sockfd)
 	return match;
 }
 
-static int recv_request_import(int sockfd)
+static int recv_request_import(struct usbip_connection *conn)
 {
 	struct op_import_request req;
 	struct op_common reply;
@@ -240,7 +169,7 @@ static int recv_request_import(int sockfd)
 	memset(&req, 0, sizeof(req));
 	memset(&reply, 0, sizeof(reply));
 
-	rc = usbip_net_recv(sockfd, &req, sizeof(req));
+	rc = usbip_net_recv(conn, &req, sizeof(req));
 	if (rc < 0) {
 		dbg("usbip_net_recv failed: import request");
 		return -1;
@@ -258,12 +187,7 @@ static int recv_request_import(int sockfd)
 
 	if (found) {
 		/* should set TCP_NODELAY for usbip */
-		usbip_net_set_nodelay(sockfd);
-
-		/* export device needs a TCP/IP socket descriptor */
-		rc = usbip_host_export_device(edev, sockfd);
-		if (rc < 0)
-			error = ERR_SYSERR;
+		usbip_net_set_nodelay(conn->sockfd);
 
 		/* check for allowed IPs */
 		snprintf(ip_attr_path, sizeof(ip_attr_path), "%s/%s:%d.%d/%s",
@@ -276,7 +200,8 @@ static int recv_request_import(int sockfd)
 			if (rc < 0) {
 				err("Unable to open sysfs");
 				error = ERR_SYSERR;
-			} else if (check_allowed(usbip_acl->value, sockfd) != 1) {
+			} else if (check_allowed(usbip_acl->value,
+						conn->sockfd) != 1) {
 				info("Access denied to device %s",
 					edev->udev.busid);
 				error = ERR_PERM;
@@ -285,12 +210,22 @@ static int recv_request_import(int sockfd)
 		} else {
 			err("failed to get ip list");
 		}
+
+		/*
+		 * There is a race condition here: Other clients might
+		 * take it, as this check doesn't lock the device
+		 * However, this seems hardly avoidable here.
+		 */
+		if (edev->status != SDEV_ST_AVAILABLE) {
+			error = ERR_INUSE;
+			found = 0;
+		}
 	} else {
 		info("requested device not found: %s", req.busid);
 		error = ERR_NOTFOUND;
 	}
 
-	rc = usbip_net_send_op_common(sockfd, OP_REP_IMPORT, error);
+	rc = usbip_net_send_op_common(conn, OP_REP_IMPORT, error);
 	if (rc < 0) {
 		dbg("usbip_net_send_op_common failed: %#0x", OP_REP_IMPORT);
 		return -1;
@@ -304,18 +239,27 @@ static int recv_request_import(int sockfd)
 	memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev));
 	usbip_net_pack_usb_device(1, &pdu_udev);
 
-	rc = usbip_net_send(sockfd, &pdu_udev, sizeof(pdu_udev));
+	rc = usbip_net_send(conn, &pdu_udev, sizeof(pdu_udev));
 	if (rc < 0) {
 		dbg("usbip_net_send failed: devinfo");
 		return -1;
 	}
 
+	usbip_net_bye(conn);
+
+	/* export device needs a TCP/IP socket descriptor */
+	rc = usbip_host_export_device(edev, conn->sockfd);
+	if (rc < 0) {
+		err("usbip_host_export_device");
+		return -1;
+	}
+
 	dbg("import request busid %s: complete", req.busid);
 
 	return 0;
 }
 
-static int send_reply_devlist(int connfd)
+static int send_reply_devlist(struct usbip_connection *conn)
 {
 	struct usbip_exported_device *edev;
 	struct usbip_usb_device pdu_udev;
@@ -332,14 +276,14 @@ static int send_reply_devlist(int connfd)
 	}
 	info("exportable devices: %d", reply.ndev);
 
-	rc = usbip_net_send_op_common(connfd, OP_REP_DEVLIST, ERR_OK);
+	rc = usbip_net_send_op_common(conn, OP_REP_DEVLIST, ERR_OK);
 	if (rc < 0) {
 		dbg("usbip_net_send_op_common failed: %#0x", OP_REP_DEVLIST);
 		return -1;
 	}
 	PACK_OP_DEVLIST_REPLY(1, &reply);
 
-	rc = usbip_net_send(connfd, &reply, sizeof(reply));
+	rc = usbip_net_send(conn, &reply, sizeof(reply));
 	if (rc < 0) {
 		dbg("usbip_net_send failed: %#0x", OP_REP_DEVLIST);
 		return -1;
@@ -351,7 +295,7 @@ static int send_reply_devlist(int connfd)
 		memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev));
 		usbip_net_pack_usb_device(1, &pdu_udev);
 
-		rc = usbip_net_send(connfd, &pdu_udev, sizeof(pdu_udev));
+		rc = usbip_net_send(conn, &pdu_udev, sizeof(pdu_udev));
 		if (rc < 0) {
 			dbg("usbip_net_send failed: pdu_udev");
 			return -1;
@@ -362,7 +306,7 @@ static int send_reply_devlist(int connfd)
 			memcpy(&pdu_uinf, &edev->uinf[i], sizeof(pdu_uinf));
 			usbip_net_pack_usb_interface(1, &pdu_uinf);
 
-			rc = usbip_net_send(connfd, &pdu_uinf,
+			rc = usbip_net_send(conn, &pdu_uinf,
 					    sizeof(pdu_uinf));
 			if (rc < 0) {
 				dbg("usbip_net_send failed: pdu_uinf");
@@ -374,20 +318,20 @@ static int send_reply_devlist(int connfd)
 	return 0;
 }
 
-static int recv_request_devlist(int connfd)
+static int recv_request_devlist(struct usbip_connection *conn)
 {
 	struct op_devlist_request req;
 	int rc;
 
 	memset(&req, 0, sizeof(req));
 
-	rc = usbip_net_recv(connfd, &req, sizeof(req));
+	rc = usbip_net_recv(conn, &req, sizeof(req));
 	if (rc < 0) {
 		dbg("usbip_net_recv failed: devlist request");
 		return -1;
 	}
 
-	rc = send_reply_devlist(connfd);
+	rc = send_reply_devlist(conn);
 	if (rc < 0) {
 		dbg("send_reply_devlist failed");
 		return -1;
@@ -396,7 +340,7 @@ static int recv_request_devlist(int connfd)
 	return 0;
 }
 
-static int recv_pdu(int connfd)
+static int recv_pdu(struct usbip_connection *conn)
 {
 	int auth = !need_auth, cont = 1, ret;
 
@@ -413,19 +357,19 @@ static int recv_pdu(int connfd)
 	while (cont) {
 		uint16_t code = OP_UNSPEC;
 
-		ret = usbip_net_recv_op_common(connfd, &code);
+		ret = usbip_net_recv_op_common(conn, &code);
 		if (ret < 0) {
 			dbg("could not receive opcode: %#0x: %s", code,
 				usbip_net_strerror(ret));
 			return -1;
 		}
 
-		info("received request: %#0x(%d)", code, connfd);
+		info("received request: %#0x", code);
 
 		/* We require an authenticated encryption */
 		if (!auth && code != OP_REQ_STARTTLS) {
 			info("Unauthenticated connection attempt");
-			usbip_net_send_op_common(connfd, OP_REPLY, ERR_AUTHREQ);
+			usbip_net_send_op_common(conn, OP_REPLY, ERR_AUTHREQ);
 			return -1;
 		}
 
@@ -433,15 +377,16 @@ static int recv_pdu(int connfd)
 #ifdef HAVE_GNUTLS
 		case OP_REQ_STARTTLS:
 			if (!need_auth) {
-				usbip_net_send_op_common(connfd, OP_REPLY,
+				usbip_net_send_op_common(conn, OP_REPLY,
 					ERR_NOAUTH);
 				err("Unexpected TLS handshake attempt (client "
 					"uses password, server doesn't)");
 				ret = -1;
 			} else {
-				usbip_net_send_op_common(connfd, OP_REPLY,
+				usbip_net_send_op_common(conn, OP_REPLY,
 					ERR_OK);
-				ret = net_srp_server_handshake(connfd);
+				err("Starting handshake");
+				ret = usbip_net_srp_server_handshake(conn);
 				if (ret != 0)
 					err("TLS handshake failed");
 				auth = 1;
@@ -449,11 +394,11 @@ static int recv_pdu(int connfd)
 			break;
 #endif
 		case OP_REQ_DEVLIST:
-			ret = recv_request_devlist(connfd);
+			ret = recv_request_devlist(conn);
 			cont = 0;
 			break;
 		case OP_REQ_IMPORT:
-			ret = recv_request_import(connfd);
+			ret = recv_request_import(conn);
 			cont = 0;
 			break;
 		case OP_REQ_DEVINFO:
@@ -464,9 +409,9 @@ static int recv_pdu(int connfd)
 		}
 
 		if (ret == 0)
-			info("request %#0x(%d): complete", code, connfd);
+			info("request %#0x: complete", code);
 		else {
-			info("request %#0x(%d): failed", code, connfd);
+			info("request %#0x: failed", code);
 			break;
 		}
 	}
@@ -474,71 +419,23 @@ static int recv_pdu(int connfd)
 	return ret;
 }
 
-#ifdef HAVE_LIBWRAP
-static int tcpd_auth(int connfd)
-{
-	struct request_info request;
-	int rc;
-
-	request_init(&request, RQ_DAEMON, PROGNAME, RQ_FILE, connfd, 0);
-	fromhost(&request);
-	rc = hosts_access(&request);
-	if (rc == 0)
-		return -1;
-
-	return 0;
-}
-#endif
-
-static int do_accept(int listenfd)
-{
-	int connfd;
-	struct sockaddr_storage ss;
-	socklen_t len = sizeof(ss);
-	char host[NI_MAXHOST], port[NI_MAXSERV];
-	int rc;
-
-	memset(&ss, 0, sizeof(ss));
-
-	connfd = accept(listenfd, (struct sockaddr *)&ss, &len);
-	if (connfd < 0) {
-		err("failed to accept connection");
-		return -1;
-	}
-
-	rc = getnameinfo((struct sockaddr *)&ss, len, host, sizeof(host),
-			 port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV);
-	if (rc)
-		err("getnameinfo: %s", gai_strerror(rc));
-
-#ifdef HAVE_LIBWRAP
-	rc = tcpd_auth(connfd);
-	if (rc < 0) {
-		info("denied access from %s", host);
-		close(connfd);
-		return -1;
-	}
-#endif
-	info("connection from %s, port %s", host, port);
-
-	return connfd;
-}
-
 int process_request(int listenfd)
 {
 	pid_t childpid;
-	int connfd;
+	struct usbip_connection conn;
+	int rc;
 
-	connfd = do_accept(listenfd);
-	if (connfd < 0)
+	rc = usbip_net_accept(listenfd, &conn);
+	if (rc < 0)
 		return -1;
 	childpid = fork();
 	if (childpid == 0) {
 		close(listenfd);
-		recv_pdu(connfd);
+		recv_pdu(&conn);
+		usbip_net_bye(&conn);
 		exit(0);
 	}
-	close(connfd);
+	close(conn.sockfd);
 	return 0;
 }
 
@@ -775,37 +672,6 @@ static int do_standalone_mode(int daemonize, int ipv4, int ipv6)
 	return 0;
 }
 
-#ifdef HAVE_GNUTLS
-static int usbip_init_gnutls(void)
-{
-	int ret;
-
-	gnutls_global_init();
-
-	srp_salt.data = gnutls_malloc(16);
-	if (!srp_salt.data)
-		return GNUTLS_E_MEMORY_ERROR;
-
-	ret = gnutls_rnd(GNUTLS_RND_NONCE, srp_salt.data, 16);
-	if (ret < 0)
-		return ret;
-	srp_salt.size = 16;
-
-	ret = gnutls_srp_allocate_server_credentials(&srp_cred);
-	if (ret < 0)
-		return ret;
-
-	ret = gnutls_srp_verifier("dummyuser", optarg, &srp_salt, &SRP_GROUP,
-		&SRP_PRIME, &srp_verifier);
-	if (ret < 0)
-		return ret;
-
-	gnutls_srp_set_server_credentials_function(srp_cred, net_srp_callback);
-
-	return GNUTLS_E_SUCCESS;
-}
-#endif
-
 int main(int argc, char *argv[])
 {
 	static const struct option longopts[] = {
@@ -859,7 +725,7 @@ int main(int argc, char *argv[])
 		case 's':
 #ifdef HAVE_GNUTLS
 			need_auth = 1;
-			ret = usbip_init_gnutls();
+			ret = usbip_net_init_gnutls();
 			if (ret < 0) {
 				err("Unable to initialize GnuTLS: %s",
 					gnutls_strerror(ret));
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv3  11/16] staging: usbip: Exchange session keys in userspace
  2013-09-28 17:42   ` [PATCHv3 01/16] staging: usbip: Add support for client authentication Dominik Paulus
                       ` (8 preceding siblings ...)
  2013-09-28 17:42     ` [PATCHv3 10/16] staging: usbip: TLS for all userspace communication Dominik Paulus
@ 2013-09-28 17:42     ` Dominik Paulus
  2013-09-28 17:42     ` [PATCHv3 12/16] staging: usbip: Pass session keys to the kernel Dominik Paulus
                       ` (4 subsequent siblings)
  14 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-28 17:42 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: linux-kernel, Ilija Hadzic, Anthony Foiani, Masanari Iida,
	linux-kernel, devel, tobias.polzer, Dominik Paulus

In preparation for the kernel crypto support, we exchange two - randomly
generated - session keys between usbip and usbipd to be used for
encrypting all traffic generated in kernelspace. We use two different
128-bit keys, one for sending and one for receiving. Both are generated
by the client (usbip, probably has more entropy available than the
server) and transferred over the already established TLS connection.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 .../staging/usbip/userspace/libsrc/usbip_common.h  | 21 ++++++++++
 .../usbip/userspace/libsrc/usbip_host_driver.c     |  5 ++-
 .../usbip/userspace/libsrc/usbip_host_driver.h     |  3 +-
 .../staging/usbip/userspace/libsrc/vhci_driver.c   | 19 +++------
 .../staging/usbip/userspace/libsrc/vhci_driver.h   |  9 ++---
 drivers/staging/usbip/userspace/src/usbip_attach.c | 47 +++++++++++++++++++++-
 drivers/staging/usbip/userspace/src/usbipd.c       | 32 ++++++++++++---
 7 files changed, 105 insertions(+), 31 deletions(-)

diff --git a/drivers/staging/usbip/userspace/libsrc/usbip_common.h b/drivers/staging/usbip/userspace/libsrc/usbip_common.h
index 938ad1c..f804c04 100644
--- a/drivers/staging/usbip/userspace/libsrc/usbip_common.h
+++ b/drivers/staging/usbip/userspace/libsrc/usbip_common.h
@@ -127,6 +127,27 @@ struct usbip_usb_device {
 	uint8_t bNumInterfaces;
 } __attribute__((packed));
 
+
+/*
+ * These structs contain the configuration
+ * data to be passed to the kernel
+ */
+struct host_conf {
+	int sockfd;
+	uint8_t use_crypto;
+	uint8_t key1[16];
+	uint8_t key2[16];
+};
+struct vhci_conf {
+	uint8_t port;
+	int sockfd;
+	uint32_t devid;
+	uint32_t speed;
+	uint8_t use_crypto;
+	uint8_t key1[16];
+	uint8_t key2[16];
+};
+
 #define to_string(s)	#s
 
 void dump_usb_interface(struct usbip_usb_interface *);
diff --git a/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c b/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c
index 71a449c..dd93493 100644
--- a/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c
+++ b/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c
@@ -332,7 +332,8 @@ int usbip_host_refresh_device_list(void)
 	return 0;
 }
 
-int usbip_host_export_device(struct usbip_exported_device *edev, int sockfd)
+int usbip_host_export_device(struct usbip_exported_device *edev,
+			     struct host_conf *conf)
 {
 	char attr_name[] = "usbip_sockfd";
 	char attr_path[SYSFS_PATH_MAX];
@@ -366,7 +367,7 @@ int usbip_host_export_device(struct usbip_exported_device *edev, int sockfd)
 		return -1;
 	}
 
-	snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n", sockfd);
+	snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n", conf->sockfd);
 	dbg("write: %s", sockfd_buff);
 
 	ret = sysfs_write_attribute(attr, sockfd_buff, strlen(sockfd_buff));
diff --git a/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.h b/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.h
index 34fd14c..b76e41c 100644
--- a/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.h
+++ b/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.h
@@ -42,7 +42,8 @@ int usbip_host_driver_open(void);
 void usbip_host_driver_close(void);
 
 int usbip_host_refresh_device_list(void);
-int usbip_host_export_device(struct usbip_exported_device *edev, int sockfd);
+int usbip_host_export_device(struct usbip_exported_device *edev,
+			     struct host_conf *conf);
 struct usbip_exported_device *usbip_host_get_device(int num);
 
 #endif /* __USBIP_HOST_DRIVER_H */
diff --git a/drivers/staging/usbip/userspace/libsrc/vhci_driver.c b/drivers/staging/usbip/userspace/libsrc/vhci_driver.c
index 1091bb2..d1d45bb 100644
--- a/drivers/staging/usbip/userspace/libsrc/vhci_driver.c
+++ b/drivers/staging/usbip/userspace/libsrc/vhci_driver.c
@@ -467,8 +467,8 @@ int usbip_vhci_get_free_port(void)
 	return -1;
 }
 
-int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
-		uint32_t speed) {
+int usbip_vhci_attach_device(struct vhci_conf *conf)
+{
 	struct sysfs_attribute *attr_attach;
 	char buff[200]; /* what size should be ? */
 	int ret;
@@ -481,7 +481,7 @@ int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
 	}
 
 	snprintf(buff, sizeof(buff), "%u %u %u %u",
-			port, sockfd, devid, speed);
+			conf->port, conf->sockfd, conf->devid, conf->speed);
 	dbg("writing: %s", buff);
 
 	ret = sysfs_write_attribute(attr_attach, buff, strlen(buff));
@@ -490,25 +490,16 @@ int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
 		return -1;
 	}
 
-	dbg("attached port: %d", port);
+	dbg("attached port: %d", conf->port);
 
 	return 0;
 }
 
-static unsigned long get_devid(uint8_t busnum, uint8_t devnum)
+unsigned long get_devid(uint8_t busnum, uint8_t devnum)
 {
 	return (busnum << 16) | devnum;
 }
 
-/* will be removed */
-int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum,
-		uint8_t devnum, uint32_t speed)
-{
-	int devid = get_devid(busnum, devnum);
-
-	return usbip_vhci_attach_device2(port, sockfd, devid, speed);
-}
-
 int usbip_vhci_detach_device(uint8_t port)
 {
 	struct sysfs_attribute  *attr_detach;
diff --git a/drivers/staging/usbip/userspace/libsrc/vhci_driver.h b/drivers/staging/usbip/userspace/libsrc/vhci_driver.h
index 89949aa..325d0fa 100644
--- a/drivers/staging/usbip/userspace/libsrc/vhci_driver.h
+++ b/drivers/staging/usbip/userspace/libsrc/vhci_driver.h
@@ -55,13 +55,10 @@ int  usbip_vhci_refresh_device_list(void);
 
 
 int usbip_vhci_get_free_port(void);
-int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
-		uint32_t speed);
-
-/* will be removed */
-int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum,
-		uint8_t devnum, uint32_t speed);
+int usbip_vhci_attach_device(struct vhci_conf *conf);
 
 int usbip_vhci_detach_device(uint8_t port);
 
+unsigned long get_devid(uint8_t busnum, uint8_t devnum);
+
 #endif /* __VHCI_DRIVER_H */
diff --git a/drivers/staging/usbip/userspace/src/usbip_attach.c b/drivers/staging/usbip/userspace/src/usbip_attach.c
index 25c68e2..e339a05 100644
--- a/drivers/staging/usbip/userspace/src/usbip_attach.c
+++ b/drivers/staging/usbip/userspace/src/usbip_attach.c
@@ -29,6 +29,13 @@
 #include <unistd.h>
 #include <errno.h>
 
+#include "../config.h"
+
+#ifdef HAVE_GNUTLS
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#endif
+
 #include "vhci_driver.h"
 #include "usbip_common.h"
 #include "usbip_network.h"
@@ -91,6 +98,38 @@ static int import_device(struct usbip_connection *conn,
 {
 	int rc;
 	int port;
+	struct vhci_conf conf;
+
+	conf.use_crypto = 0;
+#ifdef HAVE_GNUTLS
+	if (conn->have_crypto) {
+		dbg("Generating session key and sending it to client");
+
+		rc = gnutls_rnd(GNUTLS_RND_RANDOM, conf.key1,
+				sizeof(conf.key1));
+		if (rc < 0) {
+			err("Session key generation failed: %s",
+					gnutls_strerror(rc));
+			return -1;
+		}
+		rc = gnutls_rnd(GNUTLS_RND_RANDOM, conf.key2,
+				sizeof(conf.key2));
+		if (rc < 0) {
+			err("Session key generation failed: %s",
+					gnutls_strerror(rc));
+			return -1;
+		}
+
+		if (usbip_net_send(conn, (void *) conf.key1, sizeof(conf.key1))
+				< 0 || usbip_net_send(conn, (void *) conf.key2,
+					sizeof(conf.key2)) < 0) {
+			err("Unable to send session key to client");
+			return -1;
+		}
+
+		conf.use_crypto = 1;
+	}
+#endif
 
 	rc = usbip_vhci_driver_open();
 	if (rc < 0) {
@@ -106,9 +145,13 @@ static int import_device(struct usbip_connection *conn,
 	}
 
 	usbip_net_bye(conn);
-	rc = usbip_vhci_attach_device(port, conn->sockfd, udev->busnum,
-				      udev->devnum, udev->speed);
 
+	conf.port = port;
+	conf.sockfd = conn->sockfd;
+	conf.devid = get_devid(udev->busnum, udev->devnum);
+	conf.speed = udev->speed;
+
+	rc = usbip_vhci_attach_device(&conf);
 	if (rc < 0) {
 		err("import device");
 		usbip_vhci_driver_close();
diff --git a/drivers/staging/usbip/userspace/src/usbipd.c b/drivers/staging/usbip/userspace/src/usbipd.c
index 6bd97a0..1a80bb6 100644
--- a/drivers/staging/usbip/userspace/src/usbipd.c
+++ b/drivers/staging/usbip/userspace/src/usbipd.c
@@ -245,13 +245,33 @@ static int recv_request_import(struct usbip_connection *conn)
 		return -1;
 	}
 
-	usbip_net_bye(conn);
+	if (found) {
+		struct host_conf conf = {
+			.sockfd = conn->sockfd,
+			.use_crypto = 0
+		};
 
-	/* export device needs a TCP/IP socket descriptor */
-	rc = usbip_host_export_device(edev, conn->sockfd);
-	if (rc < 0) {
-		err("usbip_host_export_device");
-		return -1;
+#ifdef HAVE_GNUTLS
+		if (conn->have_crypto) {
+			if (usbip_net_recv(conn, (void *) conf.key1,
+						sizeof(conf.key1)) < 0 ||
+					usbip_net_recv(conn, (void *) conf.key2,
+						sizeof(conf.key2)) < 0) {
+				err("Unable to receive session key");
+				return -1;
+			}
+			conf.use_crypto = 1;
+		}
+#endif
+
+		usbip_net_bye(conn);
+		/* export device needs a TCP/IP socket descriptor */
+		conf.sockfd = conn->sockfd;
+		rc = usbip_host_export_device(edev, &conf);
+		if (rc < 0) {
+			err("usbip_host_export_device");
+			return -1;
+		}
 	}
 
 	dbg("import request busid %s: complete", req.busid);
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv3  12/16] staging: usbip: Pass session keys to the kernel
  2013-09-28 17:42   ` [PATCHv3 01/16] staging: usbip: Add support for client authentication Dominik Paulus
                       ` (9 preceding siblings ...)
  2013-09-28 17:42     ` [PATCHv3 11/16] staging: usbip: Exchange session keys in userspace Dominik Paulus
@ 2013-09-28 17:42     ` Dominik Paulus
  2013-09-28 17:42     ` [PATCHv3 13/16] staging: usbip: Wrap kernel_sendmsg()/recvmsg() Dominik Paulus
                       ` (3 subsequent siblings)
  14 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-28 17:42 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: linux-kernel, Ilija Hadzic, Anthony Foiani, Masanari Iida,
	linux-kernel, devel, tobias.polzer, Dominik Paulus

This extends the userspace code to write the generated session keys to
sysfs in hexadecimal encoding after establishing the connection.
The kernel code is modified to parse the session keys.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/stub_dev.c                   | 26 +++++++++++++++++++-
 drivers/staging/usbip/usbip_common.h               | 10 ++++++++
 .../staging/usbip/userspace/libsrc/usbip_common.c  | 14 +++++++++++
 .../staging/usbip/userspace/libsrc/usbip_common.h  |  2 ++
 .../usbip/userspace/libsrc/usbip_host_driver.c     | 15 ++++++++----
 .../staging/usbip/userspace/libsrc/vhci_driver.c   |  8 ++++---
 drivers/staging/usbip/vhci_sysfs.c                 | 28 +++++++++++++++++++++-
 7 files changed, 94 insertions(+), 9 deletions(-)

diff --git a/drivers/staging/usbip/stub_dev.c b/drivers/staging/usbip/stub_dev.c
index 3939b60..0082912 100644
--- a/drivers/staging/usbip/stub_dev.c
+++ b/drivers/staging/usbip/stub_dev.c
@@ -87,13 +87,37 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
 	int sockfd = 0;
 	struct socket *socket;
 	ssize_t err = -EINVAL;
+	char sendkey[USBIP_KEYSIZE], recvkey[USBIP_KEYSIZE];
+	char sendkey_hex[2 * USBIP_KEYSIZE + 1];
+	char recvkey_hex[2 * USBIP_KEYSIZE + 1];
 
 	if (!sdev) {
 		dev_err(dev, "sdev is null\n");
 		return -ENODEV;
 	}
 
-	sscanf(buf, "%d", &sockfd);
+	/*
+	 * Read symmetric crypto keys. They are randomly
+	 * generated by userspace and passed to the kernel
+	 * via sysfs (encoded in hexadecimal)
+	 */
+	if (sscanf(buf, "%d %d %32s %32s", &sockfd, &sdev->ud.use_crypto,
+			sendkey_hex, recvkey_hex) < 1) {
+		dev_err(dev, "Invalid write to sysfs: Invalid sockfd\n");
+		return -EINVAL;
+	}
+	if (sdev->ud.use_crypto) {
+		int i;
+		dev_info(dev, "Using encrypted data transport\n");
+		for (i = USBIP_KEYSIZE - 1; i >= 0; --i) {
+			sendkey_hex[2 * (i + 1)] = 0;
+			sscanf(sendkey_hex + (2 * i), "%2hhX", &sendkey[i]);
+		}
+		for (i = USBIP_KEYSIZE - 1; i >= 0; --i) {
+			recvkey_hex[2 * (i + 1)] = 0;
+			sscanf(recvkey_hex + (2 * i), "%2hhX", &recvkey[i]);
+		}
+	}
 
 	if (sockfd != -1) {
 		dev_info(dev, "stub up\n");
diff --git a/drivers/staging/usbip/usbip_common.h b/drivers/staging/usbip/usbip_common.h
index 7e6c543..96c87ee 100644
--- a/drivers/staging/usbip/usbip_common.h
+++ b/drivers/staging/usbip/usbip_common.h
@@ -32,6 +32,13 @@
 
 #define USBIP_VERSION "1.0.0"
 
+/*
+ * Length of symmetric keys. Currently, this should be fixed at 16 bytes.
+ * Will break code if changed, look at userspace and stub_dev.c/vhci_sysfs.c
+ * where this constant is used before changing.
+ */
+#define USBIP_KEYSIZE 16
+
 #undef pr_fmt
 
 #ifdef DEBUG
@@ -290,6 +297,9 @@ struct usbip_device {
 		void (*reset)(struct usbip_device *);
 		void (*unusable)(struct usbip_device *);
 	} eh_ops;
+
+	/* Crypto support */
+	int use_crypto;
 };
 
 #define kthread_get_run(threadfn, data, namefmt, ...)			   \
diff --git a/drivers/staging/usbip/userspace/libsrc/usbip_common.c b/drivers/staging/usbip/userspace/libsrc/usbip_common.c
index 17e08e0..1ec9cc9 100644
--- a/drivers/staging/usbip/userspace/libsrc/usbip_common.c
+++ b/drivers/staging/usbip/userspace/libsrc/usbip_common.c
@@ -290,3 +290,17 @@ void usbip_names_get_class(char *buff, size_t size, uint8_t class,
 
 	snprintf(buff, size, "%s / %s / %s (%02x/%02x/%02x)", c, s, p, class, subclass, protocol);
 }
+
+/*
+ * Converts a 16-byte key to hexadecimal to be pushed to kernelspace
+ *
+ * Output buffer must be at least 33 bytes long
+ */
+char *keytohex(unsigned char *key, char *out)
+{
+	int i;
+	out[32] = 0;
+	for (i = 0; i != 16; ++i)
+		sprintf(out + (2 * i), "%02X", key[i]);
+	return out;
+}
diff --git a/drivers/staging/usbip/userspace/libsrc/usbip_common.h b/drivers/staging/usbip/userspace/libsrc/usbip_common.h
index f804c04..a5d4d21 100644
--- a/drivers/staging/usbip/userspace/libsrc/usbip_common.h
+++ b/drivers/staging/usbip/userspace/libsrc/usbip_common.h
@@ -168,4 +168,6 @@ void usbip_names_get_product(char *buff, size_t size, uint16_t vendor,
 void usbip_names_get_class(char *buff, size_t size, uint8_t class,
 			   uint8_t subclass, uint8_t protocol);
 
+char *keytohex(unsigned char *key, char *out);
+
 #endif /* __USBIP_COMMON_H */
diff --git a/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c b/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c
index dd93493..dfcb22d 100644
--- a/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c
+++ b/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c
@@ -338,7 +338,7 @@ int usbip_host_export_device(struct usbip_exported_device *edev,
 	char attr_name[] = "usbip_sockfd";
 	char attr_path[SYSFS_PATH_MAX];
 	struct sysfs_attribute *attr;
-	char sockfd_buff[30];
+	char sockfd_buff[512];
 	int ret;
 
 	if (edev->status != SDEV_ST_AVAILABLE) {
@@ -367,10 +367,17 @@ int usbip_host_export_device(struct usbip_exported_device *edev,
 		return -1;
 	}
 
-	snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n", conf->sockfd);
-	dbg("write: %s", sockfd_buff);
+	{
+		char key1[33], key2[33];
+		snprintf(sockfd_buff, sizeof(sockfd_buff), "%d %d %s %s\n",
+				conf->sockfd, conf->use_crypto,
+				keytohex(conf->key2, key2),
+				keytohex(conf->key1, key1));
+		dbg("write: %s", sockfd_buff);
+	}
 
-	ret = sysfs_write_attribute(attr, sockfd_buff, strlen(sockfd_buff));
+	ret = sysfs_write_attribute(attr, (const char *) sockfd_buff,
+			strlen(sockfd_buff));
 	if (ret < 0) {
 		dbg("sysfs_write_attribute failed: sockfd %s to %s",
 		    sockfd_buff, attr_path);
diff --git a/drivers/staging/usbip/userspace/libsrc/vhci_driver.c b/drivers/staging/usbip/userspace/libsrc/vhci_driver.c
index d1d45bb..66cdfaf 100644
--- a/drivers/staging/usbip/userspace/libsrc/vhci_driver.c
+++ b/drivers/staging/usbip/userspace/libsrc/vhci_driver.c
@@ -470,7 +470,7 @@ int usbip_vhci_get_free_port(void)
 int usbip_vhci_attach_device(struct vhci_conf *conf)
 {
 	struct sysfs_attribute *attr_attach;
-	char buff[200]; /* what size should be ? */
+	char buff[512], key1[33], key2[33];
 	int ret;
 
 	attr_attach = sysfs_get_device_attr(vhci_driver->hc_device, "attach");
@@ -480,8 +480,10 @@ int usbip_vhci_attach_device(struct vhci_conf *conf)
 		return -1;
 	}
 
-	snprintf(buff, sizeof(buff), "%u %u %u %u",
-			conf->port, conf->sockfd, conf->devid, conf->speed);
+	snprintf(buff, sizeof(buff), "%u %u %u %u %d %s %s",
+			conf->port, conf->sockfd, conf->devid, conf->speed,
+			conf->use_crypto, keytohex(conf->key1, key1),
+			keytohex(conf->key2, key2));
 	dbg("writing: %s", buff);
 
 	ret = sysfs_write_attribute(attr_attach, buff, strlen(buff));
diff --git a/drivers/staging/usbip/vhci_sysfs.c b/drivers/staging/usbip/vhci_sysfs.c
index 9b51586..1ef3f25 100644
--- a/drivers/staging/usbip/vhci_sysfs.c
+++ b/drivers/staging/usbip/vhci_sysfs.c
@@ -174,14 +174,40 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
 	struct socket *socket;
 	int sockfd = 0;
 	__u32 rhport = 0, devid = 0, speed = 0;
+	unsigned char sendkey[USBIP_KEYSIZE], recvkey[USBIP_KEYSIZE];
+	char sendkey_hex[2*USBIP_KEYSIZE+1], recvkey_hex[2*USBIP_KEYSIZE+1];
+	int use_crypto = 0;
 
 	/*
 	 * @rhport: port number of vhci_hcd
 	 * @sockfd: socket descriptor of an established TCP connection
 	 * @devid: unique device identifier in a remote host
 	 * @speed: usb device speed in a remote host
+	 * @use_crypto: Whether to use an encrypted connection
+	 * @recvkey_hex/@sendkey_hex: Hexadecimal encoded 128bit
+	 *              symmetric encryption keys to be used.
+	 *              Generated randomly for each connection
+	 *              by userspace.
 	 */
-	sscanf(buf, "%u %u %u %u", &rhport, &sockfd, &devid, &speed);
+	sendkey_hex[0] = 0;
+	recvkey_hex[0] = 0;
+	sscanf(buf, "%u %u %u %u %d %32s %32s", &rhport, &sockfd,
+			&devid, &speed, &use_crypto, sendkey_hex,
+			recvkey_hex);
+	if (use_crypto && strlen(sendkey_hex) == 32 &&
+			strlen(recvkey_hex) == 32) {
+		int i;
+		dev_info(dev, "Using encrypted data transport\n");
+		/* Decode decimal representation */
+		for (i = USBIP_KEYSIZE - 1; i >= 0; --i) {
+			sendkey_hex[2 * (i + 1)] = 0;
+			sscanf(sendkey_hex + (2 * i), "%2hhX", &sendkey[i]);
+		}
+		for (i = USBIP_KEYSIZE - 1; i >= 0; --i) {
+			recvkey_hex[2 * (i + 1)] = 0;
+			sscanf(recvkey_hex + (2 * i), "%2hhX", &recvkey[i]);
+		}
+	}
 
 	usbip_dbg_vhci_sysfs("rhport(%u) sockfd(%u) devid(%u) speed(%u)\n",
 			     rhport, sockfd, devid, speed);
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv3  13/16] staging: usbip: Wrap kernel_sendmsg()/recvmsg()
  2013-09-28 17:42   ` [PATCHv3 01/16] staging: usbip: Add support for client authentication Dominik Paulus
                       ` (10 preceding siblings ...)
  2013-09-28 17:42     ` [PATCHv3 12/16] staging: usbip: Pass session keys to the kernel Dominik Paulus
@ 2013-09-28 17:42     ` Dominik Paulus
  2013-09-30 12:42       ` Dan Carpenter
  2013-09-28 17:42     ` [PATCHv3 14/16] staging: usbip: Add encryption support to kernel Dominik Paulus
                       ` (2 subsequent siblings)
  14 siblings, 1 reply; 53+ messages in thread
From: Dominik Paulus @ 2013-09-28 17:42 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: linux-kernel, Ilija Hadzic, Anthony Foiani, Masanari Iida,
	linux-kernel, devel, tobias.polzer, Dominik Paulus

This adds two simple wrappers around kernel_sendmsg() and
kernel_recvmsg() that can be extended to perform additional
cryptographic operations on the data before sending it.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/stub_rx.c      |  2 +-
 drivers/staging/usbip/stub_tx.c      |  6 ++--
 drivers/staging/usbip/usbip_common.c | 66 ++++++++++++++++++++----------------
 drivers/staging/usbip/usbip_common.h |  7 +++-
 drivers/staging/usbip/vhci_rx.c      |  2 +-
 drivers/staging/usbip/vhci_tx.c      |  4 +--
 6 files changed, 48 insertions(+), 39 deletions(-)

diff --git a/drivers/staging/usbip/stub_rx.c b/drivers/staging/usbip/stub_rx.c
index db48a78..6ba9969 100644
--- a/drivers/staging/usbip/stub_rx.c
+++ b/drivers/staging/usbip/stub_rx.c
@@ -553,7 +553,7 @@ static void stub_rx_pdu(struct usbip_device *ud)
 	memset(&pdu, 0, sizeof(pdu));
 
 	/* receive a pdu header */
-	ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu));
+	ret = usbip_recv(ud, &pdu, sizeof(pdu));
 	if (ret != sizeof(pdu)) {
 		dev_err(dev, "recv a header, %d\n", ret);
 		usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
diff --git a/drivers/staging/usbip/stub_tx.c b/drivers/staging/usbip/stub_tx.c
index cd5326a..9a9f9e6 100644
--- a/drivers/staging/usbip/stub_tx.c
+++ b/drivers/staging/usbip/stub_tx.c
@@ -257,8 +257,7 @@ static int stub_send_ret_submit(struct stub_device *sdev)
 			iovnum++;
 		}
 
-		ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg,
-						iov,  iovnum, txsize);
+		ret = usbip_sendmsg(&sdev->ud, &msg, iov, iovnum, txsize);
 		if (ret != txsize) {
 			dev_err(&sdev->interface->dev,
 				"sendmsg failed!, retval %d for %zd\n",
@@ -332,8 +331,7 @@ static int stub_send_ret_unlink(struct stub_device *sdev)
 		iov[0].iov_len  = sizeof(pdu_header);
 		txsize += sizeof(pdu_header);
 
-		ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, iov,
-				     1, txsize);
+		ret = usbip_sendmsg(&sdev->ud, &msg, iov, 1, txsize);
 		if (ret != txsize) {
 			dev_err(&sdev->interface->dev,
 				"sendmsg failed!, retval %d for %zd\n",
diff --git a/drivers/staging/usbip/usbip_common.c b/drivers/staging/usbip/usbip_common.c
index e3fc749..83ec279 100644
--- a/drivers/staging/usbip/usbip_common.c
+++ b/drivers/staging/usbip/usbip_common.c
@@ -339,7 +339,7 @@ void usbip_dump_header(struct usbip_header *pdu)
 EXPORT_SYMBOL_GPL(usbip_dump_header);
 
 /* Receive data over TCP/IP. */
-int usbip_recv(struct socket *sock, void *buf, int size)
+int usbip_recv(struct usbip_device *ud, void *buf, int size)
 {
 	int result;
 	struct msghdr msg;
@@ -352,34 +352,29 @@ int usbip_recv(struct socket *sock, void *buf, int size)
 
 	usbip_dbg_xmit("enter\n");
 
-	if (!sock || !buf || !size) {
-		pr_err("invalid arg, sock %p buff %p size %d\n", sock, buf,
+	if (!ud || !buf || !size) {
+		pr_err("invalid arg, ud %p buff %p size %d\n", ud, buf,
 		       size);
 		return -EINVAL;
 	}
 
-	do {
-		sock->sk->sk_allocation = GFP_NOIO;
-		iov.iov_base    = buf;
-		iov.iov_len     = size;
-		msg.msg_name    = NULL;
-		msg.msg_namelen = 0;
-		msg.msg_control = NULL;
-		msg.msg_controllen = 0;
-		msg.msg_namelen    = 0;
-		msg.msg_flags      = MSG_NOSIGNAL;
-
-		result = kernel_recvmsg(sock, &msg, &iov, 1, size, MSG_WAITALL);
-		if (result <= 0) {
-			pr_debug("receive sock %p buf %p size %u ret %d total %d\n",
-				 sock, buf, size, result, total);
-			goto err;
-		}
-
-		size -= result;
-		buf += result;
-		total += result;
-	} while (size > 0);
+	ud->tcp_socket->sk->sk_allocation = GFP_NOIO;
+	iov.iov_base    = buf;
+	iov.iov_len     = size;
+	msg.msg_name    = NULL;
+	msg.msg_namelen = 0;
+	msg.msg_control = NULL;
+	msg.msg_controllen = 0;
+	msg.msg_namelen    = 0;
+	msg.msg_flags      = MSG_NOSIGNAL;
+
+	result = usbip_recvmsg(ud, &msg, &iov, 1, size, MSG_WAITALL);
+
+	if (result < 0) {
+		pr_debug("receive sock %p buf %p size %u ret %d total %d\n",
+			 ud->tcp_socket, buf, size, result, total);
+		return result;
+	}
 
 	if (usbip_dbg_flag_xmit) {
 		if (!in_interrupt())
@@ -393,9 +388,6 @@ int usbip_recv(struct socket *sock, void *buf, int size)
 			 osize, result, size, total);
 	}
 
-	return total;
-
-err:
 	return result;
 }
 EXPORT_SYMBOL_GPL(usbip_recv);
@@ -634,6 +626,20 @@ static void usbip_pack_iso(struct usbip_iso_packet_descriptor *iso,
 	}
 }
 
+int usbip_recvmsg(struct usbip_device *ud, struct msghdr *msg,
+		  struct kvec *vec, size_t num, size_t size, int flags)
+{
+	return kernel_recvmsg(ud->tcp_socket, msg, vec, num, size, flags);
+}
+EXPORT_SYMBOL_GPL(usbip_recvmsg);
+
+int usbip_sendmsg(struct usbip_device *ud, struct msghdr *msg,
+		  struct kvec *vec, size_t num, size_t size)
+{
+	return kernel_sendmsg(ud->tcp_socket, msg, vec, num, size);
+}
+EXPORT_SYMBOL_GPL(usbip_sendmsg);
+
 /* must free buffer */
 struct usbip_iso_packet_descriptor*
 usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen)
@@ -680,7 +686,7 @@ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb)
 	if (!buff)
 		return -ENOMEM;
 
-	ret = usbip_recv(ud->tcp_socket, buff, size);
+	ret = usbip_recv(ud, buff, size);
 	if (ret != size) {
 		dev_err(&urb->dev->dev, "recv iso_frame_descriptor, %d\n",
 			ret);
@@ -785,7 +791,7 @@ int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb)
 	if (!(size > 0))
 		return 0;
 
-	ret = usbip_recv(ud->tcp_socket, urb->transfer_buffer, size);
+	ret = usbip_recv(ud, urb->transfer_buffer, size);
 	if (ret != size) {
 		dev_err(&urb->dev->dev, "recv xbuf, %d\n", ret);
 		if (ud->side == USBIP_STUB) {
diff --git a/drivers/staging/usbip/usbip_common.h b/drivers/staging/usbip/usbip_common.h
index 96c87ee..bdad29f 100644
--- a/drivers/staging/usbip/usbip_common.h
+++ b/drivers/staging/usbip/usbip_common.h
@@ -323,7 +323,7 @@ struct usbip_device {
 void usbip_dump_urb(struct urb *purb);
 void usbip_dump_header(struct usbip_header *pdu);
 
-int usbip_recv(struct socket *sock, void *buf, int size);
+int usbip_recv(struct usbip_device *ui, void *buf, int size);
 struct socket *sockfd_to_socket(unsigned int sockfd);
 
 void usbip_pack_pdu(struct usbip_header *pdu, struct urb *urb, int cmd,
@@ -344,6 +344,11 @@ void usbip_stop_eh(struct usbip_device *ud);
 void usbip_event_add(struct usbip_device *ud, unsigned long event);
 int usbip_event_happened(struct usbip_device *ud);
 
+int usbip_recvmsg(struct usbip_device *ui, struct msghdr *msg,
+		  struct kvec *vec, size_t num, size_t size, int flags);
+int usbip_sendmsg(struct usbip_device *ui, struct msghdr *msg,
+		  struct kvec *vec, size_t num, size_t size);
+
 static inline int interface_to_busnum(struct usb_interface *interface)
 {
 	struct usb_device *udev = interface_to_usbdev(interface);
diff --git a/drivers/staging/usbip/vhci_rx.c b/drivers/staging/usbip/vhci_rx.c
index d07fcb5..34706a8 100644
--- a/drivers/staging/usbip/vhci_rx.c
+++ b/drivers/staging/usbip/vhci_rx.c
@@ -207,7 +207,7 @@ static void vhci_rx_pdu(struct usbip_device *ud)
 	memset(&pdu, 0, sizeof(pdu));
 
 	/* receive a pdu header */
-	ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu));
+	ret = usbip_recv(ud, &pdu, sizeof(pdu));
 	if (ret < 0) {
 		if (ret == -ECONNRESET)
 			pr_info("connection reset by peer\n");
diff --git a/drivers/staging/usbip/vhci_tx.c b/drivers/staging/usbip/vhci_tx.c
index 409fd99..09663e6 100644
--- a/drivers/staging/usbip/vhci_tx.c
+++ b/drivers/staging/usbip/vhci_tx.c
@@ -115,7 +115,7 @@ static int vhci_send_cmd_submit(struct vhci_device *vdev)
 			txsize += len;
 		}
 
-		ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 3, txsize);
+		ret = usbip_sendmsg(&vdev->ud, &msg, iov, 3, txsize);
 		if (ret != txsize) {
 			pr_err("sendmsg failed!, ret=%d for %zd\n", ret,
 			       txsize);
@@ -184,7 +184,7 @@ static int vhci_send_cmd_unlink(struct vhci_device *vdev)
 		iov[0].iov_len  = sizeof(pdu_header);
 		txsize += sizeof(pdu_header);
 
-		ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 1, txsize);
+		ret = usbip_sendmsg(&vdev->ud, &msg, iov, 1, txsize);
 		if (ret != txsize) {
 			pr_err("sendmsg failed!, ret=%d for %zd\n", ret,
 			       txsize);
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv3  14/16] staging: usbip: Add encryption support to kernel
  2013-09-28 17:42   ` [PATCHv3 01/16] staging: usbip: Add support for client authentication Dominik Paulus
                       ` (11 preceding siblings ...)
  2013-09-28 17:42     ` [PATCHv3 13/16] staging: usbip: Wrap kernel_sendmsg()/recvmsg() Dominik Paulus
@ 2013-09-28 17:42     ` Dominik Paulus
  2013-09-30 12:38       ` Dan Carpenter
  2013-09-28 17:42     ` [PATCHv3 15/16] staging: usbip: Update documentation Dominik Paulus
  2013-09-28 17:42     ` [PATCHv3 16/16] staging: usbip: Increment version number to 1.2.1 Dominik Paulus
  14 siblings, 1 reply; 53+ messages in thread
From: Dominik Paulus @ 2013-09-28 17:42 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: linux-kernel, Ilija Hadzic, Anthony Foiani, Masanari Iida,
	linux-kernel, devel, tobias.polzer, Dominik Paulus

This adds code performing the actual encryption and authentication
operations in the usbip kernel code. The whole data stream may now be
encrypted and authenticated with AES-GCM and symmetric 128 bit keys.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/Kconfig        |   2 +-
 drivers/staging/usbip/stub.h         |   3 +
 drivers/staging/usbip/stub_dev.c     |   8 +
 drivers/staging/usbip/usbip_common.c | 351 ++++++++++++++++++++++++++++++++++-
 drivers/staging/usbip/usbip_common.h |  22 +++
 drivers/staging/usbip/vhci_hcd.c     |   4 +-
 drivers/staging/usbip/vhci_sysfs.c   |  10 +
 7 files changed, 396 insertions(+), 4 deletions(-)

diff --git a/drivers/staging/usbip/Kconfig b/drivers/staging/usbip/Kconfig
index 8860009..87220af 100644
--- a/drivers/staging/usbip/Kconfig
+++ b/drivers/staging/usbip/Kconfig
@@ -1,6 +1,6 @@
 config USBIP_CORE
 	tristate "USB/IP support"
-	depends on USB && NET
+	depends on USB && NET && CRYPTO_GCM && CRYPTO_AES
 	default N
 	---help---
 	  This enables pushing USB packets over IP to allow remote
diff --git a/drivers/staging/usbip/stub.h b/drivers/staging/usbip/stub.h
index cfe75d1..2aaea3a 100644
--- a/drivers/staging/usbip/stub.h
+++ b/drivers/staging/usbip/stub.h
@@ -26,6 +26,9 @@
 #include <linux/types.h>
 #include <linux/usb.h>
 #include <linux/wait.h>
+#include <linux/crypto.h>
+#include <linux/err.h>
+#include <linux/scatterlist.h>
 
 #define STUB_BUSID_OTHER 0
 #define STUB_BUSID_REMOV 1
diff --git a/drivers/staging/usbip/stub_dev.c b/drivers/staging/usbip/stub_dev.c
index 0082912..45db24e 100644
--- a/drivers/staging/usbip/stub_dev.c
+++ b/drivers/staging/usbip/stub_dev.c
@@ -21,6 +21,7 @@
 #include <linux/file.h>
 #include <linux/kthread.h>
 #include <linux/module.h>
+#include <linux/kfifo.h>
 
 #include "usbip_common.h"
 #include "stub.h"
@@ -137,6 +138,12 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
 
 		spin_unlock_irq(&sdev->ud.lock);
 
+		if (sdev->ud.use_crypto) {
+			err = usbip_init_crypto(&sdev->ud, sendkey, recvkey);
+			if (err < 0)
+				goto err;
+		}
+
 		sdev->ud.tcp_rx = kthread_get_run(stub_rx_loop, &sdev->ud,
 						  "stub_rx");
 		sdev->ud.tcp_tx = kthread_get_run(stub_tx_loop, &sdev->ud,
@@ -298,6 +305,7 @@ static void stub_shutdown_connection(struct usbip_device *ud)
 	}
 
 	/* 3. free used data */
+	usbip_deinit_crypto(ud);
 	stub_device_cleanup_urbs(sdev);
 
 	/* 4. free stub_unlink */
diff --git a/drivers/staging/usbip/usbip_common.c b/drivers/staging/usbip/usbip_common.c
index 83ec279..58106bc 100644
--- a/drivers/staging/usbip/usbip_common.c
+++ b/drivers/staging/usbip/usbip_common.c
@@ -26,6 +26,8 @@
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <net/sock.h>
+#include <linux/scatterlist.h>
+#include <linux/crypto.h>
 
 #include "usbip_common.h"
 
@@ -626,17 +628,362 @@ static void usbip_pack_iso(struct usbip_iso_packet_descriptor *iso,
 	}
 }
 
+int usbip_init_crypto(struct usbip_device *ud, unsigned char *sendkey, unsigned
+		char *recvkey)
+{
+	int ret;
+
+	ud->use_crypto = 1;
+
+	ud->tfm_recv = crypto_alloc_aead("gcm(aes)", 0, 0);
+	if (IS_ERR(ud->tfm_recv))
+		return PTR_ERR(ud->tfm_recv);
+	ud->tfm_send = crypto_alloc_aead("gcm(aes)", 0, 0);
+	if (IS_ERR(ud->tfm_send)) {
+		ret = PTR_ERR(ud->tfm_send);
+		goto err_free_recv;
+	}
+	ret = kfifo_alloc(&ud->recv_queue, RECVQ_SIZE, GFP_KERNEL);
+	if (ret)
+		goto err_free_send;
+
+	if (crypto_aead_setkey(ud->tfm_send, sendkey, USBIP_KEYSIZE) ||
+	    crypto_aead_setkey(ud->tfm_recv, recvkey, USBIP_KEYSIZE) ||
+	    crypto_aead_setauthsize(ud->tfm_send, USBIP_AUTHSIZE) ||
+	    crypto_aead_setauthsize(ud->tfm_recv, USBIP_AUTHSIZE)) {
+		ret = -EINVAL;
+		goto err_free_fifo;
+	}
+
+	ud->ctr_send = 0;
+	ud->ctr_recv = 0;
+
+	return 0;
+
+err_free_fifo:
+	kfifo_free(&ud->recv_queue);
+err_free_send:
+	crypto_free_aead(ud->tfm_send);
+err_free_recv:
+	crypto_free_aead(ud->tfm_recv);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usbip_init_crypto);
+
+void usbip_deinit_crypto(struct usbip_device *ud)
+{
+	if (ud->use_crypto) {
+		crypto_free_aead(ud->tfm_send);
+		crypto_free_aead(ud->tfm_recv);
+		kfifo_free(&ud->recv_queue);
+		ud->use_crypto = 0;
+	}
+}
+EXPORT_SYMBOL_GPL(usbip_deinit_crypto);
+
+struct tcrypt_result {
+	struct completion completion;
+	int err;
+};
+
+static void tcrypt_complete(struct crypto_async_request *req, int err)
+{
+	struct tcrypt_result *res = req->data;
+
+	if (err == -EINPROGRESS)
+		return;
+
+	res->err = err;
+	complete(&res->completion);
+}
+
+#define USBIP_ENCRYPT 1
+#define USBIP_DECRYPT 0
+/*
+ * Perform encryption/decryption on one chunk of data.
+ * Uses global crypto state stored in usbip_device.
+ * Parameters:
+ * encrypt: USBIP_ENCRYPT to perform encryption, USBIP_DECRYPT to perform
+ * decryption
+ * packetsize: Size of the encrypted packet, including the authentication tag,
+ * not including the associated data (length field).
+ * plaintext and ciphertext have to be appropiately managed by the caller
+ * (i.e. they must be at least packetsize bytes long).
+ * Returns: 0 on success, negative error code on failure
+ */
+static int usbip_crypt(struct usbip_device *ud, int encrypt,
+		uint32_t packetsize, unsigned char *plaintext,
+		unsigned char *ciphertext)
+{
+	struct crypto_aead *tfm;
+	struct aead_request *req;
+	struct tcrypt_result result;
+	struct scatterlist plain, cipher, assoc;
+	char iv[16];
+	u64 *iv_num;
+	u64 iv_net;
+	const int plainsize = packetsize - USBIP_AUTHSIZE;
+	int ret;
+
+	/* Currently, this is guaranteed by the caller */
+	if (packetsize < USBIP_AUTHSIZE)
+		return -EINVAL;
+
+	memset(iv, 0, sizeof(iv));
+	if (encrypt) {
+		tfm = ud->tfm_send;
+		iv_num = &ud->ctr_send;
+	} else {
+		tfm = ud->tfm_recv;
+		iv_num = &ud->ctr_recv;
+	}
+	iv_net = cpu_to_be64(*iv_num);
+	memcpy(iv, &iv_net, sizeof(iv_net));
+
+	req = aead_request_alloc(tfm, GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	init_completion(&result.completion);
+	aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+				  tcrypt_complete, &result);
+
+	sg_init_one(&cipher, ciphertext, packetsize);
+	sg_init_one(&plain, plaintext, plainsize);
+	crypto_aead_clear_flags(tfm, ~0);
+
+	if (encrypt)
+		aead_request_set_crypt(req, &plain, &cipher, plainsize, iv);
+	else
+		aead_request_set_crypt(req, &cipher, &plain, packetsize, iv);
+	packetsize = cpu_to_be32(packetsize);
+	sg_init_one(&assoc, &packetsize, sizeof(packetsize));
+	/* Associated data: Unencrypted length tag */
+	aead_request_set_assoc(req, &assoc, sizeof(packetsize));
+
+	if (encrypt)
+		ret = crypto_aead_encrypt(req);
+	else
+		ret = crypto_aead_decrypt(req);
+
+	switch (ret) {
+	case 0: /* Success */
+		break;
+	case -EINPROGRESS:
+	case -EBUSY:
+		wait_for_completion(&result.completion);
+		ret = result.err;
+		break;
+	default:
+		aead_request_free(req);
+		return ret;
+	}
+
+	aead_request_free(req);
+
+	(*iv_num)++; /* Increment IV */
+
+	return ret;
+}
+
+/*
+ * Wrapper to kernel_recvmsg. If necessary, also does the necessary decryption.
+ * If decryption is enabled, you _MUST_ pass 1 as parameter for num, i.e.
+ * only receive into a single continuous buffer.
+ */
 int usbip_recvmsg(struct usbip_device *ud, struct msghdr *msg,
 		  struct kvec *vec, size_t num, size_t size, int flags)
 {
-	return kernel_recvmsg(ud->tcp_socket, msg, vec, num, size, flags);
+	int ret;
+	size_t total = 0;
+	unsigned char *plainbuf, *cipherbuf;
+
+	/* If crypto is disabled, we just wrap the normal kernel calls. */
+	if (!ud->use_crypto)
+		return kernel_recvmsg(ud->tcp_socket, msg, vec, num, size,
+				flags);
+
+	if (vec[0].iov_len < size)
+		return -EINVAL;
+	if (num != 1)
+		return -EINVAL;
+
+	plainbuf = kmalloc(USBIP_PACKETSIZE, GFP_KERNEL);
+	if (!plainbuf)
+		return PTR_ERR(plainbuf);
+	cipherbuf = kmalloc(USBIP_PACKETSIZE, GFP_KERNEL);
+	if (!cipherbuf) {
+		kfree(plainbuf);
+		return PTR_ERR(cipherbuf);
+	}
+
+	while (total < size) {
+		uint32_t packetsize;
+		struct kvec recvvec;
+
+		/*
+		 * We use a global kfifo to buffer unrequested plaintext bytes.
+		 * Flush this buffer first before receiving new data.
+		 */
+		if (kfifo_len(&ud->recv_queue)) {
+			size_t next = min_t(size_t, kfifo_len(&ud->recv_queue),
+					size - total);
+			/* No error checking necessary - see previous line */
+			ret = kfifo_out(&ud->recv_queue, vec[0].iov_base + total, next);
+			total += next;
+			continue;
+		}
+
+		/* See usbip_sendmsg() for the format of one encrypted packet */
+
+		/*
+		 * Receive size of next crypto packet
+		 */
+		recvvec.iov_base = &packetsize;
+		recvvec.iov_len = sizeof(packetsize);
+
+		ret = kernel_recvmsg(ud->tcp_socket, msg, &recvvec, 1,
+				sizeof(packetsize), flags);
+		packetsize = be32_to_cpu(packetsize);
+		if (ret < 0) {
+			total = ret;
+			goto err;
+		} else if (ret != sizeof(packetsize)) {
+			total = -EBADMSG;
+			goto err;
+		}
+
+		if (packetsize > USBIP_PACKETSIZE) {
+			total = -EBADMSG;
+			goto err;
+		}
+
+		/*
+		 * Receive the rest of the packet
+		 */
+		recvvec.iov_base = cipherbuf;
+		recvvec.iov_len = packetsize;
+		ret = kernel_recvmsg(ud->tcp_socket, msg, &recvvec, 1,
+				packetsize, flags);
+		if (ret <= 0) {
+			total = ret;
+			goto err;
+		} else if (ret != packetsize) {
+			total = -EBADMSG;
+			goto err;
+		}
+
+		/*
+		 * Decrypt the packet. This will also authenticate the length
+		 * field
+		 */
+		ret = usbip_crypt(ud, 0, packetsize, plainbuf, cipherbuf);
+		if (ret != 0) {
+			total = ret;
+			goto err;
+		}
+
+		/*
+		 * Add this packet to our global buffer (It will be stored in
+		 * the user buffer in the next loop iteration) No error
+		 * checking necessary - we already know the packet is going to
+		 * fit.
+		 */
+		(void) kfifo_in(&ud->recv_queue, plainbuf, packetsize -
+				USBIP_AUTHSIZE);
+	}
+
+err:
+	kfree(plainbuf);
+	kfree(cipherbuf);
+
+	return total;
 }
 EXPORT_SYMBOL_GPL(usbip_recvmsg);
 
 int usbip_sendmsg(struct usbip_device *ud, struct msghdr *msg,
 		  struct kvec *vec, size_t num, size_t size)
 {
-	return kernel_sendmsg(ud->tcp_socket, msg, vec, num, size);
+	int i = 0, ret, offset = 0;
+	size_t total = 0;
+	unsigned char *cipherbuf;
+
+	/* If crypto is disabled, we just wrap the normal kernel calls. */
+	if (!ud->use_crypto)
+		return kernel_sendmsg(ud->tcp_socket, msg, vec, num, size);
+
+	cipherbuf = kmalloc(USBIP_PACKETSIZE, GFP_KERNEL);
+	if (IS_ERR(cipherbuf))
+		return PTR_ERR(cipherbuf);
+
+	/*
+	 * The receiver has to decrypt whole packets. To avoid the need
+	 * to allocate large buffers at the receiving side, we split the
+	 * data to be sent in USBIP_PACKETSIZE large chunks that can be
+	 * decrypted separately. See below for the format of each chunk.
+	 */
+
+	/* Iterate over all kvecs, splitting them up as necessary. */
+	for (i = 0; i != num && size; ) {
+		/* Compute the remaining number of bytes to send for
+		 * this kvec */
+		const size_t PLAIN_SIZE = min_t(size_t, vec[i].iov_len -
+				offset, min_t(size_t, size, USBIP_PACKETSIZE -
+					USBIP_AUTHSIZE));
+		const size_t PACKET_SIZE = PLAIN_SIZE + USBIP_AUTHSIZE;
+		uint32_t packet_size_net = cpu_to_be32(PACKET_SIZE);
+		struct kvec sendvec[2];
+
+		if (PLAIN_SIZE == 0) {
+			++i;
+			offset = 0;
+			continue;
+		}
+
+		/*
+		 * One encrypted packet consists of:
+		 *  - An unencrypted, authenticated length tag (exactly 4
+		 *    bytes) containing the length of the packet.
+		 *  - Up to USBIP_PACKETSIZE - USBIP_AUTHSIZE bytes of user
+		 *    payload, encrypted
+		 *  - Exactly USBIP_AUTHSIZE bytes authentication tag.
+		 * Note: The packet length is also authenticated, but has
+		 * - for obvious reasons - to be sent in plaintext. This
+		 * packet format will be parsed by usbip_recvmsg (see above).
+		 */
+		ret = usbip_crypt(ud, 1, PACKET_SIZE, vec[i].iov_base + offset,
+				cipherbuf);
+		if (ret != 0) {
+			kfree(cipherbuf);
+			return ret;
+		}
+
+		/* Length field */
+		sendvec[0].iov_base = &packet_size_net;
+		sendvec[0].iov_len = sizeof(packet_size_net);
+		/* Payload and authentication tag */
+		sendvec[1].iov_base = cipherbuf;
+		sendvec[1].iov_len = PACKET_SIZE;
+		ret = kernel_sendmsg(ud->tcp_socket, msg, sendvec,
+				ARRAY_SIZE(sendvec), sendvec[0].iov_len +
+				sendvec[1].iov_len);
+		if (ret < 0) {
+			kfree(cipherbuf);
+			return ret;
+		}
+		if (ret != sendvec[0].iov_len + sendvec[1].iov_len) {
+			kfree(cipherbuf);
+			return -EPROTO;
+		}
+		offset += PLAIN_SIZE;
+		size -= PLAIN_SIZE;
+		total += PLAIN_SIZE;
+	}
+
+	kfree(cipherbuf);
+
+	return total;
 }
 EXPORT_SYMBOL_GPL(usbip_sendmsg);
 
diff --git a/drivers/staging/usbip/usbip_common.h b/drivers/staging/usbip/usbip_common.h
index bdad29f..939a232 100644
--- a/drivers/staging/usbip/usbip_common.h
+++ b/drivers/staging/usbip/usbip_common.h
@@ -29,15 +29,28 @@
 #include <linux/types.h>
 #include <linux/usb.h>
 #include <linux/wait.h>
+#include <linux/kfifo.h>
 
 #define USBIP_VERSION "1.0.0"
 
 /*
+ * Length of the authentication tag associated with each packet, in bytes. Can
+ * be set to 4, 8, 12, 13, 14, 15 or 16. See crypto_gcm_setauthsize in
+ * crypto/gcm.c. Increasing this will increase crypto protocol overhead.
+ */
+#define USBIP_AUTHSIZE 4
+/*
  * Length of symmetric keys. Currently, this should be fixed at 16 bytes.
  * Will break code if changed, look at userspace and stub_dev.c/vhci_sysfs.c
  * where this constant is used before changing.
  */
 #define USBIP_KEYSIZE 16
+/*
+ * Maximum size of encrypted packets. Decreasing this will increase overhead
+ * and decrease memory usage.
+ */
+#define USBIP_PACKETSIZE 1024
+#define RECVQ_SIZE (2*USBIP_PACKETSIZE)
 
 #undef pr_fmt
 
@@ -300,6 +313,11 @@ struct usbip_device {
 
 	/* Crypto support */
 	int use_crypto;
+	struct crypto_aead *tfm_recv;
+	struct crypto_aead *tfm_send;
+	/* Counters to be used as IVs */
+	u64 ctr_send, ctr_recv;
+	DECLARE_KFIFO_PTR(recv_queue, char);
 };
 
 #define kthread_get_run(threadfn, data, namefmt, ...)			   \
@@ -323,6 +341,10 @@ struct usbip_device {
 void usbip_dump_urb(struct urb *purb);
 void usbip_dump_header(struct usbip_header *pdu);
 
+int usbip_init_crypto(struct usbip_device *ud, unsigned char *sendkey,
+		unsigned char *recvkey);
+void usbip_deinit_crypto(struct usbip_device *ud);
+
 int usbip_recv(struct usbip_device *ui, void *buf, int size);
 struct socket *sockfd_to_socket(unsigned int sockfd);
 
diff --git a/drivers/staging/usbip/vhci_hcd.c b/drivers/staging/usbip/vhci_hcd.c
index e810ad5..55290c1 100644
--- a/drivers/staging/usbip/vhci_hcd.c
+++ b/drivers/staging/usbip/vhci_hcd.c
@@ -786,7 +786,9 @@ static void vhci_shutdown_connection(struct usbip_device *ud)
 		kthread_stop_put(vdev->ud.tcp_tx);
 		vdev->ud.tcp_tx = NULL;
 	}
-	pr_info("stop threads\n");
+	pr_info("stopped threads\n");
+
+	usbip_deinit_crypto(&vdev->ud);
 
 	/* active connection is closed */
 	if (vdev->ud.tcp_socket) {
diff --git a/drivers/staging/usbip/vhci_sysfs.c b/drivers/staging/usbip/vhci_sysfs.c
index 1ef3f25..ce46c16 100644
--- a/drivers/staging/usbip/vhci_sysfs.c
+++ b/drivers/staging/usbip/vhci_sysfs.c
@@ -20,6 +20,8 @@
 #include <linux/kthread.h>
 #include <linux/file.h>
 #include <linux/net.h>
+#include <linux/crypto.h>
+#include <linux/kfifo.h>
 
 #include "usbip_common.h"
 #include "vhci.h"
@@ -227,6 +229,13 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
 	/* begin a lock */
 	spin_lock(&the_controller->lock);
 	vdev = port_to_vdev(rhport);
+	if (use_crypto) {
+		int ret = usbip_init_crypto(&vdev->ud, sendkey, recvkey);
+		if (ret < 0) {
+			spin_unlock(&the_controller->lock);
+			return ret;
+		}
+	}
 	spin_lock(&vdev->ud.lock);
 
 	if (vdev->ud.status != VDEV_ST_NULL) {
@@ -237,6 +246,7 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
 		fput(socket->file);
 
 		dev_err(dev, "port %d already used\n", rhport);
+		usbip_deinit_crypto(&vdev->ud);
 		return -EINVAL;
 	}
 
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv3  15/16] staging: usbip: Update documentation
  2013-09-28 17:42   ` [PATCHv3 01/16] staging: usbip: Add support for client authentication Dominik Paulus
                       ` (12 preceding siblings ...)
  2013-09-28 17:42     ` [PATCHv3 14/16] staging: usbip: Add encryption support to kernel Dominik Paulus
@ 2013-09-28 17:42     ` Dominik Paulus
  2013-09-28 17:42     ` [PATCHv3 16/16] staging: usbip: Increment version number to 1.2.1 Dominik Paulus
  14 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-28 17:42 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: linux-kernel, Ilija Hadzic, Anthony Foiani, Masanari Iida,
	linux-kernel, devel, tobias.polzer, Dominik Paulus

From: Tobias Polzer <tobias.polzer@fau.de>

README was updated and cleaned. It now contains just one example, which
was updated to use encryption. Also, the new crypto behaviour is
documented. The usbip "port" command has been removed from the README,
as it isn't supported by newer userland versions. One dead link was
removed from the checklist section.

Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
---
 drivers/staging/usbip/userspace/README       | 72 +++++++++++-----------------
 drivers/staging/usbip/userspace/doc/usbipd.8 |  4 +-
 2 files changed, 31 insertions(+), 45 deletions(-)

diff --git a/drivers/staging/usbip/userspace/README b/drivers/staging/usbip/userspace/README
index 00a1658..01ea616 100644
--- a/drivers/staging/usbip/userspace/README
+++ b/drivers/staging/usbip/userspace/README
@@ -12,56 +12,53 @@
     - sysfsutils >= 2.0.0
 	sysfsutils library
 
-    - libwrap0-dev
+    - libwrap0-dev (optional)
 	tcp wrapper library
 
     - gcc >= 4.0
 
     - libtool, automake >= 1.9, autoconf >= 2.5.0, pkg-config
 
+    - libgnutls-dev >= 3.0 (libgnutls28-dev on debian) (optional)
+
 
 [Install]
     0. Generate configuration scripts.
 	$ ./autogen.sh
 
     1. Compile & install the userspace utilities.
-	$ ./configure [--with-tcp-wrappers=no] [--with-usbids-dir=<dir>]
+	$ ./configure [--with-tcp-wrappers=no] [--with-usbids-dir=<dir>] [--with-gnutls]
 	$ make install
 
     2. Compile & install USB/IP drivers.
 
 
 [Usage]
-    server:# (Physically attach your USB device.)
-
-    server:# insmod usbip-core.ko
-    server:# insmod usbip-host.ko
-
-    server:# usbipd -D
-	- Start usbip daemon.
-
-    server:# usbip list -l
-	- List driver assignments for USB devices.
 
-    server:# usbip bind --busid 1-2
-	- Bind usbip-host.ko to the device with busid 1-2.
-	- The USB device 1-2 is now exportable to other hosts!
-	- Use `usbip unbind --busid 1-2' to stop exporting the device.
+See usbip(8) and usbipd(8).
 
-    client:# insmod usbip-core.ko
-    client:# insmod vhci-hcd.ko
+[Security considerations]
+By default, all of the usbip network traffic is unencrypted and
+unauthenticated. As it is mostly parsed in staging quality kernel code, you
+should use usbip in this mode only in absolutely trusted environments.
 
-    client:# usbip list --remote <host>
-	- List exported USB devices on the <host>.
+In addition to the usual methods for secure network tunneling - SSH tunnels,
+IPsec, etc. -, usbip version 1.2.1 supports out-of-the-box crypto for all of
+the network traffic. This requires usbip to be compiled with GnuTLS enabled
+(configure switch: --with-gnutls). Crypto support can be enabled by using
+password authentication. If the --auth flag is set, usbip will not only
+authenticate client and server using the shared passphrase, but also encrypt
+and authenticate all of the following traffic. For the userspace traffic,
+GnuTLS is used, the kernel traffic is encrypted and authenticated using AES-GCM
+with 128bit keys. The session keys are randomly generated and exchanged for
+in userspace for each connection.
 
-    client:# usbip attach --remote <host> --busid 1-2
-	- Connect the remote USB device.
-
-    client:# usbip port
-	- Show virtual port status.
-
-    client:# usbip detach --port <port>
-	- Detach the USB device.
+The encryption support has been designed to offer perfect forward secrecy and
+decent security even if using rather weak passwords. Strong passwords are
+mainly needed to provide proper authorization (this is still important, see
+above!) and to secure against man-in-the-middle attacks.
+However, the crypto code still lacks complete review and code auditing. Do not
+rely on it for strong security.
 
 
 [Example]
@@ -72,7 +69,7 @@ Physically attach your USB devices to this host.
 
     trois:# insmod path/to/usbip-core.ko
     trois:# insmod path/to/usbip-host.ko
-    trois:# usbipd -D
+    trois:# usbipd -sVerySecret -D
 
 In another terminal, let's look up what USB devices are physically
 attached to this host.
@@ -135,7 +132,7 @@ exportable on the host.
     deux:# insmod path/to/usbip-core.ko
     deux:# insmod path/to/vhci-hcd.ko
 
-    deux:# usbip list --remote 10.0.0.3
+    deux:# usbip --auth VerySecret list --remote 10.0.0.3
     Exportable USB devices
     ======================
      - 10.0.0.3
@@ -163,20 +160,9 @@ exportable on the host.
 
 Attach a remote USB device:
 
-    deux:# usbip attach --remote 10.0.0.3 --busid 1-1
+    deux:# usbip --auth VerySecret attach --remote 10.0.0.3 --busid 1-1
     port 0 attached
 
-Show the devices attached to this client:
-
-    deux:# usbip port
-    Port 00: <Port in Use> at Full Speed(12Mbps)
-	   Prolific Technology, Inc. : unknown product (067b:3507)
-	   6-1 -> usbip://10.0.0.3:3240/1-1  (remote bus/dev 001/004)
-	   6-1:1.0 used by usb-storage
-			  /sys/class/scsi_device/0:0:0:0/device
-			  /sys/class/scsi_host/host0/device
-			  /sys/block/sda/device
-
 Detach the imported device:
 
     deux:# usbip detach --port 0
@@ -184,8 +170,6 @@ Detach the imported device:
 
 
 [Checklist]
-    - See 'Debug Tips' on the project wiki.
-	- http://usbip.wiki.sourceforge.net/how-to-debug-usbip
     - usbip-host.ko must be bound to the target device.
 	- See /proc/bus/usb/devices and find "Driver=..." lines of the device.
     - Shutdown firewall.
diff --git a/drivers/staging/usbip/userspace/doc/usbipd.8 b/drivers/staging/usbip/userspace/doc/usbipd.8
index 8beb95a..3fcd9ed 100644
--- a/drivers/staging/usbip/userspace/doc/usbipd.8
+++ b/drivers/staging/usbip/userspace/doc/usbipd.8
@@ -56,7 +56,9 @@ Listen on TCP/IP port PORT.
 .HP
 \fB\-s\fR, \fB\-\-auth\fR
 .IP
-Sets the password to be used for client authentication. If -a is used, the server will only accept connections from authenticated clients. Note: USB traffic will still be unencrypted, this currently only serves for authentication.
+Sets the password to be used for client authentication. If -a is used, the
+server will only accept connections from authenticated clients. This will also
+encrypt the whole usbip traffic, including kernel traffic, using 128bit AES.
 .PP
 
 \fB\-h\fR, \fB\-\-help\fR
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv3  16/16] staging: usbip: Increment version number to 1.2.1
  2013-09-28 17:42   ` [PATCHv3 01/16] staging: usbip: Add support for client authentication Dominik Paulus
                       ` (13 preceding siblings ...)
  2013-09-28 17:42     ` [PATCHv3 15/16] staging: usbip: Update documentation Dominik Paulus
@ 2013-09-28 17:42     ` Dominik Paulus
  14 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-09-28 17:42 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: linux-kernel, Ilija Hadzic, Anthony Foiani, Masanari Iida,
	linux-kernel, devel, tobias.polzer, Dominik Paulus

Also increment the kernel module version number to match the
userspace version, as compatibility with old userspace utilities
is now at least partially broken.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/usbip_common.h         | 2 +-
 drivers/staging/usbip/userspace/configure.ac | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/staging/usbip/usbip_common.h b/drivers/staging/usbip/usbip_common.h
index 939a232..b315082 100644
--- a/drivers/staging/usbip/usbip_common.h
+++ b/drivers/staging/usbip/usbip_common.h
@@ -31,7 +31,7 @@
 #include <linux/wait.h>
 #include <linux/kfifo.h>
 
-#define USBIP_VERSION "1.0.0"
+#define USBIP_VERSION "1.2.1"
 
 /*
  * Length of the authentication tag associated with each packet, in bytes. Can
diff --git a/drivers/staging/usbip/userspace/configure.ac b/drivers/staging/usbip/userspace/configure.ac
index bc08b81..26dabe3 100644
--- a/drivers/staging/usbip/userspace/configure.ac
+++ b/drivers/staging/usbip/userspace/configure.ac
@@ -1,7 +1,7 @@
 dnl Process this file with autoconf to produce a configure script.
 
 AC_PREREQ(2.59)
-AC_INIT([usbip-utils], [1.1.1], [linux-usb@vger.kernel.org])
+AC_INIT([usbip-utils], [1.2.1], [linux-usb@vger.kernel.org])
 
 CURRENT=0
 REVISION=1
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCHv3  02/16] staging: usbip: Add kernel support for client ACLs
  2013-09-28 17:42     ` [PATCHv3 02/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
@ 2013-09-30  8:29       ` Dan Carpenter
  0 siblings, 0 replies; 53+ messages in thread
From: Dan Carpenter @ 2013-09-30  8:29 UTC (permalink / raw)
  To: Dominik Paulus
  Cc: Greg Kroah-Hartman, Anthony Foiani, devel, linux-kernel,
	Kurt Kanzenbach, linux-kernel, tobias.polzer, Ilija Hadzic

The main thing is that there is an IS_ERR vs NULL check.  The rest is
just style nits.

On Sat, Sep 28, 2013 at 07:42:39PM +0200, Dominik Paulus wrote:
> +static ssize_t store_acl(struct device *dev, struct device_attribute *attr,
> +			const char *buf, size_t count)
> +{
> +	struct stub_device *sdev = dev_get_drvdata(dev);
> +	int retval = 0;
> +
> +	if (!sdev)
> +		return -ENODEV;

Can this even happen?  If not, don't check for impossible conditions it
is sloppy.

> +
> +	if (count >= PAGE_SIZE)
> +		/* Prevent storing oversized ACLs in kernel memory */
> +		return -EINVAL;
> +

Put parenthesis around multi-line indents.  But in this case just delete
the obvious comment.

> +	/* Store ACL */

Delete obvious comment.

> +	spin_lock_irq(&sdev->ip_lock);
> +	kfree(sdev->acls);
> +	sdev->acls = kstrdup(buf, GFP_KERNEL);

kstrdup() returns a NULL on error not an error pointer.  Configure vim
to use cscope and run make cscope.

> +	if (IS_ERR(sdev->acls)) {
> +		retval = PTR_ERR(sdev->acls);
> +		sdev->acls = NULL;
> +	} else {
> +		retval = strlen(sdev->acls);
> +	}
> +	spin_unlock_irq(&sdev->ip_lock);
> +
> +	return retval;
> +}
> +
> +/*
> + * This functions prints all allowed IP addrs for this dev
> + */
> +static ssize_t show_acl(struct device *dev, struct device_attribute *attr,
> +		       char *buf)
> +{
> +	struct stub_device *sdev = dev_get_drvdata(dev);
> +	int retval = 0;

Don't initialize things if you don't use the value.  (It stops GCC from
warning about uninitialized variables properly).

> +
> +	if (!sdev)
> +		return -ENODEV;
> +
> +	spin_lock_irq(&sdev->ip_lock);
> +	if (sdev->acls == NULL) {
> +		retval = 0;
> +	} else {
> +		strcpy(buf, sdev->acls);
> +		retval = strlen(buf);
> +	}
> +	spin_unlock_irq(&sdev->ip_lock);
> +
> +	return retval;
> +}
> +static DEVICE_ATTR(usbip_acl, S_IWUSR | S_IRUGO, show_acl, store_acl);
> +
>  static int stub_add_files(struct device *dev)
>  {
>  	int err = 0;
> @@ -157,9 +213,13 @@ static int stub_add_files(struct device *dev)
>  	err = device_create_file(dev, &dev_attr_usbip_debug);
>  	if (err)
>  		goto err_debug;
> +	err = device_create_file(dev, &dev_attr_usbip_acl);
> +	if (err)
> +		goto err_ip;
>  
>  	return 0;
> -
> +err_ip:
> +	device_remove_file(dev, &dev_attr_usbip_debug);
>  err_debug:
>  	device_remove_file(dev, &dev_attr_usbip_sockfd);
>  err_sockfd:

I wasn't able to figure out what the "ip" part of "err_ip" meant...

Really these are all poor label names.  They are based on the goto
location instead of the label location.

> @@ -173,6 +233,7 @@ static void stub_remove_files(struct device *dev)
>  	device_remove_file(dev, &dev_attr_usbip_status);
>  	device_remove_file(dev, &dev_attr_usbip_sockfd);
>  	device_remove_file(dev, &dev_attr_usbip_debug);
> +	device_remove_file(dev, &dev_attr_usbip_acl);
>  }
>  
>  static void stub_shutdown_connection(struct usbip_device *ud)
> @@ -306,12 +367,14 @@ static struct stub_device *stub_device_alloc(struct usb_device *udev,
>  	sdev->ud.status		= SDEV_ST_AVAILABLE;
>  	spin_lock_init(&sdev->ud.lock);
>  	sdev->ud.tcp_socket	= NULL;
> +	sdev->acls = NULL;

This isn't really needed because we allocated sdev with kzalloc().


>  
>  	INIT_LIST_HEAD(&sdev->priv_init);
>  	INIT_LIST_HEAD(&sdev->priv_tx);
>  	INIT_LIST_HEAD(&sdev->priv_free);
>  	INIT_LIST_HEAD(&sdev->unlink_free);
>  	INIT_LIST_HEAD(&sdev->unlink_tx);
> +	spin_lock_init(&sdev->ip_lock);
>  	spin_lock_init(&sdev->priv_lock);
>  
>  	init_waitqueue_head(&sdev->tx_waitq);
> @@ -511,6 +574,9 @@ static void stub_disconnect(struct usb_interface *interface)
>  	usb_put_dev(sdev->udev);
>  	usb_put_intf(interface);
>  
> +	/* free ACL list */
> +	kfree(sdev->acls);
> +
>  	/* free sdev */
>  	busid_priv->sdev = NULL;
>  	stub_device_free(sdev);
> -- 
> 1.8.4
> 
> _______________________________________________
> devel mailing list
> devel@linuxdriverproject.org
> http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCHv3  14/16] staging: usbip: Add encryption support to kernel
  2013-09-28 17:42     ` [PATCHv3 14/16] staging: usbip: Add encryption support to kernel Dominik Paulus
@ 2013-09-30 12:38       ` Dan Carpenter
  2013-10-19 14:39         ` [PATCHv4 00/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
  0 siblings, 1 reply; 53+ messages in thread
From: Dan Carpenter @ 2013-09-30 12:38 UTC (permalink / raw)
  To: Dominik Paulus
  Cc: Greg Kroah-Hartman, Anthony Foiani, devel, linux-kernel,
	linux-kernel, tobias.polzer, Ilija Hadzic

There are still some checking ERR_PTR instead of NULL and some wrong
return codes left over.

On Sat, Sep 28, 2013 at 07:42:51PM +0200, Dominik Paulus wrote:
> This adds code performing the actual encryption and authentication
> operations in the usbip kernel code. The whole data stream may now be
> encrypted and authenticated with AES-GCM and symmetric 128 bit keys.
> 
> Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
> Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
> ---
>  drivers/staging/usbip/Kconfig        |   2 +-
>  drivers/staging/usbip/stub.h         |   3 +
>  drivers/staging/usbip/stub_dev.c     |   8 +
>  drivers/staging/usbip/usbip_common.c | 351 ++++++++++++++++++++++++++++++++++-
>  drivers/staging/usbip/usbip_common.h |  22 +++
>  drivers/staging/usbip/vhci_hcd.c     |   4 +-
>  drivers/staging/usbip/vhci_sysfs.c   |  10 +
>  7 files changed, 396 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/staging/usbip/Kconfig b/drivers/staging/usbip/Kconfig
> index 8860009..87220af 100644
> --- a/drivers/staging/usbip/Kconfig
> +++ b/drivers/staging/usbip/Kconfig
> @@ -1,6 +1,6 @@
>  config USBIP_CORE
>  	tristate "USB/IP support"
> -	depends on USB && NET
> +	depends on USB && NET && CRYPTO_GCM && CRYPTO_AES
>  	default N
>  	---help---
>  	  This enables pushing USB packets over IP to allow remote
> diff --git a/drivers/staging/usbip/stub.h b/drivers/staging/usbip/stub.h
> index cfe75d1..2aaea3a 100644
> --- a/drivers/staging/usbip/stub.h
> +++ b/drivers/staging/usbip/stub.h
> @@ -26,6 +26,9 @@
>  #include <linux/types.h>
>  #include <linux/usb.h>
>  #include <linux/wait.h>
> +#include <linux/crypto.h>
> +#include <linux/err.h>
> +#include <linux/scatterlist.h>
>  
>  #define STUB_BUSID_OTHER 0
>  #define STUB_BUSID_REMOV 1
> diff --git a/drivers/staging/usbip/stub_dev.c b/drivers/staging/usbip/stub_dev.c
> index 0082912..45db24e 100644
> --- a/drivers/staging/usbip/stub_dev.c
> +++ b/drivers/staging/usbip/stub_dev.c
> @@ -21,6 +21,7 @@
>  #include <linux/file.h>
>  #include <linux/kthread.h>
>  #include <linux/module.h>
> +#include <linux/kfifo.h>
>  
>  #include "usbip_common.h"
>  #include "stub.h"
> @@ -137,6 +138,12 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
>  
>  		spin_unlock_irq(&sdev->ud.lock);
>  
> +		if (sdev->ud.use_crypto) {
> +			err = usbip_init_crypto(&sdev->ud, sendkey, recvkey);
> +			if (err < 0)
> +				goto err;
> +		}
> +
>  		sdev->ud.tcp_rx = kthread_get_run(stub_rx_loop, &sdev->ud,
>  						  "stub_rx");
>  		sdev->ud.tcp_tx = kthread_get_run(stub_tx_loop, &sdev->ud,
> @@ -298,6 +305,7 @@ static void stub_shutdown_connection(struct usbip_device *ud)
>  	}
>  
>  	/* 3. free used data */
> +	usbip_deinit_crypto(ud);
>  	stub_device_cleanup_urbs(sdev);
>  
>  	/* 4. free stub_unlink */
> diff --git a/drivers/staging/usbip/usbip_common.c b/drivers/staging/usbip/usbip_common.c
> index 83ec279..58106bc 100644
> --- a/drivers/staging/usbip/usbip_common.c
> +++ b/drivers/staging/usbip/usbip_common.c
> @@ -26,6 +26,8 @@
>  #include <linux/module.h>
>  #include <linux/moduleparam.h>
>  #include <net/sock.h>
> +#include <linux/scatterlist.h>
> +#include <linux/crypto.h>
>  
>  #include "usbip_common.h"
>  
> @@ -626,17 +628,362 @@ static void usbip_pack_iso(struct usbip_iso_packet_descriptor *iso,
>  	}
>  }
>  
> +int usbip_init_crypto(struct usbip_device *ud, unsigned char *sendkey, unsigned
> +		char *recvkey)
> +{
> +	int ret;
> +
> +	ud->use_crypto = 1;
> +
> +	ud->tfm_recv = crypto_alloc_aead("gcm(aes)", 0, 0);
> +	if (IS_ERR(ud->tfm_recv))
> +		return PTR_ERR(ud->tfm_recv);
> +	ud->tfm_send = crypto_alloc_aead("gcm(aes)", 0, 0);
> +	if (IS_ERR(ud->tfm_send)) {
> +		ret = PTR_ERR(ud->tfm_send);
> +		goto err_free_recv;
> +	}
> +	ret = kfifo_alloc(&ud->recv_queue, RECVQ_SIZE, GFP_KERNEL);
> +	if (ret)
> +		goto err_free_send;
> +
> +	if (crypto_aead_setkey(ud->tfm_send, sendkey, USBIP_KEYSIZE) ||
> +	    crypto_aead_setkey(ud->tfm_recv, recvkey, USBIP_KEYSIZE) ||
> +	    crypto_aead_setauthsize(ud->tfm_send, USBIP_AUTHSIZE) ||
> +	    crypto_aead_setauthsize(ud->tfm_recv, USBIP_AUTHSIZE)) {
> +		ret = -EINVAL;
> +		goto err_free_fifo;
> +	}
> +
> +	ud->ctr_send = 0;
> +	ud->ctr_recv = 0;
> +
> +	return 0;
> +
> +err_free_fifo:
> +	kfifo_free(&ud->recv_queue);
> +err_free_send:
> +	crypto_free_aead(ud->tfm_send);
> +err_free_recv:
> +	crypto_free_aead(ud->tfm_recv);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(usbip_init_crypto);
> +
> +void usbip_deinit_crypto(struct usbip_device *ud)
> +{
> +	if (ud->use_crypto) {
> +		crypto_free_aead(ud->tfm_send);
> +		crypto_free_aead(ud->tfm_recv);
> +		kfifo_free(&ud->recv_queue);
> +		ud->use_crypto = 0;
> +	}
> +}
> +EXPORT_SYMBOL_GPL(usbip_deinit_crypto);
> +
> +struct tcrypt_result {
> +	struct completion completion;
> +	int err;
> +};
> +
> +static void tcrypt_complete(struct crypto_async_request *req, int err)
> +{
> +	struct tcrypt_result *res = req->data;
> +
> +	if (err == -EINPROGRESS)
> +		return;
> +
> +	res->err = err;
> +	complete(&res->completion);
> +}
> +
> +#define USBIP_ENCRYPT 1
> +#define USBIP_DECRYPT 0
> +/*
> + * Perform encryption/decryption on one chunk of data.
> + * Uses global crypto state stored in usbip_device.
> + * Parameters:
> + * encrypt: USBIP_ENCRYPT to perform encryption, USBIP_DECRYPT to perform
> + * decryption
> + * packetsize: Size of the encrypted packet, including the authentication tag,
> + * not including the associated data (length field).
> + * plaintext and ciphertext have to be appropiately managed by the caller
> + * (i.e. they must be at least packetsize bytes long).
> + * Returns: 0 on success, negative error code on failure
> + */
> +static int usbip_crypt(struct usbip_device *ud, int encrypt,
> +		uint32_t packetsize, unsigned char *plaintext,
> +		unsigned char *ciphertext)
> +{
> +	struct crypto_aead *tfm;
> +	struct aead_request *req;
> +	struct tcrypt_result result;
> +	struct scatterlist plain, cipher, assoc;
> +	char iv[16];
> +	u64 *iv_num;
> +	u64 iv_net;
> +	const int plainsize = packetsize - USBIP_AUTHSIZE;
> +	int ret;
> +
> +	/* Currently, this is guaranteed by the caller */
> +	if (packetsize < USBIP_AUTHSIZE)
> +		return -EINVAL;
> +
> +	memset(iv, 0, sizeof(iv));
> +	if (encrypt) {
> +		tfm = ud->tfm_send;
> +		iv_num = &ud->ctr_send;
> +	} else {
> +		tfm = ud->tfm_recv;
> +		iv_num = &ud->ctr_recv;
> +	}
> +	iv_net = cpu_to_be64(*iv_num);
> +	memcpy(iv, &iv_net, sizeof(iv_net));
> +
> +	req = aead_request_alloc(tfm, GFP_KERNEL);
> +	if (!req)
> +		return -ENOMEM;
> +
> +	init_completion(&result.completion);
> +	aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
> +				  tcrypt_complete, &result);
> +
> +	sg_init_one(&cipher, ciphertext, packetsize);
> +	sg_init_one(&plain, plaintext, plainsize);
> +	crypto_aead_clear_flags(tfm, ~0);
> +
> +	if (encrypt)
> +		aead_request_set_crypt(req, &plain, &cipher, plainsize, iv);
> +	else
> +		aead_request_set_crypt(req, &cipher, &plain, packetsize, iv);
> +	packetsize = cpu_to_be32(packetsize);
> +	sg_init_one(&assoc, &packetsize, sizeof(packetsize));
> +	/* Associated data: Unencrypted length tag */
> +	aead_request_set_assoc(req, &assoc, sizeof(packetsize));
> +
> +	if (encrypt)
> +		ret = crypto_aead_encrypt(req);
> +	else
> +		ret = crypto_aead_decrypt(req);
> +
> +	switch (ret) {
> +	case 0: /* Success */
> +		break;
> +	case -EINPROGRESS:
> +	case -EBUSY:
> +		wait_for_completion(&result.completion);
> +		ret = result.err;
> +		break;
> +	default:
> +		aead_request_free(req);
> +		return ret;
> +	}
> +
> +	aead_request_free(req);
> +
> +	(*iv_num)++; /* Increment IV */
> +
> +	return ret;
> +}
> +
> +/*
> + * Wrapper to kernel_recvmsg. If necessary, also does the necessary decryption.
> + * If decryption is enabled, you _MUST_ pass 1 as parameter for num, i.e.
> + * only receive into a single continuous buffer.
> + */
>  int usbip_recvmsg(struct usbip_device *ud, struct msghdr *msg,
>  		  struct kvec *vec, size_t num, size_t size, int flags)
>  {
> -	return kernel_recvmsg(ud->tcp_socket, msg, vec, num, size, flags);
> +	int ret;
> +	size_t total = 0;
> +	unsigned char *plainbuf, *cipherbuf;
> +
> +	/* If crypto is disabled, we just wrap the normal kernel calls. */

Don't comment on obvious stuff.  It's distracting.

> +	if (!ud->use_crypto)
> +		return kernel_recvmsg(ud->tcp_socket, msg, vec, num, size,
> +				flags);
> +
> +	if (vec[0].iov_len < size)
> +		return -EINVAL;
> +	if (num != 1)
> +		return -EINVAL;
> +
> +	plainbuf = kmalloc(USBIP_PACKETSIZE, GFP_KERNEL);
> +	if (!plainbuf)
> +		return PTR_ERR(plainbuf);

		return -ENOMEM;

> +	cipherbuf = kmalloc(USBIP_PACKETSIZE, GFP_KERNEL);
> +	if (!cipherbuf) {
> +		kfree(plainbuf);
> +		return PTR_ERR(cipherbuf);

		return -ENOMEM;

> +	}
> +
> +	while (total < size) {
> +		uint32_t packetsize;
> +		struct kvec recvvec;
> +
> +		/*
> +		 * We use a global kfifo to buffer unrequested plaintext bytes.
> +		 * Flush this buffer first before receiving new data.
> +		 */
> +		if (kfifo_len(&ud->recv_queue)) {
> +			size_t next = min_t(size_t, kfifo_len(&ud->recv_queue),
> +					size - total);
> +			/* No error checking necessary - see previous line */
> +			ret = kfifo_out(&ud->recv_queue, vec[0].iov_base + total, next);
> +			total += next;
> +			continue;
> +		}
> +
> +		/* See usbip_sendmsg() for the format of one encrypted packet */
> +
> +		/*
> +		 * Receive size of next crypto packet
> +		 */
> +		recvvec.iov_base = &packetsize;
> +		recvvec.iov_len = sizeof(packetsize);
> +
> +		ret = kernel_recvmsg(ud->tcp_socket, msg, &recvvec, 1,
> +				sizeof(packetsize), flags);
> +		packetsize = be32_to_cpu(packetsize);
> +		if (ret < 0) {
> +			total = ret;
> +			goto err;
> +		} else if (ret != sizeof(packetsize)) {
> +			total = -EBADMSG;
> +			goto err;
> +		}
> +
> +		if (packetsize > USBIP_PACKETSIZE) {
> +			total = -EBADMSG;
> +			goto err;
> +		}
> +
> +		/*
> +		 * Receive the rest of the packet
> +		 */
> +		recvvec.iov_base = cipherbuf;
> +		recvvec.iov_len = packetsize;
> +		ret = kernel_recvmsg(ud->tcp_socket, msg, &recvvec, 1,
> +				packetsize, flags);
> +		if (ret <= 0) {
> +			total = ret;
> +			goto err;
> +		} else if (ret != packetsize) {
> +			total = -EBADMSG;
> +			goto err;
> +		}
> +
> +		/*
> +		 * Decrypt the packet. This will also authenticate the length
> +		 * field
> +		 */
> +		ret = usbip_crypt(ud, 0, packetsize, plainbuf, cipherbuf);
> +		if (ret != 0) {

I personally would prefer just "if (ret) {".  This complaint is
throughout.  "!= 0" is a double negative.

> +			total = ret;
> +			goto err;
> +		}
> +
> +		/*
> +		 * Add this packet to our global buffer (It will be stored in
> +		 * the user buffer in the next loop iteration) No error
> +		 * checking necessary - we already know the packet is going to
> +		 * fit.
> +		 */
> +		(void) kfifo_in(&ud->recv_queue, plainbuf, packetsize -
> +				USBIP_AUTHSIZE);
> +	}
> +
> +err:
> +	kfree(plainbuf);
> +	kfree(cipherbuf);
> +
> +	return total;
>  }
>  EXPORT_SYMBOL_GPL(usbip_recvmsg);
>  
>  int usbip_sendmsg(struct usbip_device *ud, struct msghdr *msg,
>  		  struct kvec *vec, size_t num, size_t size)
>  {
> -	return kernel_sendmsg(ud->tcp_socket, msg, vec, num, size);
> +	int i = 0, ret, offset = 0;
> +	size_t total = 0;
> +	unsigned char *cipherbuf;
> +
> +	/* If crypto is disabled, we just wrap the normal kernel calls. */
> +	if (!ud->use_crypto)
> +		return kernel_sendmsg(ud->tcp_socket, msg, vec, num, size);
> +
> +	cipherbuf = kmalloc(USBIP_PACKETSIZE, GFP_KERNEL);
> +	if (IS_ERR(cipherbuf))
> +		return PTR_ERR(cipherbuf);

	if (!cipherbuf)
		return -ENOMEM;

> +
> +	/*
> +	 * The receiver has to decrypt whole packets. To avoid the need
> +	 * to allocate large buffers at the receiving side, we split the
> +	 * data to be sent in USBIP_PACKETSIZE large chunks that can be
> +	 * decrypted separately. See below for the format of each chunk.
> +	 */
> +
> +	/* Iterate over all kvecs, splitting them up as necessary. */
> +	for (i = 0; i != num && size; ) {
> +		/* Compute the remaining number of bytes to send for
> +		 * this kvec */
> +		const size_t PLAIN_SIZE = min_t(size_t, vec[i].iov_len -
> +				offset, min_t(size_t, size, USBIP_PACKETSIZE -
> +					USBIP_AUTHSIZE));

I don't understand why PLAIN_SIZE is all caps or why it is const.  This
whole assignment is a bit jumbled and hard to parse.

If we remove the const that gains us 6 characters.  Since
"vec[i].iov_len - offset" is size_t type then the outside min_t could be
changed to "min".  We could use a #define to shorten
"USBIP_PACKETSIZE - USBIP_AUTHSIZE".

#define USBIP_DATASIZE (USBIP_PACKETSIZE - USBIP_AUTHSIZE)

		size_t plain_size = min(vec[i].iov_len - offset,
					min_t(size_t, size, USBIP_DATASIZE);


> +		const size_t PACKET_SIZE = PLAIN_SIZE + USBIP_AUTHSIZE;
> +		uint32_t packet_size_net = cpu_to_be32(PACKET_SIZE);
> +		struct kvec sendvec[2];
> +
> +		if (PLAIN_SIZE == 0) {
> +			++i;
> +			offset = 0;
> +			continue;
> +		}
> +
> +		/*
> +		 * One encrypted packet consists of:
> +		 *  - An unencrypted, authenticated length tag (exactly 4
> +		 *    bytes) containing the length of the packet.
> +		 *  - Up to USBIP_PACKETSIZE - USBIP_AUTHSIZE bytes of user
> +		 *    payload, encrypted
> +		 *  - Exactly USBIP_AUTHSIZE bytes authentication tag.
> +		 * Note: The packet length is also authenticated, but has
> +		 * - for obvious reasons - to be sent in plaintext. This
> +		 * packet format will be parsed by usbip_recvmsg (see above).
> +		 */
> +		ret = usbip_crypt(ud, 1, PACKET_SIZE, vec[i].iov_base + offset,
> +				cipherbuf);
> +		if (ret != 0) {
> +			kfree(cipherbuf);
> +			return ret;
> +		}

		if (ret) {
			total = ret;
			goto out;
		}

> +
> +		/* Length field */
> +		sendvec[0].iov_base = &packet_size_net;
> +		sendvec[0].iov_len = sizeof(packet_size_net);
> +		/* Payload and authentication tag */
> +		sendvec[1].iov_base = cipherbuf;
> +		sendvec[1].iov_len = PACKET_SIZE;
> +		ret = kernel_sendmsg(ud->tcp_socket, msg, sendvec,
> +				ARRAY_SIZE(sendvec), sendvec[0].iov_len +
> +				sendvec[1].iov_len);

It's better to break this up like this:

		ret = kernel_sendmsg(ud->tcp_socket, msg, sendvec,
				     ARRAY_SIZE(sendvec),
				     sendvec[0].iov_len + sendvec[1].iov_len);


Alternatively you could add a temp variable:

		send_len = sendvec[0].iov_len + sendvec[1].iov_len;
		ret = kernel_sendmsg(ud->tcp_socket, msg, sendvec,
				     ARRAY_SIZE(sendvec), send_len);
		if (ret < 0) {
			total = ret;
			goto out;
		}
		if (ret != send_len) {
			total = -EPROTO;
			goto out;
		}


> +		if (ret < 0) {
> +			kfree(cipherbuf);
> +			return ret;
> +		}
> +		if (ret != sendvec[0].iov_len + sendvec[1].iov_len) {
> +			kfree(cipherbuf);
> +			return -EPROTO;
> +		}
> +		offset += PLAIN_SIZE;
> +		size -= PLAIN_SIZE;
> +		total += PLAIN_SIZE;
> +	}
> +

out:

> +	kfree(cipherbuf);
> +
> +	return total;
>  }
>  EXPORT_SYMBOL_GPL(usbip_sendmsg);
>  
> diff --git a/drivers/staging/usbip/usbip_common.h b/drivers/staging/usbip/usbip_common.h
> index bdad29f..939a232 100644
> --- a/drivers/staging/usbip/usbip_common.h
> +++ b/drivers/staging/usbip/usbip_common.h
> @@ -29,15 +29,28 @@
>  #include <linux/types.h>
>  #include <linux/usb.h>
>  #include <linux/wait.h>
> +#include <linux/kfifo.h>
>  
>  #define USBIP_VERSION "1.0.0"
>  
>  /*
> + * Length of the authentication tag associated with each packet, in bytes. Can
> + * be set to 4, 8, 12, 13, 14, 15 or 16. See crypto_gcm_setauthsize in
> + * crypto/gcm.c. Increasing this will increase crypto protocol overhead.
> + */
> +#define USBIP_AUTHSIZE 4
> +/*
>   * Length of symmetric keys. Currently, this should be fixed at 16 bytes.
>   * Will break code if changed, look at userspace and stub_dev.c/vhci_sysfs.c
>   * where this constant is used before changing.
>   */
>  #define USBIP_KEYSIZE 16
> +/*
> + * Maximum size of encrypted packets. Decreasing this will increase overhead
> + * and decrease memory usage.
> + */
> +#define USBIP_PACKETSIZE 1024
> +#define RECVQ_SIZE (2*USBIP_PACKETSIZE)
>  
>  #undef pr_fmt
>  
> @@ -300,6 +313,11 @@ struct usbip_device {
>  
>  	/* Crypto support */
>  	int use_crypto;
> +	struct crypto_aead *tfm_recv;
> +	struct crypto_aead *tfm_send;
> +	/* Counters to be used as IVs */
> +	u64 ctr_send, ctr_recv;
> +	DECLARE_KFIFO_PTR(recv_queue, char);
>  };
>  
>  #define kthread_get_run(threadfn, data, namefmt, ...)			   \
> @@ -323,6 +341,10 @@ struct usbip_device {
>  void usbip_dump_urb(struct urb *purb);
>  void usbip_dump_header(struct usbip_header *pdu);
>  
> +int usbip_init_crypto(struct usbip_device *ud, unsigned char *sendkey,
> +		unsigned char *recvkey);
> +void usbip_deinit_crypto(struct usbip_device *ud);
> +
>  int usbip_recv(struct usbip_device *ui, void *buf, int size);
>  struct socket *sockfd_to_socket(unsigned int sockfd);
>  
> diff --git a/drivers/staging/usbip/vhci_hcd.c b/drivers/staging/usbip/vhci_hcd.c
> index e810ad5..55290c1 100644
> --- a/drivers/staging/usbip/vhci_hcd.c
> +++ b/drivers/staging/usbip/vhci_hcd.c
> @@ -786,7 +786,9 @@ static void vhci_shutdown_connection(struct usbip_device *ud)
>  		kthread_stop_put(vdev->ud.tcp_tx);
>  		vdev->ud.tcp_tx = NULL;
>  	}
> -	pr_info("stop threads\n");
> +	pr_info("stopped threads\n");
> +
> +	usbip_deinit_crypto(&vdev->ud);
>  
>  	/* active connection is closed */
>  	if (vdev->ud.tcp_socket) {
> diff --git a/drivers/staging/usbip/vhci_sysfs.c b/drivers/staging/usbip/vhci_sysfs.c
> index 1ef3f25..ce46c16 100644
> --- a/drivers/staging/usbip/vhci_sysfs.c
> +++ b/drivers/staging/usbip/vhci_sysfs.c
> @@ -20,6 +20,8 @@
>  #include <linux/kthread.h>
>  #include <linux/file.h>
>  #include <linux/net.h>
> +#include <linux/crypto.h>
> +#include <linux/kfifo.h>
>  
>  #include "usbip_common.h"
>  #include "vhci.h"
> @@ -227,6 +229,13 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
>  	/* begin a lock */
>  	spin_lock(&the_controller->lock);
>  	vdev = port_to_vdev(rhport);
> +	if (use_crypto) {
> +		int ret = usbip_init_crypto(&vdev->ud, sendkey, recvkey);
> +		if (ret < 0) {
> +			spin_unlock(&the_controller->lock);
> +			return ret;
> +		}
> +	}
>  	spin_lock(&vdev->ud.lock);
>  
>  	if (vdev->ud.status != VDEV_ST_NULL) {
> @@ -237,6 +246,7 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
>  		fput(socket->file);
>  
>  		dev_err(dev, "port %d already used\n", rhport);
> +		usbip_deinit_crypto(&vdev->ud);
>  		return -EINVAL;
>  	}
>  
> -- 
> 1.8.4
> 
> _______________________________________________
> devel mailing list
> devel@linuxdriverproject.org
> http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCHv3  13/16] staging: usbip: Wrap kernel_sendmsg()/recvmsg()
  2013-09-28 17:42     ` [PATCHv3 13/16] staging: usbip: Wrap kernel_sendmsg()/recvmsg() Dominik Paulus
@ 2013-09-30 12:42       ` Dan Carpenter
  0 siblings, 0 replies; 53+ messages in thread
From: Dan Carpenter @ 2013-09-30 12:42 UTC (permalink / raw)
  To: Dominik Paulus
  Cc: Greg Kroah-Hartman, Anthony Foiani, devel, linux-kernel,
	linux-kernel, tobias.polzer, Ilija Hadzic

On Sat, Sep 28, 2013 at 07:42:50PM +0200, Dominik Paulus wrote:
> @@ -352,34 +352,29 @@ int usbip_recv(struct socket *sock, void *buf, int size)
>  
>  	usbip_dbg_xmit("enter\n");
>  
> -	if (!sock || !buf || !size) {
> -		pr_err("invalid arg, sock %p buff %p size %d\n", sock, buf,
> +	if (!ud || !buf || !size) {
> +		pr_err("invalid arg, ud %p buff %p size %d\n", ud, buf,
>  		       size);

"size" can fit on the first line now.

		pr_err("invalid arg, ud %p buff %p size %d\n", ud, buf, size);

>  		return -EINVAL;
>  	}
>  
> -	do {
> -		sock->sk->sk_allocation = GFP_NOIO;
> -		iov.iov_base    = buf;
> -		iov.iov_len     = size;
> -		msg.msg_name    = NULL;
> -		msg.msg_namelen = 0;
> -		msg.msg_control = NULL;
> -		msg.msg_controllen = 0;
> -		msg.msg_namelen    = 0;
> -		msg.msg_flags      = MSG_NOSIGNAL;
> -
> -		result = kernel_recvmsg(sock, &msg, &iov, 1, size, MSG_WAITALL);
> -		if (result <= 0) {
> -			pr_debug("receive sock %p buf %p size %u ret %d total %d\n",
> -				 sock, buf, size, result, total);
> -			goto err;
> -		}
> -
> -		size -= result;
> -		buf += result;
> -		total += result;
> -	} while (size > 0);
> +	ud->tcp_socket->sk->sk_allocation = GFP_NOIO;
> +	iov.iov_base    = buf;
> +	iov.iov_len     = size;
> +	msg.msg_name    = NULL;
> +	msg.msg_namelen = 0;
> +	msg.msg_control = NULL;
> +	msg.msg_controllen = 0;
> +	msg.msg_namelen    = 0;
> +	msg.msg_flags      = MSG_NOSIGNAL;
> +
> +	result = usbip_recvmsg(ud, &msg, &iov, 1, size, MSG_WAITALL);
> +

Don't put a blank line here.

> +	if (result < 0) {
> +		pr_debug("receive sock %p buf %p size %u ret %d total %d\n",
> +			 ud->tcp_socket, buf, size, result, total);
> +		return result;
> +	}
>  

regards,
dan carpenter

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCHv4 00/16] staging: usbip: Add kernel support for client ACLs
  2013-09-30 12:38       ` Dan Carpenter
@ 2013-10-19 14:39         ` Dominik Paulus
  2013-10-19 14:39           ` [PATCHv4 01/16] staging: usbip: Add support for client authentication Dominik Paulus
                             ` (15 more replies)
  0 siblings, 16 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-10-19 14:39 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Greg Kroah-Hartman, Anthony Foiani, devel, linux-kernel,
	Kurt Kanzenbach, linux-kernel, tobias.polzer, Ilija Hadzic

Hi,

sorry for the messed-up error checking. We fixed some of the style nitpicks and
hopefully all of the mistakes you pointed out (thank you for that!).

Regards,
	Dominik and Tobias


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCHv4 01/16] staging: usbip: Add support for client authentication
  2013-10-19 14:39         ` [PATCHv4 00/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
@ 2013-10-19 14:39           ` Dominik Paulus
  2013-10-19 14:39           ` [PATCHv4 02/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
                             ` (14 subsequent siblings)
  15 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-10-19 14:39 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Dominik Paulus, Tobias Polzer, Greg Kroah-Hartman,
	Anthony Foiani, devel, linux-kernel, Kurt Kanzenbach,
	linux-kernel, tobias.polzer, Ilija Hadzic

This patch adds support for authenticating both client and server using
a pre-shared passphrase using SRP (Secure Remote Password) over TLS (see
RFC 5054) using GnuTLS. Both usbip and usbipd now accept a shared secret
as a command line argument. Currently, the established TLS connection is
only used to perform a secure handshake and dropped before the socket is
passed to the kernel. The code may be extended to exchange a session key
over TLS and pass it to the kernel to perform IPsec.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/configure.ac       |  14 ++
 drivers/staging/usbip/userspace/doc/usbip.8        |   4 +
 drivers/staging/usbip/userspace/doc/usbipd.8       |   5 +
 drivers/staging/usbip/userspace/src/usbip.c        |  30 ++-
 drivers/staging/usbip/userspace/src/usbip_attach.c |   2 +-
 drivers/staging/usbip/userspace/src/usbip_list.c   |   2 +-
 .../staging/usbip/userspace/src/usbip_network.c    |  81 ++++++++
 .../staging/usbip/userspace/src/usbip_network.h    |   9 +-
 drivers/staging/usbip/userspace/src/usbipd.c       | 217 ++++++++++++++++++---
 9 files changed, 331 insertions(+), 33 deletions(-)

diff --git a/drivers/staging/usbip/userspace/configure.ac b/drivers/staging/usbip/userspace/configure.ac
index 0ee5d92..2208516 100644
--- a/drivers/staging/usbip/userspace/configure.ac
+++ b/drivers/staging/usbip/userspace/configure.ac
@@ -83,6 +83,20 @@ AC_ARG_WITH([tcp-wrappers],
 		AC_DEFINE([HAVE_LIBWRAP], [1], [use tcp wrapper])],
 	       [AC_MSG_RESULT([no]); LIBS="$saved_LIBS"])])
 
+# Checks for the GnuTLS library
+AC_ARG_WITH([gnutls],
+			[AS_HELP_STRING([--with-gnutls],
+							[use the GnuTLS library for authentication])],
+			dnl [ACTION-IF-GIVEN]
+			[if test "$withval" = "yes"; then
+			 PKG_CHECK_MODULES([GNUTLS], [gnutls])
+			 AC_DEFINE([HAVE_GNUTLS], [1], [use gnutls])
+			 CFLAGS="$CFLAGS $GNUTLS_CFLAGS"
+			 LDFLAGS="$LDFLAGS $GNUTLS_LIBS"
+			 fi
+			],
+			)
+
 # Sets directory containing usb.ids.
 AC_ARG_WITH([usbids-dir],
 	    [AS_HELP_STRING([--with-usbids-dir=DIR],
diff --git a/drivers/staging/usbip/userspace/doc/usbip.8 b/drivers/staging/usbip/userspace/doc/usbip.8
index a6097be..847aa40 100644
--- a/drivers/staging/usbip/userspace/doc/usbip.8
+++ b/drivers/staging/usbip/userspace/doc/usbip.8
@@ -27,6 +27,10 @@ Log to syslog.
 \fB\-\-tcp-port PORT\fR
 .IP
 Connect to PORT on remote host (used for attach and list --remote).
+
+\fB\-\-auth\fR
+.IP
+Set the password to be used for client authentication. See usbipd(8) for more information.
 .PP
 
 .SH COMMANDS
diff --git a/drivers/staging/usbip/userspace/doc/usbipd.8 b/drivers/staging/usbip/userspace/doc/usbipd.8
index ac4635d..8beb95a 100644
--- a/drivers/staging/usbip/userspace/doc/usbipd.8
+++ b/drivers/staging/usbip/userspace/doc/usbipd.8
@@ -52,6 +52,11 @@ If no FILE specified, use /var/run/usbipd.pid
 \fB\-tPORT\fR, \fB\-\-tcp\-port PORT\fR
 .IP
 Listen on TCP/IP port PORT.
+
+.HP
+\fB\-s\fR, \fB\-\-auth\fR
+.IP
+Sets the password to be used for client authentication. If -a is used, the server will only accept connections from authenticated clients. Note: USB traffic will still be unencrypted, this currently only serves for authentication.
 .PP
 
 \fB\-h\fR, \fB\-\-help\fR
diff --git a/drivers/staging/usbip/userspace/src/usbip.c b/drivers/staging/usbip/userspace/src/usbip.c
index 04a5f20..8a5de83 100644
--- a/drivers/staging/usbip/userspace/src/usbip.c
+++ b/drivers/staging/usbip/userspace/src/usbip.c
@@ -25,6 +25,12 @@
 #include <getopt.h>
 #include <syslog.h>
 
+#include "../config.h"
+
+#ifdef HAVE_GNUTLS
+#include <gnutls/gnutls.h>
+#endif
+
 #include "usbip_common.h"
 #include "usbip_network.h"
 #include "usbip.h"
@@ -35,8 +41,12 @@ static int usbip_version(int argc, char *argv[]);
 static const char usbip_version_string[] = PACKAGE_STRING;
 
 static const char usbip_usage_string[] =
-	"usbip [--debug] [--log] [--tcp-port PORT] [version]\n"
-	"             [help] <command> <args>\n";
+	"usbip "
+#ifdef HAVE_GNUTLS
+	"[--auth PASSWORD] "
+#endif
+	"[--debug] [--log] [--tcp-port PORT]\n"
+	"             [version] [help] <command> <args>\n";
 
 static void usbip_usage(void)
 {
@@ -142,6 +152,7 @@ int main(int argc, char *argv[])
 		{ "debug",    no_argument,       NULL, 'd' },
 		{ "log",      no_argument,       NULL, 'l' },
 		{ "tcp-port", required_argument, NULL, 't' },
+		{ "auth",     required_argument, NULL, 's' },
 		{ NULL,       0,                 NULL,  0  }
 	};
 
@@ -152,12 +163,25 @@ int main(int argc, char *argv[])
 	usbip_use_stderr = 1;
 	opterr = 0;
 	for (;;) {
-		opt = getopt_long(argc, argv, "+dlt:", opts, NULL);
+		opt = getopt_long(argc, argv, "+dls:t:", opts, NULL);
 
 		if (opt == -1)
 			break;
 
 		switch (opt) {
+		case 's':
+#ifdef HAVE_GNUTLS
+			usbip_srp_password = optarg;
+			rc = gnutls_global_init();
+			if (rc < 0) {
+				err("Unable to initialize GnuTLS library: %s",
+					gnutls_strerror(rc));
+				return EXIT_FAILURE;
+			}
+#else
+			err("usbip has been compiled without GnuTLS support");
+#endif
+			break;
 		case 'd':
 			usbip_use_debug = 1;
 			break;
diff --git a/drivers/staging/usbip/userspace/src/usbip_attach.c b/drivers/staging/usbip/userspace/src/usbip_attach.c
index 0858411..2363e56 100644
--- a/drivers/staging/usbip/userspace/src/usbip_attach.c
+++ b/drivers/staging/usbip/userspace/src/usbip_attach.c
@@ -175,7 +175,7 @@ static int attach_device(char *host, char *busid)
 	int rc;
 	int rhport;
 
-	sockfd = usbip_net_tcp_connect(host, usbip_port_string);
+	sockfd = usbip_net_connect(host);
 	if (sockfd < 0) {
 		err("tcp connect");
 		return -1;
diff --git a/drivers/staging/usbip/userspace/src/usbip_list.c b/drivers/staging/usbip/userspace/src/usbip_list.c
index 237e099..e4fa5b8 100644
--- a/drivers/staging/usbip/userspace/src/usbip_list.c
+++ b/drivers/staging/usbip/userspace/src/usbip_list.c
@@ -131,7 +131,7 @@ static int list_exported_devices(char *host)
 	int rc;
 	int sockfd;
 
-	sockfd = usbip_net_tcp_connect(host, usbip_port_string);
+	sockfd = usbip_net_connect(host);
 	if (sockfd < 0) {
 		err("could not connect to %s:%s: %s", host,
 		    usbip_port_string, gai_strerror(sockfd));
diff --git a/drivers/staging/usbip/userspace/src/usbip_network.c b/drivers/staging/usbip/userspace/src/usbip_network.c
index b4c37e7..9425cb6 100644
--- a/drivers/staging/usbip/userspace/src/usbip_network.c
+++ b/drivers/staging/usbip/userspace/src/usbip_network.c
@@ -29,11 +29,19 @@
 #include <tcpd.h>
 #endif
 
+#include "../config.h"
+#ifdef HAVE_GNUTLS
+#include <gnutls/gnutls.h>
+#endif
+
 #include "usbip_common.h"
 #include "usbip_network.h"
 
 int usbip_port = 3240;
 char *usbip_port_string = "3240";
+#ifdef HAVE_GNUTLS
+char *usbip_srp_password;
+#endif
 
 void usbip_setup_port_number(char *arg)
 {
@@ -301,3 +309,76 @@ int usbip_net_tcp_connect(char *hostname, char *service)
 
 	return sockfd;
 }
+
+#ifdef HAVE_GNUTLS
+int usbip_net_srp_handshake(int sockfd)
+{
+	int ret;
+	gnutls_session_t session;
+	gnutls_srp_client_credentials_t srp_cred;
+
+	ret = gnutls_srp_allocate_client_credentials(&srp_cred);
+	if (ret < 0)
+		return ret;
+
+	gnutls_srp_set_client_credentials(srp_cred, "dummyuser",
+		usbip_srp_password);
+
+	ret = gnutls_init(&session, GNUTLS_CLIENT);
+	if (ret < 0) {
+		gnutls_srp_free_client_credentials(srp_cred);
+		return ret;
+	}
+
+	gnutls_priority_set_direct(session, "NORMAL:+SRP", NULL);
+
+	gnutls_credentials_set(session, GNUTLS_CRD_SRP, srp_cred);
+	gnutls_transport_set_int (session, sockfd);
+
+	do {
+		ret = gnutls_handshake(session);
+	} while (ret < 0 && !gnutls_error_is_fatal(ret));
+
+	gnutls_bye(session, GNUTLS_SHUT_RDWR);
+
+	gnutls_deinit(session);
+	gnutls_srp_free_client_credentials(srp_cred);
+
+	return ret;
+}
+#endif
+
+/*
+ * Connect to the server. Performs the TCP connection attempt
+ * and - if necessary - the TLS handshake used for authentication.
+ */
+int usbip_net_connect(char *hostname)
+{
+	int sockfd;
+
+	sockfd = usbip_net_tcp_connect(hostname, usbip_port_string);
+	if (sockfd < 0)
+		return sockfd;
+
+#ifdef HAVE_GNUTLS
+	if (usbip_srp_password) {
+		int rc;
+
+		rc = usbip_net_send_op_common(sockfd, OP_REQ_STARTTLS, 0);
+		if (rc < 0) {
+			err("usbip_net_send_op_common failed");
+			return EAI_SYSTEM;
+		}
+
+		rc = usbip_net_srp_handshake(sockfd);
+		if (rc < 0) {
+			err("Unable to perform TLS handshake (wrong password?): %s",
+				gnutls_strerror(rc));
+			close(sockfd);
+			return EAI_SYSTEM;
+		}
+	}
+#endif
+
+	return sockfd;
+}
diff --git a/drivers/staging/usbip/userspace/src/usbip_network.h b/drivers/staging/usbip/userspace/src/usbip_network.h
index f19ae19..758656b 100644
--- a/drivers/staging/usbip/userspace/src/usbip_network.h
+++ b/drivers/staging/usbip/userspace/src/usbip_network.h
@@ -16,6 +16,7 @@
 
 extern int usbip_port;
 extern char *usbip_port_string;
+extern char *usbip_srp_password;
 void usbip_setup_port_number(char *arg);
 
 /* ---------------------------------------------------------------------- */
@@ -168,6 +169,12 @@ struct op_devlist_reply_extra {
 	usbip_net_pack_uint32_t(pack, &(reply)->ndev);\
 } while (0)
 
+/* ---------------------------------------------------------------------- */
+/* Initiate encrypted connection. */
+#define OP_STARTTLS 0x06
+#define OP_REQ_STARTTLS   (OP_REQUEST | OP_STARTTLS)
+#define OP_REP_STARTTLS   (OP_REPLY   | OP_STARTTLS)
+
 void usbip_net_pack_uint32_t(int pack, uint32_t *num);
 void usbip_net_pack_uint16_t(int pack, uint16_t *num);
 void usbip_net_pack_usb_device(int pack, struct usbip_usb_device *udev);
@@ -181,6 +188,6 @@ int usbip_net_set_reuseaddr(int sockfd);
 int usbip_net_set_nodelay(int sockfd);
 int usbip_net_set_keepalive(int sockfd);
 int usbip_net_set_v6only(int sockfd);
-int usbip_net_tcp_connect(char *hostname, char *port);
+int usbip_net_connect(char *hostname);
 
 #endif /* __USBIP_NETWORK_H */
diff --git a/drivers/staging/usbip/userspace/src/usbipd.c b/drivers/staging/usbip/userspace/src/usbipd.c
index 7980f8b..8db2f27 100644
--- a/drivers/staging/usbip/userspace/src/usbipd.c
+++ b/drivers/staging/usbip/userspace/src/usbipd.c
@@ -32,6 +32,11 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 
+#ifdef HAVE_GNUTLS
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#endif
+
 #ifdef HAVE_LIBWRAP
 #include <tcpd.h>
 #endif
@@ -63,6 +68,11 @@ static const char usbipd_help_string[] =
 	"	-6, --ipv6\n"
 	"		Bind to IPv6. Default is both.\n"
 	"\n"
+#ifdef HAVE_GNUTLS
+	"	-sPASSWORD, --auth PASSWORD\n"
+	"		Set PASSWORD as key used for authentication.\n"
+	"\n"
+#endif
 	"	-D, --daemon\n"
 	"		Run as a daemon process.\n"
 	"\n"
@@ -82,6 +92,78 @@ static const char usbipd_help_string[] =
 	"	-v, --version\n"
 	"		Show version.\n";
 
+static int need_auth;
+#ifdef HAVE_GNUTLS
+static gnutls_datum_t srp_salt, srp_verifier;
+static gnutls_srp_server_credentials_t srp_cred;
+
+#define SRP_GROUP gnutls_srp_2048_group_generator
+#define SRP_PRIME gnutls_srp_2048_group_prime
+
+static int net_srp_callback(gnutls_session_t sess, const char *username,
+	gnutls_datum_t *nsalt, gnutls_datum_t *nverifier, gnutls_datum_t *g,
+	gnutls_datum_t *n)
+{
+	/*
+	 * GnuTLS expects us to allocate all data returned from callbacks
+	 * using gnutls_malloc(), thus, we have to create a fresh copy of
+	 * our static credentials for every connection.
+	 */
+	nsalt->data = gnutls_malloc(srp_salt.size);
+	nverifier->data = gnutls_malloc(srp_verifier.size);
+	if (nsalt->data == NULL || nverifier->data == NULL) {
+		gnutls_free(nsalt->data);
+		gnutls_free(nverifier->data);
+		return -1;
+	}
+	nsalt->size = srp_salt.size;
+	nverifier->size = srp_verifier.size;
+	memcpy(nverifier->data, srp_verifier.data, srp_verifier.size);
+	memcpy(nsalt->data, srp_salt.data, srp_salt.size);
+
+	*g = SRP_GROUP;
+	*n = SRP_PRIME;
+
+	/* We only have a single session, thus, ignore it */
+	(void) sess;
+
+	if (strcmp(username, "dummyuser"))
+		/* User invalid, stored dummy data in g and n. */
+		return 1;
+
+	return 0;
+}
+
+static int net_srp_server_handshake(int connfd)
+{
+	int ret;
+	gnutls_session_t session;
+
+	if (gnutls_init(&session, GNUTLS_SERVER) != 0)
+		return -1;
+	gnutls_priority_set_direct(session, "NORMAL:-KX-ALL:+SRP", NULL);
+	if (gnutls_credentials_set(session, GNUTLS_CRD_SRP, srp_cred) != 0)
+		return -1;
+
+	gnutls_transport_set_int(session, connfd);
+
+	do {
+		ret = gnutls_handshake(session);
+	} while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+	if (ret < 0)
+		err("GnuTLS handshake failed (%s)", gnutls_strerror(ret));
+	else
+		info("GnuTLS handshake completed");
+
+	if (gnutls_bye(session, GNUTLS_SHUT_RDWR) != 0)
+		err("Unable to shutdown TLS connection.");
+	gnutls_deinit(session);
+
+	return ret;
+}
+#endif
+
 static void usbipd_help(void)
 {
 	printf("%s\n", usbipd_help_string);
@@ -238,14 +320,7 @@ static int recv_request_devlist(int connfd)
 
 static int recv_pdu(int connfd)
 {
-	uint16_t code = OP_UNSPEC;
-	int ret;
-
-	ret = usbip_net_recv_op_common(connfd, &code);
-	if (ret < 0) {
-		dbg("could not receive opcode: %#0x", code);
-		return -1;
-	}
+	int auth = !need_auth, cont = 1, ret;
 
 	ret = usbip_host_refresh_device_list();
 	if (ret < 0) {
@@ -253,25 +328,64 @@ static int recv_pdu(int connfd)
 		return -1;
 	}
 
-	info("received request: %#0x(%d)", code, connfd);
-	switch (code) {
-	case OP_REQ_DEVLIST:
-		ret = recv_request_devlist(connfd);
-		break;
-	case OP_REQ_IMPORT:
-		ret = recv_request_import(connfd);
-		break;
-	case OP_REQ_DEVINFO:
-	case OP_REQ_CRYPKEY:
-	default:
-		err("received an unknown opcode: %#0x", code);
-		ret = -1;
-	}
+	/*
+	 * Process opcodes. We might receive more than one, as the
+	 * client might send STARTTLS first
+	 */
+	while (cont) {
+		uint16_t code = OP_UNSPEC;
 
-	if (ret == 0)
-		info("request %#0x(%d): complete", code, connfd);
-	else
-		info("request %#0x(%d): failed", code, connfd);
+		ret = usbip_net_recv_op_common(connfd, &code);
+		if (ret < 0) {
+			dbg("could not receive opcode: %#0x", code);
+			return -1;
+		}
+
+		info("received request: %#0x(%d)", code, connfd);
+
+		/* We require an authenticated encryption */
+		if (!auth && code != OP_REQ_STARTTLS) {
+			usbip_net_send_op_common(connfd, OP_REPLY, ST_NA);
+			return -1;
+		}
+
+		switch (code) {
+#ifdef HAVE_GNUTLS
+		case OP_REQ_STARTTLS:
+			if (!need_auth) {
+				ret = -1;
+				err("Unexpected TLS handshake attempt (client "
+					"uses password, server doesn't)");
+			} else {
+				ret = net_srp_server_handshake(connfd);
+				if (ret != 0)
+					err("TLS handshake failed");
+				auth = 1;
+			}
+			break;
+#endif
+		case OP_REQ_DEVLIST:
+			ret = recv_request_devlist(connfd);
+			cont = 0;
+			break;
+		case OP_REQ_IMPORT:
+			ret = recv_request_import(connfd);
+			cont = 0;
+			break;
+		case OP_REQ_DEVINFO:
+		case OP_REQ_CRYPKEY:
+		default:
+			err("received an unknown opcode: %#0x", code);
+			ret = -1;
+		}
+
+		if (ret == 0)
+			info("request %#0x(%d): complete", code, connfd);
+		else {
+			info("request %#0x(%d): failed", code, connfd);
+			break;
+		}
+	}
 
 	return ret;
 }
@@ -577,6 +691,37 @@ static int do_standalone_mode(int daemonize, int ipv4, int ipv6)
 	return 0;
 }
 
+#ifdef HAVE_GNUTLS
+static int usbip_init_gnutls(void)
+{
+	int ret;
+
+	gnutls_global_init();
+
+	srp_salt.data = gnutls_malloc(16);
+	if (!srp_salt.data)
+		return GNUTLS_E_MEMORY_ERROR;
+
+	ret = gnutls_rnd(GNUTLS_RND_NONCE, srp_salt.data, 16);
+	if (ret < 0)
+		return ret;
+	srp_salt.size = 16;
+
+	ret = gnutls_srp_allocate_server_credentials(&srp_cred);
+	if (ret < 0)
+		return ret;
+
+	ret = gnutls_srp_verifier("dummyuser", optarg, &srp_salt, &SRP_GROUP,
+		&SRP_PRIME, &srp_verifier);
+	if (ret < 0)
+		return ret;
+
+	gnutls_srp_set_server_credentials_function(srp_cred, net_srp_callback);
+
+	return GNUTLS_E_SUCCESS;
+}
+#endif
+
 int main(int argc, char *argv[])
 {
 	static const struct option longopts[] = {
@@ -589,6 +734,7 @@ int main(int argc, char *argv[])
 		{ "tcp-port", required_argument, NULL, 't' },
 		{ "help",     no_argument,       NULL, 'h' },
 		{ "version",  no_argument,       NULL, 'v' },
+		{ "auth",     required_argument, NULL, 's' },
 		{ NULL,	      0,                 NULL,  0  }
 	};
 
@@ -601,6 +747,9 @@ int main(int argc, char *argv[])
 	int daemonize = 0;
 	int ipv4 = 0, ipv6 = 0;
 	int opt, rc = -1;
+#ifdef HAVE_GNUTLS
+	int ret;
+#endif
 	pid_file = NULL;
 
 	usbip_use_stderr = 1;
@@ -611,7 +760,7 @@ int main(int argc, char *argv[])
 
 	cmd = cmd_standalone_mode;
 	for (;;) {
-		opt = getopt_long(argc, argv, "46DdP::t:hv", longopts, NULL);
+		opt = getopt_long(argc, argv, "46s:DdP::t:hv", longopts, NULL);
 
 		if (opt == -1)
 			break;
@@ -623,6 +772,20 @@ int main(int argc, char *argv[])
 		case '6':
 			ipv6 = 1;
 			break;
+		case 's':
+#ifdef HAVE_GNUTLS
+			need_auth = 1;
+			ret = usbip_init_gnutls();
+			if (ret < 0) {
+				err("Unable to initialize GnuTLS: %s",
+					gnutls_strerror(ret));
+				return EXIT_FAILURE;
+			}
+			break;
+#else
+			err("usbipd has been compiled without GnuTLS support");
+			break;
+#endif
 		case 'D':
 			daemonize = 1;
 			break;
-- 
1.8.4.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv4 02/16] staging: usbip: Add kernel support for client ACLs
  2013-10-19 14:39         ` [PATCHv4 00/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
  2013-10-19 14:39           ` [PATCHv4 01/16] staging: usbip: Add support for client authentication Dominik Paulus
@ 2013-10-19 14:39           ` Dominik Paulus
  2013-10-19 14:39           ` [PATCHv4 03/16] staging: usbip: Add CIDR matching helper functions Dominik Paulus
                             ` (13 subsequent siblings)
  15 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-10-19 14:39 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Dominik Paulus, Kurt Kanzenbach, Tobias Polzer,
	Greg Kroah-Hartman, Anthony Foiani, devel, linux-kernel,
	linux-kernel, tobias.polzer, Ilija Hadzic

This patch adds the possibility to stored ACLs for allowed clients for
each stub device in sysfs. It adds a new sysfs entry called "usbip_acl"
for each stub device, containing a list of CIDR masks of allowed
clients. This file will be used by usbip and usbipd to store the ACL.

Signed-off-by: Kurt Kanzenbach <ly80toro@cip.cs.fau.de>
Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/stub.h     |  5 ++++
 drivers/staging/usbip/stub_dev.c | 61 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 65 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/usbip/stub.h b/drivers/staging/usbip/stub.h
index a73e437..cfe75d1 100644
--- a/drivers/staging/usbip/stub.h
+++ b/drivers/staging/usbip/stub.h
@@ -60,6 +60,11 @@ struct stub_device {
 	struct list_head unlink_free;
 
 	wait_queue_head_t tx_waitq;
+
+	/* list of allowed IP addrs */
+	char *acls;
+	/* for locking list operations */
+	spinlock_t ip_lock;
 };
 
 /* private data into urb->priv */
diff --git a/drivers/staging/usbip/stub_dev.c b/drivers/staging/usbip/stub_dev.c
index 76a1ff0..0e33542 100644
--- a/drivers/staging/usbip/stub_dev.c
+++ b/drivers/staging/usbip/stub_dev.c
@@ -142,6 +142,56 @@ err:
 }
 static DEVICE_ATTR(usbip_sockfd, S_IWUSR, NULL, store_sockfd);
 
+/*
+ * This function replaces the current ACL list
+ */
+static ssize_t store_acl(struct device *dev, struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct stub_device *sdev = dev_get_drvdata(dev);
+	int retval;
+
+	if (count >= PAGE_SIZE)
+		return -EINVAL;
+
+	spin_lock_irq(&sdev->ip_lock);
+	kfree(sdev->acls);
+	sdev->acls = kstrdup(buf, GFP_KERNEL);
+	if (!sdev->acls) {
+		retval = -ENOMEM;
+	} else {
+		retval = strlen(sdev->acls);
+	}
+	spin_unlock_irq(&sdev->ip_lock);
+
+	return retval;
+}
+
+/*
+ * This functions prints all allowed IP addrs for this dev
+ */
+static ssize_t show_acl(struct device *dev, struct device_attribute *attr,
+		       char *buf)
+{
+	struct stub_device *sdev = dev_get_drvdata(dev);
+	int retval;
+
+	if (!sdev)
+		return -ENODEV;
+
+	spin_lock_irq(&sdev->ip_lock);
+	if (sdev->acls == NULL) {
+		retval = 0;
+	} else {
+		strcpy(buf, sdev->acls);
+		retval = strlen(buf);
+	}
+	spin_unlock_irq(&sdev->ip_lock);
+
+	return retval;
+}
+static DEVICE_ATTR(usbip_acl, S_IWUSR | S_IRUGO, show_acl, store_acl);
+
 static int stub_add_files(struct device *dev)
 {
 	int err = 0;
@@ -157,9 +207,13 @@ static int stub_add_files(struct device *dev)
 	err = device_create_file(dev, &dev_attr_usbip_debug);
 	if (err)
 		goto err_debug;
+	err = device_create_file(dev, &dev_attr_usbip_acl);
+	if (err)
+		goto err_acl;
 
 	return 0;
-
+err_acl:
+	device_remove_file(dev, &dev_attr_usbip_debug);
 err_debug:
 	device_remove_file(dev, &dev_attr_usbip_sockfd);
 err_sockfd:
@@ -173,6 +227,7 @@ static void stub_remove_files(struct device *dev)
 	device_remove_file(dev, &dev_attr_usbip_status);
 	device_remove_file(dev, &dev_attr_usbip_sockfd);
 	device_remove_file(dev, &dev_attr_usbip_debug);
+	device_remove_file(dev, &dev_attr_usbip_acl);
 }
 
 static void stub_shutdown_connection(struct usbip_device *ud)
@@ -312,6 +367,7 @@ static struct stub_device *stub_device_alloc(struct usb_device *udev,
 	INIT_LIST_HEAD(&sdev->priv_free);
 	INIT_LIST_HEAD(&sdev->unlink_free);
 	INIT_LIST_HEAD(&sdev->unlink_tx);
+	spin_lock_init(&sdev->ip_lock);
 	spin_lock_init(&sdev->priv_lock);
 
 	init_waitqueue_head(&sdev->tx_waitq);
@@ -511,6 +567,9 @@ static void stub_disconnect(struct usb_interface *interface)
 	usb_put_dev(sdev->udev);
 	usb_put_intf(interface);
 
+	/* free ACL list */
+	kfree(sdev->acls);
+
 	/* free sdev */
 	busid_priv->sdev = NULL;
 	stub_device_free(sdev);
-- 
1.8.4.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv4 03/16] staging: usbip: Add CIDR matching helper functions
  2013-10-19 14:39         ` [PATCHv4 00/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
  2013-10-19 14:39           ` [PATCHv4 01/16] staging: usbip: Add support for client authentication Dominik Paulus
  2013-10-19 14:39           ` [PATCHv4 02/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
@ 2013-10-19 14:39           ` Dominik Paulus
  2013-10-19 14:39           ` [PATCHv4 04/16] staging: usbip: Add ACL support to usbip bind Dominik Paulus
                             ` (12 subsequent siblings)
  15 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-10-19 14:39 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Dominik Paulus, Tobias Polzer, Greg Kroah-Hartman,
	Anthony Foiani, devel, linux-kernel, Kurt Kanzenbach,
	linux-kernel, tobias.polzer, Ilija Hadzic

This patch adds a few utility functions to match IP addresses against
CIDR masks.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/src/utils.c | 84 +++++++++++++++++++++++++++++
 drivers/staging/usbip/userspace/src/utils.h | 15 ++++++
 2 files changed, 99 insertions(+)

diff --git a/drivers/staging/usbip/userspace/src/utils.c b/drivers/staging/usbip/userspace/src/utils.c
index 2d4966e..df40817 100644
--- a/drivers/staging/usbip/userspace/src/utils.c
+++ b/drivers/staging/usbip/userspace/src/utils.c
@@ -74,3 +74,87 @@ int modify_match_busid(char *busid, int add)
 
 	return ret;
 }
+
+/*
+ * Parses a string of form "ip/prefix" into a subnet mask to dest.
+ * Returns -1 on error, 0 on success
+ */
+int parse_cidr(const char *src, struct subnet *dest)
+{
+	char *ip, *prefix, *saveptr;
+	char *endptr;
+	struct in6_addr ip6;
+	struct in_addr ip4;
+	int bits;
+	long int tmp;
+	char buf[128]; /* For strtok */
+
+	strncpy(buf, src, sizeof(buf));
+	buf[sizeof(buf)-1] = 0;
+
+	ip = strtok_r(buf, "/", &saveptr);
+	prefix = strtok_r(NULL, "/", &saveptr);
+	if (strtok_r(NULL, "/", &saveptr) || !ip ||
+			strlen(src) > sizeof(buf) - 1)
+		return -1;
+
+	if (inet_pton(AF_INET6, ip, &ip6) == 1) {
+		dest->ai_family = AF_INET6;
+		bits = 128;
+		dest->address.ip6 = ip6;
+	} else if (inet_pton(AF_INET, ip, &ip4) == 1) {
+		dest->ai_family = AF_INET;
+		bits = 32;
+		dest->address.ip4 = ip4;
+	} else {
+		return -1;
+	}
+
+	/*
+	 * We also accept single IPs without an explicitely
+	 * specified prefix
+	 */
+	if (prefix) {
+		tmp = strtol(prefix, &endptr, 10);
+		if (tmp < 0 || tmp > bits || *endptr != '\0')
+			return -1;
+		dest->prefix = tmp;
+	} else {
+		dest->prefix = bits;
+	}
+
+	return 0;
+}
+
+/*
+ * Checks if addr is in range. Expects addr to be a struct in6_addr* if
+ * ai_family == AF_INET6, else struct in_addr*.
+ * Returns 1 if in range, 0 otherwise.
+ */
+int in_range(struct sockaddr_storage *addr, struct subnet range)
+{
+	if (addr->ss_family != range.ai_family)
+		return 0;
+	if (addr->ss_family == AF_INET6) {
+		int i;
+		struct sockaddr_in6 *in6addr = (struct sockaddr_in6 *) addr;
+		unsigned char *ip = in6addr->sin6_addr.s6_addr;
+		for (i = 0; i < range.prefix; ++i) {
+			int idx = i/8, mask = 1 << (7 - i%8);
+			if ((ip[idx] & mask) != (range.address.ip6.s6_addr[idx]
+			  & mask))
+				return 0;
+		}
+	} else {
+		int i;
+		struct sockaddr_in *inaddr = (struct sockaddr_in *) addr;
+		uint32_t ip = ntohl(inaddr->sin_addr.s_addr);
+		uint32_t comp = ntohl(range.address.ip4.s_addr);
+		for (i = 0; i < range.prefix; ++i) {
+			int mask = 1 << (31-i);
+			if ((ip & mask) != (comp & mask))
+				return 0;
+		}
+	}
+	return 1;
+}
diff --git a/drivers/staging/usbip/userspace/src/utils.h b/drivers/staging/usbip/userspace/src/utils.h
index 5916fd3..a3704ef 100644
--- a/drivers/staging/usbip/userspace/src/utils.h
+++ b/drivers/staging/usbip/userspace/src/utils.h
@@ -19,7 +19,22 @@
 #ifndef __UTILS_H
 #define __UTILS_H
 
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netinet/ip.h>
+
+struct subnet {
+	int ai_family;
+	int prefix;
+	union {
+		struct in6_addr ip6;
+		struct in_addr ip4;
+	} address;
+};
+
 int modify_match_busid(char *busid, int add);
+int parse_cidr(const char *src, struct subnet *dest);
+int in_range(struct sockaddr_storage *addr, struct subnet range);
 
 #endif /* __UTILS_H */
 
-- 
1.8.4.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv4 04/16] staging: usbip: Add ACL support to usbip bind
  2013-10-19 14:39         ` [PATCHv4 00/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
                             ` (2 preceding siblings ...)
  2013-10-19 14:39           ` [PATCHv4 03/16] staging: usbip: Add CIDR matching helper functions Dominik Paulus
@ 2013-10-19 14:39           ` Dominik Paulus
  2013-10-19 14:39           ` [PATCHv4 05/16] staging: usbip: Add support for ACLs in usbipd Dominik Paulus
                             ` (11 subsequent siblings)
  15 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-10-19 14:39 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Dominik Paulus, Kurt Kanzenbach, Tobias Polzer,
	Greg Kroah-Hartman, Anthony Foiani, devel, linux-kernel,
	linux-kernel, tobias.polzer, Ilija Hadzic

Add the command line argument -a (--allow) to usbip bind to specify
networks allowed to attach to the device and code to store the ACLs in
sysfs.

Signed-off-by: Kurt Kanzenbach <ly80toro@cip.cs.fau.de>
Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/doc/usbip.8      |  8 ++-
 drivers/staging/usbip/userspace/src/usbip_bind.c | 74 ++++++++++++++++++++----
 2 files changed, 69 insertions(+), 13 deletions(-)

diff --git a/drivers/staging/usbip/userspace/doc/usbip.8 b/drivers/staging/usbip/userspace/doc/usbip.8
index 847aa40..c7ba36f 100644
--- a/drivers/staging/usbip/userspace/doc/usbip.8
+++ b/drivers/staging/usbip/userspace/doc/usbip.8
@@ -60,9 +60,15 @@ Detach an imported USB device.
 .PP
 
 .HP
-\fBbind\fR \-\-busid=<\fIbusid\fR>
+\fBbind\fR \-\-busid=<\fIbusid\fR> [\-\-allow=<\fICIDR mask\fR>...]
 .IP
 Make a device exportable.
+.br
+\-\-allow accepts CIDR masks like 127.0.0.0/8 or fd00::/64
+.br
+Only hosts in (at least) one of the allowed ranges are accepted. If
+\-\-allow is omitted, 0.0.0.0/0 and ::/0 are added to the list. The list can
+be read/written from corresponding \fBusbip_acl\fR file in sysfs after bind.
 .PP
 
 .HP
diff --git a/drivers/staging/usbip/userspace/src/usbip_bind.c b/drivers/staging/usbip/userspace/src/usbip_bind.c
index 9ecaf6e..d2739fc 100644
--- a/drivers/staging/usbip/userspace/src/usbip_bind.c
+++ b/drivers/staging/usbip/userspace/src/usbip_bind.c
@@ -37,8 +37,9 @@ enum unbind_status {
 
 static const char usbip_bind_usage_string[] =
 	"usbip bind <args>\n"
-	"    -b, --busid=<busid>    Bind " USBIP_HOST_DRV_NAME ".ko to device "
-	"on <busid>\n";
+	"    -b, --busid=<busid>        Bind " USBIP_HOST_DRV_NAME ".ko to "
+	"device on <busid>\n"
+	"    -a, --allow=<CIDR mask>    Restrict device access to <CIDR mask>\n";
 
 void usbip_bind_usage(void)
 {
@@ -46,17 +47,19 @@ void usbip_bind_usage(void)
 }
 
 /* call at unbound state */
-static int bind_usbip(char *busid)
+static int bind_usbip(char *busid, char *allow)
 {
 	char bus_type[] = "usb";
 	char attr_name[] = "bind";
 	char sysfs_mntpath[SYSFS_PATH_MAX];
 	char bind_attr_path[SYSFS_PATH_MAX];
 	char intf_busid[SYSFS_BUS_ID_SIZE];
+	char ip_attr_path[SYSFS_PATH_MAX];
 	struct sysfs_device *busid_dev;
 	struct sysfs_attribute *bind_attr;
 	struct sysfs_attribute *bConfValue;
 	struct sysfs_attribute *bNumIntfs;
+	struct sysfs_attribute *usbip_ip;
 	int i, failed = 0;
 	int rc, ret = -1;
 
@@ -101,8 +104,32 @@ static int bind_usbip(char *busid)
 			dbg("bind driver at %s failed", intf_busid);
 			failed = 1;
 		}
+
+	}
+
+	/*
+	 * store allowed IP ranges
+	 * specified by `usbip bind -b <busid> --allow <CIDR mask>`
+	 */
+	snprintf(ip_attr_path, sizeof(ip_attr_path),
+			"%s/%s/%s/%s/%s/%s:%.1s.%d/%s",
+			sysfs_mntpath, SYSFS_BUS_NAME, bus_type,
+			SYSFS_DRIVERS_NAME, USBIP_HOST_DRV_NAME, busid,
+			bConfValue->value, 0, "usbip_acl");
+
+	usbip_ip = sysfs_open_attribute(ip_attr_path);
+	if (!usbip_ip) {
+		err("sysfs_open_attribute failed: path=%s",
+				ip_attr_path);
+		goto err_close_busid_dev;
 	}
 
+	rc = sysfs_write_attribute(usbip_ip, allow, strlen(allow));
+	if (rc)
+		err("sysfs_write_attribute failed");
+
+	sysfs_close_attribute(usbip_ip);
+
 	if (!failed)
 		ret = 0;
 
@@ -213,7 +240,7 @@ out:
 	return status;
 }
 
-static int bind_device(char *busid)
+static int bind_device(char *busid, char *allow)
 {
 	int rc;
 
@@ -233,7 +260,7 @@ static int bind_device(char *busid)
 		return -1;
 	}
 
-	rc = bind_usbip(busid);
+	rc = bind_usbip(busid, allow);
 	if (rc < 0) {
 		err("could not bind device to %s", USBIP_HOST_DRV_NAME);
 		modify_match_busid(busid, 0);
@@ -249,29 +276,52 @@ int usbip_bind(int argc, char *argv[])
 {
 	static const struct option opts[] = {
 		{ "busid", required_argument, NULL, 'b' },
+		{ "allow", required_argument, NULL, 'a' },
 		{ NULL,    0,                 NULL,  0  }
 	};
 
-	int opt;
-	int ret = -1;
+	int opt, rc;
+	char allow[4096];
+	char *device = NULL;
+	struct subnet subnet;
+
+	allow[0] = 0;
 
 	for (;;) {
-		opt = getopt_long(argc, argv, "b:", opts, NULL);
+		opt = getopt_long(argc, argv, "a:b:", opts, NULL);
 
 		if (opt == -1)
 			break;
 
 		switch (opt) {
+		case 'a':
+			rc = parse_cidr(optarg, &subnet);
+			if (rc < 0) {
+				err("Invalid subnet specified: %s", optarg);
+				goto err_out;
+			}
+
+			if (strlen(allow) < sizeof(allow) - strlen(optarg) - 2)
+				sprintf(allow + strlen(allow), "%s\n", optarg);
+			else
+				err("ACL length too long.");
+			break;
 		case 'b':
-			ret = bind_device(optarg);
-			goto out;
+			device = optarg;
+			break;
 		default:
 			goto err_out;
 		}
 	}
 
+	/* By default, allow access from all IPs */
+	if (!allow[0])
+		strcpy(allow, "::/0\n0.0.0.0/0\n");
+
+	if (device)
+		return bind_device(device, allow);
+
 err_out:
 	usbip_bind_usage();
-out:
-	return ret;
+	return -1;
 }
-- 
1.8.4.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv4 05/16] staging: usbip: Add support for ACLs in usbipd
  2013-10-19 14:39         ` [PATCHv4 00/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
                             ` (3 preceding siblings ...)
  2013-10-19 14:39           ` [PATCHv4 04/16] staging: usbip: Add ACL support to usbip bind Dominik Paulus
@ 2013-10-19 14:39           ` Dominik Paulus
  2013-10-19 14:39           ` [PATCHv4 06/16] staging: usbip: Add proper error reporting Dominik Paulus
                             ` (10 subsequent siblings)
  15 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-10-19 14:39 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Dominik Paulus, Kurt Kanzenbach, Tobias Polzer,
	Greg Kroah-Hartman, Anthony Foiani, devel, linux-kernel,
	linux-kernel, tobias.polzer, Ilija Hadzic

Interpret the ACLs stored in sysfs in usbipd and reject clients not
matching one of the ACLs.

Signed-off-by: Kurt Kanzenbach <ly80toro@cip.cs.fau.de>
Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/src/Makefile.am |  2 +-
 drivers/staging/usbip/userspace/src/usbipd.c    | 79 +++++++++++++++++++++++++
 2 files changed, 80 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/usbip/userspace/src/Makefile.am b/drivers/staging/usbip/userspace/src/Makefile.am
index a113003..5161bae 100644
--- a/drivers/staging/usbip/userspace/src/Makefile.am
+++ b/drivers/staging/usbip/userspace/src/Makefile.am
@@ -9,4 +9,4 @@ usbip_SOURCES := usbip.h utils.h usbip.c utils.c usbip_network.c \
 		 usbip_bind.c usbip_unbind.c
 
 
-usbipd_SOURCES := usbip_network.h usbipd.c usbip_network.c
+usbipd_SOURCES := usbip_network.h usbipd.c usbip_network.c utils.c
diff --git a/drivers/staging/usbip/userspace/src/usbipd.c b/drivers/staging/usbip/userspace/src/usbipd.c
index 8db2f27..bc1fd19 100644
--- a/drivers/staging/usbip/userspace/src/usbipd.c
+++ b/drivers/staging/usbip/userspace/src/usbipd.c
@@ -48,6 +48,7 @@
 #include "usbip_host_driver.h"
 #include "usbip_common.h"
 #include "usbip_network.h"
+#include "utils.h"
 
 #undef  PROGNAME
 #define PROGNAME "usbipd"
@@ -169,12 +170,69 @@ static void usbipd_help(void)
 	printf("%s\n", usbipd_help_string);
 }
 
+/*
+ * Checks whether client IP matches at least one
+ * ACL entry
+ *
+ * Returns:
+ *		1  if matches
+ *		0  if not
+ *		-1 on error
+ */
+static int check_allowed(char *acls, int sockfd)
+{
+	int rc, match;
+	struct sockaddr_storage sa;
+	char *acl_cpy, *iter, *saveptr;
+	socklen_t sa_len = sizeof(sa);
+
+	rc = getpeername(sockfd, (struct sockaddr *) &sa, &sa_len);
+	if (rc || sa_len > sizeof(sa)) {
+		err("getpeername failed: %s", strerror(errno));
+		return -1;
+	}
+
+	/*
+	 * We are going to modify our argument,
+	 * thus, we need to duplicate it.
+	 */
+	acl_cpy = strdup(acls);
+	if (!acl_cpy) {
+		err("strdup(): %s", strerror(errno));
+		return -1;
+	}
+
+	match = 0;
+	iter = strtok_r(acl_cpy, "\n", &saveptr);
+	/*
+	 * Iterate over ACL entries and check for
+	 * matching one.
+	 */
+	while (iter) {
+		struct subnet net;
+
+		if (parse_cidr(iter, &net) < 0) {
+			dbg("parse_cidr() failed");
+		} else if (in_range(&sa, net)) {
+			match = 1;
+			break;
+		}
+
+		iter = strtok_r(NULL, "\n", &saveptr);
+	}
+
+	free(acl_cpy);
+	return match;
+}
+
 static int recv_request_import(int sockfd)
 {
 	struct op_import_request req;
 	struct op_common reply;
 	struct usbip_exported_device *edev;
 	struct usbip_usb_device pdu_udev;
+	struct sysfs_attribute *usbip_acl;
+	char ip_attr_path[SYSFS_PATH_MAX];
 	int found = 0;
 	int error = 0;
 	int rc;
@@ -206,6 +264,27 @@ static int recv_request_import(int sockfd)
 		rc = usbip_host_export_device(edev, sockfd);
 		if (rc < 0)
 			error = 1;
+
+		/* check for allowed IPs */
+		snprintf(ip_attr_path, sizeof(ip_attr_path), "%s/%s:%d.%d/%s",
+			edev->udev.path, edev->udev.busid,
+			edev->udev.bConfigurationValue, 0, "usbip_acl");
+
+		usbip_acl = sysfs_open_attribute(ip_attr_path);
+		if (usbip_acl) {
+			rc = sysfs_read_attribute(usbip_acl);
+			if (rc < 0) {
+				err("Unable to open sysfs");
+				error = 1;
+			} else if (check_allowed(usbip_acl->value, sockfd) != 1) {
+				info("Access denied to device %s",
+					edev->udev.busid);
+				error = 1;
+			}
+			sysfs_close_attribute(usbip_acl);
+		} else {
+			err("failed to get ip list");
+		}
 	} else {
 		info("requested device not found: %s", req.busid);
 		error = 1;
-- 
1.8.4.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv4 06/16] staging: usbip: Add proper error reporting
  2013-10-19 14:39         ` [PATCHv4 00/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
                             ` (4 preceding siblings ...)
  2013-10-19 14:39           ` [PATCHv4 05/16] staging: usbip: Add support for ACLs in usbipd Dominik Paulus
@ 2013-10-19 14:39           ` Dominik Paulus
  2013-10-25 13:26             ` Dan Carpenter
  2013-10-19 14:39           ` [PATCHv4 07/16] staging: usbip: Handle usbip being started as user Dominik Paulus
                             ` (9 subsequent siblings)
  15 siblings, 1 reply; 53+ messages in thread
From: Dominik Paulus @ 2013-10-19 14:39 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Dominik Paulus, Tobias Polzer, Greg Kroah-Hartman,
	Anthony Foiani, devel, linux-kernel, Kurt Kanzenbach,
	linux-kernel, tobias.polzer, Ilija Hadzic

This patch adds new error codes and features extended error reporting in
op_common packets.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/src/usbip_attach.c |  4 +-
 drivers/staging/usbip/userspace/src/usbip_list.c   |  3 +-
 .../staging/usbip/userspace/src/usbip_network.c    | 50 ++++++++++++++++------
 .../staging/usbip/userspace/src/usbip_network.h    | 17 +++++++-
 drivers/staging/usbip/userspace/src/usbipd.c       | 29 +++++++------
 5 files changed, 74 insertions(+), 29 deletions(-)

diff --git a/drivers/staging/usbip/userspace/src/usbip_attach.c b/drivers/staging/usbip/userspace/src/usbip_attach.c
index 2363e56..2a3f313 100644
--- a/drivers/staging/usbip/userspace/src/usbip_attach.c
+++ b/drivers/staging/usbip/userspace/src/usbip_attach.c
@@ -147,7 +147,7 @@ static int query_import_device(int sockfd, char *busid)
 	/* receive a reply */
 	rc = usbip_net_recv_op_common(sockfd, &code);
 	if (rc < 0) {
-		err("recv op_common");
+		err("recv op_common: %s", usbip_net_strerror(rc));
 		return -1;
 	}
 
@@ -177,7 +177,7 @@ static int attach_device(char *host, char *busid)
 
 	sockfd = usbip_net_connect(host);
 	if (sockfd < 0) {
-		err("tcp connect");
+		err("connection attempt failed");
 		return -1;
 	}
 
diff --git a/drivers/staging/usbip/userspace/src/usbip_list.c b/drivers/staging/usbip/userspace/src/usbip_list.c
index e4fa5b8..ff7acf8 100644
--- a/drivers/staging/usbip/userspace/src/usbip_list.c
+++ b/drivers/staging/usbip/userspace/src/usbip_list.c
@@ -64,7 +64,8 @@ static int get_exported_devices(char *host, int sockfd)
 
 	rc = usbip_net_recv_op_common(sockfd, &code);
 	if (rc < 0) {
-		dbg("usbip_net_recv_op_common failed");
+		err("usbip_net_recv_op_common failed: %s",
+			usbip_net_strerror(rc));
 		return -1;
 	}
 
diff --git a/drivers/staging/usbip/userspace/src/usbip_network.c b/drivers/staging/usbip/userspace/src/usbip_network.c
index 9425cb6..e573cb2 100644
--- a/drivers/staging/usbip/userspace/src/usbip_network.c
+++ b/drivers/staging/usbip/userspace/src/usbip_network.c
@@ -181,7 +181,7 @@ int usbip_net_recv_op_common(int sockfd, uint16_t *code)
 	rc = usbip_net_recv(sockfd, &op_common, sizeof(op_common));
 	if (rc < 0) {
 		dbg("usbip_net_recv failed: %d", rc);
-		goto err;
+		return -ERR_SYSERR;
 	}
 
 	PACK_OP_COMMON(0, &op_common);
@@ -189,30 +189,48 @@ int usbip_net_recv_op_common(int sockfd, uint16_t *code)
 	if (op_common.version != USBIP_VERSION) {
 		dbg("version mismatch: %d %d", op_common.version,
 		    USBIP_VERSION);
-		goto err;
+		return -ERR_MISMATCH;
 	}
 
 	switch (*code) {
 	case OP_UNSPEC:
 		break;
 	default:
-		if (op_common.code != *code) {
+		/*
+		 * Only accept expected opcode. Exception: OP_REPLY
+		 * flag set may be sent as a reply to all requests,
+		 * if only used for status reporting.
+		 */
+		if (op_common.code != *code && op_common.code != OP_REPLY) {
 			dbg("unexpected pdu %#0x for %#0x", op_common.code,
 			    *code);
-			goto err;
+			return -ERR_UNEXPECTED;
 		}
 	}
 
-	if (op_common.status != ST_OK) {
-		dbg("request failed at peer: %d", op_common.status);
-		goto err;
-	}
-
 	*code = op_common.code;
 
-	return 0;
-err:
-	return -1;
+	return -op_common.status;
+}
+
+const char *usbip_net_strerror(int status)
+{
+	static const char *const errs[] = {
+		/* ERR_OK */ "Success",
+		/* ERR_NA */ "Command failed",
+		/* ERR_MISMATCH */ "Protocol version mismatch",
+		/* ERR_SYSERR */ "System error",
+		/* ERR_UNEXPECTED */ "Unexpected opcode received",
+		/* ERR_AUTHREQ */ "Server requires authentication",
+		/* ERR_PERM */ "Permission denied",
+		/* ERR_NOTFOUND */ "Requested device not found",
+		/* ERR_NOAUTH */ "Server doesn't support authentication"
+	};
+	if (status < 0)
+		status = -status;
+	if (status >= (int) (sizeof(errs) / sizeof(*errs)))
+		return "Invalid";
+	return errs[status];
 }
 
 int usbip_net_set_reuseaddr(int sockfd)
@@ -363,6 +381,7 @@ int usbip_net_connect(char *hostname)
 #ifdef HAVE_GNUTLS
 	if (usbip_srp_password) {
 		int rc;
+		uint16_t code = OP_REP_STARTTLS;
 
 		rc = usbip_net_send_op_common(sockfd, OP_REQ_STARTTLS, 0);
 		if (rc < 0) {
@@ -370,6 +389,13 @@ int usbip_net_connect(char *hostname)
 			return EAI_SYSTEM;
 		}
 
+		rc = usbip_net_recv_op_common(sockfd, &code);
+		if (rc < 0) {
+			err("STARTTLS attempt failed: %s",
+				usbip_net_strerror(rc));
+			return -1;
+		}
+
 		rc = usbip_net_srp_handshake(sockfd);
 		if (rc < 0) {
 			err("Unable to perform TLS handshake (wrong password?): %s",
diff --git a/drivers/staging/usbip/userspace/src/usbip_network.h b/drivers/staging/usbip/userspace/src/usbip_network.h
index 758656b..d3c1b71 100644
--- a/drivers/staging/usbip/userspace/src/usbip_network.h
+++ b/drivers/staging/usbip/userspace/src/usbip_network.h
@@ -29,8 +29,15 @@ struct op_common {
 	uint16_t code;
 
 	/* add more error code */
-#define ST_OK	0x00
-#define ST_NA	0x01
+#define ERR_OK         0x00
+#define ERR_NA         0x01
+#define ERR_MISMATCH   0x02
+#define ERR_SYSERR     0x03
+#define ERR_UNEXPECTED 0x04
+#define ERR_AUTHREQ    0x05
+#define ERR_PERM       0x06
+#define ERR_NOTFOUND   0x07
+#define ERR_NOAUTH     0x08
 	uint32_t status; /* op_code status (for reply) */
 
 } __attribute__((packed));
@@ -179,10 +186,16 @@ void usbip_net_pack_uint32_t(int pack, uint32_t *num);
 void usbip_net_pack_uint16_t(int pack, uint16_t *num);
 void usbip_net_pack_usb_device(int pack, struct usbip_usb_device *udev);
 void usbip_net_pack_usb_interface(int pack, struct usbip_usb_interface *uinf);
+const char *usbip_net_strerror(int status);
 
 ssize_t usbip_net_recv(int sockfd, void *buff, size_t bufflen);
 ssize_t usbip_net_send(int sockfd, void *buff, size_t bufflen);
 int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status);
+/*
+ * Receive opcode.
+ * Returns: 0 on success, negative error code (that may be passed to
+ * usbip_net_strerror) on failure.
+ */
 int usbip_net_recv_op_common(int sockfd, uint16_t *code);
 int usbip_net_set_reuseaddr(int sockfd);
 int usbip_net_set_nodelay(int sockfd);
diff --git a/drivers/staging/usbip/userspace/src/usbipd.c b/drivers/staging/usbip/userspace/src/usbipd.c
index bc1fd19..ae572c6 100644
--- a/drivers/staging/usbip/userspace/src/usbipd.c
+++ b/drivers/staging/usbip/userspace/src/usbipd.c
@@ -234,7 +234,7 @@ static int recv_request_import(int sockfd)
 	struct sysfs_attribute *usbip_acl;
 	char ip_attr_path[SYSFS_PATH_MAX];
 	int found = 0;
-	int error = 0;
+	int error = ERR_OK;
 	int rc;
 
 	memset(&req, 0, sizeof(req));
@@ -263,7 +263,7 @@ static int recv_request_import(int sockfd)
 		/* export device needs a TCP/IP socket descriptor */
 		rc = usbip_host_export_device(edev, sockfd);
 		if (rc < 0)
-			error = 1;
+			error = ERR_SYSERR;
 
 		/* check for allowed IPs */
 		snprintf(ip_attr_path, sizeof(ip_attr_path), "%s/%s:%d.%d/%s",
@@ -275,11 +275,11 @@ static int recv_request_import(int sockfd)
 			rc = sysfs_read_attribute(usbip_acl);
 			if (rc < 0) {
 				err("Unable to open sysfs");
-				error = 1;
+				error = ERR_SYSERR;
 			} else if (check_allowed(usbip_acl->value, sockfd) != 1) {
 				info("Access denied to device %s",
 					edev->udev.busid);
-				error = 1;
+				error = ERR_PERM;
 			}
 			sysfs_close_attribute(usbip_acl);
 		} else {
@@ -287,17 +287,16 @@ static int recv_request_import(int sockfd)
 		}
 	} else {
 		info("requested device not found: %s", req.busid);
-		error = 1;
+		error = ERR_NOTFOUND;
 	}
 
-	rc = usbip_net_send_op_common(sockfd, OP_REP_IMPORT,
-				      (!error ? ST_OK : ST_NA));
+	rc = usbip_net_send_op_common(sockfd, OP_REP_IMPORT, error);
 	if (rc < 0) {
 		dbg("usbip_net_send_op_common failed: %#0x", OP_REP_IMPORT);
 		return -1;
 	}
 
-	if (error) {
+	if (error != 0) {
 		dbg("import request busid %s: failed", req.busid);
 		return -1;
 	}
@@ -333,7 +332,7 @@ static int send_reply_devlist(int connfd)
 	}
 	info("exportable devices: %d", reply.ndev);
 
-	rc = usbip_net_send_op_common(connfd, OP_REP_DEVLIST, ST_OK);
+	rc = usbip_net_send_op_common(connfd, OP_REP_DEVLIST, ERR_OK);
 	if (rc < 0) {
 		dbg("usbip_net_send_op_common failed: %#0x", OP_REP_DEVLIST);
 		return -1;
@@ -416,7 +415,8 @@ static int recv_pdu(int connfd)
 
 		ret = usbip_net_recv_op_common(connfd, &code);
 		if (ret < 0) {
-			dbg("could not receive opcode: %#0x", code);
+			dbg("could not receive opcode: %#0x: %s", code,
+				usbip_net_strerror(ret));
 			return -1;
 		}
 
@@ -424,7 +424,8 @@ static int recv_pdu(int connfd)
 
 		/* We require an authenticated encryption */
 		if (!auth && code != OP_REQ_STARTTLS) {
-			usbip_net_send_op_common(connfd, OP_REPLY, ST_NA);
+			info("Unauthenticated connection attempt");
+			usbip_net_send_op_common(connfd, OP_REPLY, ERR_AUTHREQ);
 			return -1;
 		}
 
@@ -432,10 +433,14 @@ static int recv_pdu(int connfd)
 #ifdef HAVE_GNUTLS
 		case OP_REQ_STARTTLS:
 			if (!need_auth) {
-				ret = -1;
+				usbip_net_send_op_common(connfd, OP_REPLY,
+					ERR_NOAUTH);
 				err("Unexpected TLS handshake attempt (client "
 					"uses password, server doesn't)");
+				ret = -1;
 			} else {
+				usbip_net_send_op_common(connfd, OP_REPLY,
+					ERR_OK);
 				ret = net_srp_server_handshake(connfd);
 				if (ret != 0)
 					err("TLS handshake failed");
-- 
1.8.4.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv4 07/16] staging: usbip: Handle usbip being started as user
  2013-10-19 14:39         ` [PATCHv4 00/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
                             ` (5 preceding siblings ...)
  2013-10-19 14:39           ` [PATCHv4 06/16] staging: usbip: Add proper error reporting Dominik Paulus
@ 2013-10-19 14:39           ` Dominik Paulus
  2013-10-19 14:39           ` [PATCHv4 08/16] staging: usbip: Improve debug output Dominik Paulus
                             ` (8 subsequent siblings)
  15 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-10-19 14:39 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Dominik Paulus, Tobias Polzer, Greg Kroah-Hartman,
	Anthony Foiani, devel, linux-kernel, Kurt Kanzenbach,
	linux-kernel, tobias.polzer, Ilija Hadzic

usbip now prints an error message when started as user and requiring
root access. Also, some debug messages are changed to error messages so
the command line utilities now print less confusing (and more verbose)
error messages when not used correctly.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/src/usbip_attach.c |  3 +++
 drivers/staging/usbip/userspace/src/usbip_bind.c   | 16 ++++++++++------
 2 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/drivers/staging/usbip/userspace/src/usbip_attach.c b/drivers/staging/usbip/userspace/src/usbip_attach.c
index 2a3f313..651e93a 100644
--- a/drivers/staging/usbip/userspace/src/usbip_attach.c
+++ b/drivers/staging/usbip/userspace/src/usbip_attach.c
@@ -210,6 +210,9 @@ int usbip_attach(int argc, char *argv[])
 	int opt;
 	int ret = -1;
 
+	if (geteuid() != 0)
+		err("not running as root?");
+
 	for (;;) {
 		opt = getopt_long(argc, argv, "r:b:", opts, NULL);
 
diff --git a/drivers/staging/usbip/userspace/src/usbip_bind.c b/drivers/staging/usbip/userspace/src/usbip_bind.c
index d2739fc..ab26b30f 100644
--- a/drivers/staging/usbip/userspace/src/usbip_bind.c
+++ b/drivers/staging/usbip/userspace/src/usbip_bind.c
@@ -158,7 +158,7 @@ static int unbind_other(char *busid)
 
 	busid_dev = sysfs_open_device(bus_type, busid);
 	if (!busid_dev) {
-		dbg("sysfs_open_device %s failed: %s", busid, strerror(errno));
+		err("sysfs_open_device %s failed: %s", busid, strerror(errno));
 		return -1;
 	}
 
@@ -166,7 +166,7 @@ static int unbind_other(char *busid)
 	bDevClass  = sysfs_get_device_attr(busid_dev, "bDeviceClass");
 	bNumIntfs  = sysfs_get_device_attr(busid_dev, "bNumInterfaces");
 	if (!bConfValue || !bDevClass || !bNumIntfs) {
-		dbg("problem getting device attributes: %s",
+		err("problem getting device attributes: %s",
 		    strerror(errno));
 		goto err_close_busid_dev;
 	}
@@ -181,7 +181,7 @@ static int unbind_other(char *busid)
 			 bConfValue->value, i);
 		intf_dev = sysfs_open_device(bus_type, intf_busid);
 		if (!intf_dev) {
-			dbg("could not open interface device: %s",
+			err("could not open interface device: %s",
 			    strerror(errno));
 			goto err_close_busid_dev;
 		}
@@ -202,14 +202,14 @@ static int unbind_other(char *busid)
 		/* unbinding */
 		intf_drv = sysfs_open_driver(bus_type, intf_dev->driver_name);
 		if (!intf_drv) {
-			dbg("could not open interface driver on %s: %s",
+			err("could not open interface driver on %s: %s",
 			    intf_dev->name, strerror(errno));
 			goto err_close_intf_dev;
 		}
 
 		unbind_attr = sysfs_get_driver_attr(intf_drv, "unbind");
 		if (!unbind_attr) {
-			dbg("problem getting interface driver attribute: %s",
+			err("problem getting interface driver attribute: %s",
 			    strerror(errno));
 			goto err_close_intf_drv;
 		}
@@ -218,7 +218,8 @@ static int unbind_other(char *busid)
 					   SYSFS_BUS_ID_SIZE);
 		if (rc < 0) {
 			/* NOTE: why keep unbinding other interfaces? */
-			dbg("unbind driver at %s failed", intf_dev->bus_id);
+			err("unbind driver at %s failed: %s", intf_dev->bus_id,
+					strerror(errno));
 			status = UNBIND_ST_FAILED;
 		}
 
@@ -287,6 +288,9 @@ int usbip_bind(int argc, char *argv[])
 
 	allow[0] = 0;
 
+	if (geteuid() != 0)
+		err("not running as root?");
+
 	for (;;) {
 		opt = getopt_long(argc, argv, "a:b:", opts, NULL);
 
-- 
1.8.4.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv4 08/16] staging: usbip: Improve debug output
  2013-10-19 14:39         ` [PATCHv4 00/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
                             ` (6 preceding siblings ...)
  2013-10-19 14:39           ` [PATCHv4 07/16] staging: usbip: Handle usbip being started as user Dominik Paulus
@ 2013-10-19 14:39           ` Dominik Paulus
  2013-10-19 14:39           ` [PATCHv4 09/16] staging: usbip: Separate protocol/program version Dominik Paulus
                             ` (7 subsequent siblings)
  15 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-10-19 14:39 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Dominik Paulus, Tobias Polzer, Greg Kroah-Hartman,
	Anthony Foiani, devel, linux-kernel, Kurt Kanzenbach,
	linux-kernel, tobias.polzer, Ilija Hadzic

For IPv6, IP:Port is unreadable.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/src/usbipd.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/staging/usbip/userspace/src/usbipd.c b/drivers/staging/usbip/userspace/src/usbipd.c
index ae572c6..6550460 100644
--- a/drivers/staging/usbip/userspace/src/usbipd.c
+++ b/drivers/staging/usbip/userspace/src/usbipd.c
@@ -519,7 +519,7 @@ static int do_accept(int listenfd)
 		return -1;
 	}
 #endif
-	info("connection from %s:%s", host, port);
+	info("connection from %s, port %s", host, port);
 
 	return connfd;
 }
-- 
1.8.4.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv4 09/16] staging: usbip: Separate protocol/program version
  2013-10-19 14:39         ` [PATCHv4 00/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
                             ` (7 preceding siblings ...)
  2013-10-19 14:39           ` [PATCHv4 08/16] staging: usbip: Improve debug output Dominik Paulus
@ 2013-10-19 14:39           ` Dominik Paulus
  2013-10-19 14:39           ` [PATCHv4 10/16] staging: usbip: TLS for all userspace communication Dominik Paulus
                             ` (6 subsequent siblings)
  15 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-10-19 14:39 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Dominik Paulus, Tobias Polzer, Greg Kroah-Hartman,
	Anthony Foiani, devel, linux-kernel, Kurt Kanzenbach,
	linux-kernel, tobias.polzer, Ilija Hadzic

Not all new program versions necessarily introduce
non-backwards-compatible protocol changes. We thus move the definition
of the protocol version from configure.ac to usbip_network.h, where it
logically belongs to.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/configure.ac        | 1 -
 drivers/staging/usbip/userspace/src/usbip_network.c | 6 +++---
 drivers/staging/usbip/userspace/src/usbip_network.h | 6 ++++++
 3 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/drivers/staging/usbip/userspace/configure.ac b/drivers/staging/usbip/userspace/configure.ac
index 2208516..bc08b81 100644
--- a/drivers/staging/usbip/userspace/configure.ac
+++ b/drivers/staging/usbip/userspace/configure.ac
@@ -2,7 +2,6 @@ dnl Process this file with autoconf to produce a configure script.
 
 AC_PREREQ(2.59)
 AC_INIT([usbip-utils], [1.1.1], [linux-usb@vger.kernel.org])
-AC_DEFINE([USBIP_VERSION], [0x00000111], [binary-coded decimal version number])
 
 CURRENT=0
 REVISION=1
diff --git a/drivers/staging/usbip/userspace/src/usbip_network.c b/drivers/staging/usbip/userspace/src/usbip_network.c
index e573cb2..a606e2b 100644
--- a/drivers/staging/usbip/userspace/src/usbip_network.c
+++ b/drivers/staging/usbip/userspace/src/usbip_network.c
@@ -156,7 +156,7 @@ int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status)
 
 	memset(&op_common, 0, sizeof(op_common));
 
-	op_common.version = USBIP_VERSION;
+	op_common.version = PROTOCOL_VERSION;
 	op_common.code    = code;
 	op_common.status  = status;
 
@@ -186,9 +186,9 @@ int usbip_net_recv_op_common(int sockfd, uint16_t *code)
 
 	PACK_OP_COMMON(0, &op_common);
 
-	if (op_common.version != USBIP_VERSION) {
+	if (op_common.version != PROTOCOL_VERSION) {
 		dbg("version mismatch: %d %d", op_common.version,
-		    USBIP_VERSION);
+		    PROTOCOL_VERSION);
 		return -ERR_MISMATCH;
 	}
 
diff --git a/drivers/staging/usbip/userspace/src/usbip_network.h b/drivers/staging/usbip/userspace/src/usbip_network.h
index d3c1b71..6a41fd8 100644
--- a/drivers/staging/usbip/userspace/src/usbip_network.h
+++ b/drivers/staging/usbip/userspace/src/usbip_network.h
@@ -14,6 +14,12 @@
 
 #include <stdint.h>
 
+/*
+ * Protocol version. Incremented only on non-backwards-compatible
+ * changes.
+ */
+#define PROTOCOL_VERSION 0x111
+
 extern int usbip_port;
 extern char *usbip_port_string;
 extern char *usbip_srp_password;
-- 
1.8.4.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv4 10/16] staging: usbip: TLS for all userspace communication
  2013-10-19 14:39         ` [PATCHv4 00/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
                             ` (8 preceding siblings ...)
  2013-10-19 14:39           ` [PATCHv4 09/16] staging: usbip: Separate protocol/program version Dominik Paulus
@ 2013-10-19 14:39           ` Dominik Paulus
  2013-10-25 14:45             ` Dan Carpenter
  2013-10-19 14:39           ` [PATCHv4 11/16] staging: usbip: Exchange session keys in userspace Dominik Paulus
                             ` (5 subsequent siblings)
  15 siblings, 1 reply; 53+ messages in thread
From: Dominik Paulus @ 2013-10-19 14:39 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Dominik Paulus, Tobias Polzer, Greg Kroah-Hartman,
	Anthony Foiani, devel, linux-kernel, Kurt Kanzenbach,
	linux-kernel, tobias.polzer, Ilija Hadzic

This patch extends the TLS support to cover all communication in
userspace. The TLS connection is released shortly before the socket is
passed to the kernel.

This requires for additional connection state to be passed between
functions. We thus replaced the sockfd by a struct containing the TLS
context as well as the fd.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/userspace/src/usbip_attach.c |  24 +-
 drivers/staging/usbip/userspace/src/usbip_list.c   |  24 +-
 .../staging/usbip/userspace/src/usbip_network.c    | 279 +++++++++++++++++----
 .../staging/usbip/userspace/src/usbip_network.h    |  49 +++-
 drivers/staging/usbip/userspace/src/usbipd.c       | 244 ++++--------------
 5 files changed, 358 insertions(+), 262 deletions(-)

diff --git a/drivers/staging/usbip/userspace/src/usbip_attach.c b/drivers/staging/usbip/userspace/src/usbip_attach.c
index 651e93a..25c68e2 100644
--- a/drivers/staging/usbip/userspace/src/usbip_attach.c
+++ b/drivers/staging/usbip/userspace/src/usbip_attach.c
@@ -86,7 +86,8 @@ static int record_connection(char *host, char *port, char *busid, int rhport)
 	return 0;
 }
 
-static int import_device(int sockfd, struct usbip_usb_device *udev)
+static int import_device(struct usbip_connection *conn,
+			 struct usbip_usb_device *udev)
 {
 	int rc;
 	int port;
@@ -104,8 +105,10 @@ static int import_device(int sockfd, struct usbip_usb_device *udev)
 		return -1;
 	}
 
-	rc = usbip_vhci_attach_device(port, sockfd, udev->busnum,
+	usbip_net_bye(conn);
+	rc = usbip_vhci_attach_device(port, conn->sockfd, udev->busnum,
 				      udev->devnum, udev->speed);
+
 	if (rc < 0) {
 		err("import device");
 		usbip_vhci_driver_close();
@@ -117,7 +120,7 @@ static int import_device(int sockfd, struct usbip_usb_device *udev)
 	return port;
 }
 
-static int query_import_device(int sockfd, char *busid)
+static int query_import_device(struct usbip_connection *conn, char *busid)
 {
 	int rc;
 	struct op_import_request request;
@@ -128,7 +131,7 @@ static int query_import_device(int sockfd, char *busid)
 	memset(&reply, 0, sizeof(reply));
 
 	/* send a request */
-	rc = usbip_net_send_op_common(sockfd, OP_REQ_IMPORT, 0);
+	rc = usbip_net_send_op_common(conn, OP_REQ_IMPORT, 0);
 	if (rc < 0) {
 		err("send op_common");
 		return -1;
@@ -138,20 +141,20 @@ static int query_import_device(int sockfd, char *busid)
 
 	PACK_OP_IMPORT_REQUEST(0, &request);
 
-	rc = usbip_net_send(sockfd, (void *) &request, sizeof(request));
+	rc = usbip_net_send(conn, (void *) &request, sizeof(request));
 	if (rc < 0) {
 		err("send op_import_request");
 		return -1;
 	}
 
 	/* receive a reply */
-	rc = usbip_net_recv_op_common(sockfd, &code);
+	rc = usbip_net_recv_op_common(conn, &code);
 	if (rc < 0) {
 		err("recv op_common: %s", usbip_net_strerror(rc));
 		return -1;
 	}
 
-	rc = usbip_net_recv(sockfd, (void *) &reply, sizeof(reply));
+	rc = usbip_net_recv(conn, (void *) &reply, sizeof(reply));
 	if (rc < 0) {
 		err("recv op_import_reply");
 		return -1;
@@ -166,7 +169,7 @@ static int query_import_device(int sockfd, char *busid)
 	}
 
 	/* import a device */
-	return import_device(sockfd, &reply.udev);
+	return import_device(conn, &reply.udev);
 }
 
 static int attach_device(char *host, char *busid)
@@ -174,14 +177,15 @@ static int attach_device(char *host, char *busid)
 	int sockfd;
 	int rc;
 	int rhport;
+	struct usbip_connection conn;
 
-	sockfd = usbip_net_connect(host);
+	sockfd = usbip_net_connect(host, &conn);
 	if (sockfd < 0) {
 		err("connection attempt failed");
 		return -1;
 	}
 
-	rhport = query_import_device(sockfd, busid);
+	rhport = query_import_device(&conn, busid);
 	if (rhport < 0) {
 		err("query");
 		return -1;
diff --git a/drivers/staging/usbip/userspace/src/usbip_list.c b/drivers/staging/usbip/userspace/src/usbip_list.c
index ff7acf8..187eb7d 100644
--- a/drivers/staging/usbip/userspace/src/usbip_list.c
+++ b/drivers/staging/usbip/userspace/src/usbip_list.c
@@ -45,7 +45,7 @@ void usbip_list_usage(void)
 	printf("usage: %s", usbip_list_usage_string);
 }
 
-static int get_exported_devices(char *host, int sockfd)
+static int get_exported_devices(char *host, struct usbip_connection *conn)
 {
 	char product_name[100];
 	char class_name[100];
@@ -56,13 +56,13 @@ static int get_exported_devices(char *host, int sockfd)
 	unsigned int i;
 	int j, rc;
 
-	rc = usbip_net_send_op_common(sockfd, OP_REQ_DEVLIST, 0);
+	rc = usbip_net_send_op_common(conn, OP_REQ_DEVLIST, 0);
 	if (rc < 0) {
 		dbg("usbip_net_send_op_common failed");
 		return -1;
 	}
 
-	rc = usbip_net_recv_op_common(sockfd, &code);
+	rc = usbip_net_recv_op_common(conn, &code);
 	if (rc < 0) {
 		err("usbip_net_recv_op_common failed: %s",
 			usbip_net_strerror(rc));
@@ -70,7 +70,7 @@ static int get_exported_devices(char *host, int sockfd)
 	}
 
 	memset(&reply, 0, sizeof(reply));
-	rc = usbip_net_recv(sockfd, &reply, sizeof(reply));
+	rc = usbip_net_recv(conn, &reply, sizeof(reply));
 	if (rc < 0) {
 		dbg("usbip_net_recv_op_devlist failed");
 		return -1;
@@ -89,7 +89,7 @@ static int get_exported_devices(char *host, int sockfd)
 
 	for (i = 0; i < reply.ndev; i++) {
 		memset(&udev, 0, sizeof(udev));
-		rc = usbip_net_recv(sockfd, &udev, sizeof(udev));
+		rc = usbip_net_recv(conn, &udev, sizeof(udev));
 		if (rc < 0) {
 			dbg("usbip_net_recv failed: usbip_usb_device[%d]", i);
 			return -1;
@@ -106,7 +106,7 @@ static int get_exported_devices(char *host, int sockfd)
 		printf("%11s: %s\n", "", class_name);
 
 		for (j = 0; j < udev.bNumInterfaces; j++) {
-			rc = usbip_net_recv(sockfd, &uintf, sizeof(uintf));
+			rc = usbip_net_recv(conn, &uintf, sizeof(uintf));
 			if (rc < 0) {
 				dbg("usbip_net_recv failed: usbip_usb_intf[%d]",
 				    j);
@@ -130,23 +130,23 @@ static int get_exported_devices(char *host, int sockfd)
 static int list_exported_devices(char *host)
 {
 	int rc;
-	int sockfd;
+	struct usbip_connection conn;
 
-	sockfd = usbip_net_connect(host);
-	if (sockfd < 0) {
+	rc = usbip_net_connect(host, &conn);
+	if (rc < 0) {
 		err("could not connect to %s:%s: %s", host,
-		    usbip_port_string, gai_strerror(sockfd));
+		    usbip_port_string, usbip_net_strerror(rc));
 		return -1;
 	}
 	dbg("connected to %s:%s", host, usbip_port_string);
 
-	rc = get_exported_devices(host, sockfd);
+	rc = get_exported_devices(host, &conn);
 	if (rc < 0) {
 		err("failed to get device list from %s", host);
 		return -1;
 	}
 
-	close(sockfd);
+	usbip_net_bye(&conn);
 
 	return 0;
 }
diff --git a/drivers/staging/usbip/userspace/src/usbip_network.c b/drivers/staging/usbip/userspace/src/usbip_network.c
index a606e2b..22fa680 100644
--- a/drivers/staging/usbip/userspace/src/usbip_network.c
+++ b/drivers/staging/usbip/userspace/src/usbip_network.c
@@ -24,6 +24,7 @@
 #include <netdb.h>
 #include <netinet/tcp.h>
 #include <unistd.h>
+#include <assert.h>
 
 #ifdef HAVE_LIBWRAP
 #include <tcpd.h>
@@ -32,6 +33,7 @@
 #include "../config.h"
 #ifdef HAVE_GNUTLS
 #include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
 #endif
 
 #include "usbip_common.h"
@@ -112,8 +114,8 @@ void usbip_net_pack_usb_interface(int pack __attribute__((unused)),
 	/* uint8_t members need nothing */
 }
 
-static ssize_t usbip_net_xmit(int sockfd, void *buff, size_t bufflen,
-			      int sending)
+static ssize_t usbip_net_xmit(struct usbip_connection *conn, void *buff,
+			      size_t bufflen, int sending)
 {
 	ssize_t nbytes;
 	ssize_t total = 0;
@@ -122,10 +124,22 @@ static ssize_t usbip_net_xmit(int sockfd, void *buff, size_t bufflen,
 		return 0;
 
 	do {
-		if (sending)
-			nbytes = send(sockfd, buff, bufflen, 0);
+		if (!conn->have_crypto && sending)
+			nbytes = send(conn->sockfd, buff, bufflen, 0);
+		else if (!conn->have_crypto && !sending)
+			nbytes = recv(conn->sockfd, buff, bufflen, MSG_WAITALL);
+#ifdef HAVE_GNUTLS
+		else if (sending)
+			nbytes = gnutls_record_send(conn->session, buff, bufflen);
 		else
-			nbytes = recv(sockfd, buff, bufflen, MSG_WAITALL);
+			nbytes = gnutls_record_recv(conn->session, buff, bufflen);
+#else
+		/*
+		 * Assertion to let gcc be able to infer proper initialization
+		 * of nbytes.
+		 */
+		assert(!conn->have_crypto);
+#endif
 
 		if (nbytes <= 0)
 			return -1;
@@ -139,17 +153,20 @@ static ssize_t usbip_net_xmit(int sockfd, void *buff, size_t bufflen,
 	return total;
 }
 
-ssize_t usbip_net_recv(int sockfd, void *buff, size_t bufflen)
+ssize_t usbip_net_recv(struct usbip_connection *conn, void *buff,
+		       size_t bufflen)
 {
-	return usbip_net_xmit(sockfd, buff, bufflen, 0);
+	return usbip_net_xmit(conn, buff, bufflen, 0);
 }
 
-ssize_t usbip_net_send(int sockfd, void *buff, size_t bufflen)
+ssize_t usbip_net_send(struct usbip_connection *conn, void *buff,
+		       size_t bufflen)
 {
-	return usbip_net_xmit(sockfd, buff, bufflen, 1);
+	return usbip_net_xmit(conn, buff, bufflen, 1);
 }
 
-int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status)
+int usbip_net_send_op_common(struct usbip_connection *conn, uint32_t code,
+			     uint32_t status)
 {
 	struct op_common op_common;
 	int rc;
@@ -162,7 +179,7 @@ int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status)
 
 	PACK_OP_COMMON(1, &op_common);
 
-	rc = usbip_net_send(sockfd, &op_common, sizeof(op_common));
+	rc = usbip_net_send(conn, &op_common, sizeof(op_common));
 	if (rc < 0) {
 		dbg("usbip_net_send failed: %d", rc);
 		return -1;
@@ -171,14 +188,15 @@ int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status)
 	return 0;
 }
 
-int usbip_net_recv_op_common(int sockfd, uint16_t *code)
+
+int usbip_net_recv_op_common(struct usbip_connection *conn, uint16_t *code)
 {
 	struct op_common op_common;
 	int rc;
 
 	memset(&op_common, 0, sizeof(op_common));
 
-	rc = usbip_net_recv(sockfd, &op_common, sizeof(op_common));
+	rc = usbip_net_recv(conn, &op_common, sizeof(op_common));
 	if (rc < 0) {
 		dbg("usbip_net_recv failed: %d", rc);
 		return -ERR_SYSERR;
@@ -224,7 +242,8 @@ const char *usbip_net_strerror(int status)
 		/* ERR_AUTHREQ */ "Server requires authentication",
 		/* ERR_PERM */ "Permission denied",
 		/* ERR_NOTFOUND */ "Requested device not found",
-		/* ERR_NOAUTH */ "Server doesn't support authentication"
+		/* ERR_NOAUTH */ "Server doesn't support authentication",
+		/* ERR_INUSE */ "Requested device is already in use"
 	};
 	if (status < 0)
 		status = -status;
@@ -250,7 +269,8 @@ int usbip_net_set_nodelay(int sockfd)
 	const int val = 1;
 	int ret;
 
-	ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
+	ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &val,
+		sizeof(val));
 	if (ret < 0)
 		dbg("setsockopt: TCP_NODELAY");
 
@@ -329,82 +349,249 @@ int usbip_net_tcp_connect(char *hostname, char *service)
 }
 
 #ifdef HAVE_GNUTLS
-int usbip_net_srp_handshake(int sockfd)
+static gnutls_datum_t usbip_net_srp_salt, usbip_net_srp_verifier;
+static gnutls_srp_server_credentials_t usbip_net_srp_cred;
+
+#define SRP_GROUP gnutls_srp_2048_group_generator
+#define SRP_PRIME gnutls_srp_2048_group_prime
+
+int usbip_net_srp_client_handshake(struct usbip_connection *conn)
 {
 	int ret;
-	gnutls_session_t session;
-	gnutls_srp_client_credentials_t srp_cred;
 
-	ret = gnutls_srp_allocate_client_credentials(&srp_cred);
+	ret = gnutls_srp_allocate_client_credentials(&conn->srp_client_cred);
 	if (ret < 0)
 		return ret;
 
-	gnutls_srp_set_client_credentials(srp_cred, "dummyuser",
+	gnutls_srp_set_client_credentials(conn->srp_client_cred, "dummyuser",
 		usbip_srp_password);
 
-	ret = gnutls_init(&session, GNUTLS_CLIENT);
+	ret = gnutls_init(&conn->session, GNUTLS_CLIENT);
 	if (ret < 0) {
-		gnutls_srp_free_client_credentials(srp_cred);
+		gnutls_srp_free_client_credentials(conn->srp_client_cred);
 		return ret;
 	}
 
-	gnutls_priority_set_direct(session, "NORMAL:+SRP", NULL);
+	gnutls_priority_set_direct(conn->session, "NORMAL:+SRP", NULL);
 
-	gnutls_credentials_set(session, GNUTLS_CRD_SRP, srp_cred);
-	gnutls_transport_set_int (session, sockfd);
+	gnutls_credentials_set(conn->session, GNUTLS_CRD_SRP,
+			conn->srp_client_cred);
+	gnutls_transport_set_int (conn->session, conn->sockfd);
 
 	do {
-		ret = gnutls_handshake(session);
+		ret = gnutls_handshake(conn->session);
 	} while (ret < 0 && !gnutls_error_is_fatal(ret));
 
-	gnutls_bye(session, GNUTLS_SHUT_RDWR);
+	return ret;
+}
 
-	gnutls_deinit(session);
-	gnutls_srp_free_client_credentials(srp_cred);
+int usbip_net_srp_server_handshake(struct usbip_connection *conn)
+{
+	int ret;
+
+	if (gnutls_init(&conn->session, GNUTLS_SERVER) != 0)
+		return -1;
+	gnutls_priority_set_direct(conn->session, "NORMAL:-KX-ALL:+SRP", NULL);
+	if (gnutls_credentials_set(conn->session, GNUTLS_CRD_SRP,
+		usbip_net_srp_cred) != 0)
+		return -1;
+
+	gnutls_transport_set_int(conn->session, conn->sockfd);
+
+	do {
+		ret = gnutls_handshake(conn->session);
+	} while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+	if (ret < 0)
+		err("GnuTLS handshake failed (%s)", gnutls_strerror(ret));
+	else
+		info("GnuTLS handshake completed");
+
+	conn->have_crypto = 1;
 
 	return ret;
 }
+
+static int net_srp_callback(gnutls_session_t sess, const char *username,
+	gnutls_datum_t *nsalt, gnutls_datum_t *nverifier, gnutls_datum_t *g,
+	gnutls_datum_t *n)
+{
+	/*
+	 * GnuTLS expects us to allocate all data returned from callbacks
+	 * using gnutls_malloc(), thus, we have to create a fresh copy of
+	 * our static credentials for every connection.
+	 */
+	nsalt->data = gnutls_malloc(usbip_net_srp_salt.size);
+	nverifier->data = gnutls_malloc(usbip_net_srp_verifier.size);
+	if (nsalt->data == NULL || nverifier->data == NULL) {
+		gnutls_free(nsalt->data);
+		gnutls_free(nverifier->data);
+		return -1;
+	}
+	nsalt->size = usbip_net_srp_salt.size;
+	nverifier->size = usbip_net_srp_verifier.size;
+	memcpy(nverifier->data, usbip_net_srp_verifier.data,
+			usbip_net_srp_verifier.size);
+	memcpy(nsalt->data, usbip_net_srp_salt.data, usbip_net_srp_salt.size);
+
+	*g = SRP_GROUP;
+	*n = SRP_PRIME;
+
+	/* We only have a single session, thus, ignore it */
+	(void) sess;
+
+	if (strcmp(username, "dummyuser"))
+		/* User invalid, stored dummy data in g and n. */
+		return 1;
+
+	return 0;
+}
+
+int usbip_net_init_gnutls(void)
+{
+	int ret;
+
+	gnutls_global_init();
+
+	usbip_net_srp_salt.data = gnutls_malloc(16);
+	if (!usbip_net_srp_salt.data)
+		return GNUTLS_E_MEMORY_ERROR;
+
+	ret = gnutls_rnd(GNUTLS_RND_NONCE, usbip_net_srp_salt.data, 16);
+	if (ret < 0)
+		return ret;
+	usbip_net_srp_salt.size = 16;
+
+	ret = gnutls_srp_allocate_server_credentials(&usbip_net_srp_cred);
+	if (ret < 0)
+		return ret;
+
+	ret = gnutls_srp_verifier("dummyuser", optarg, &usbip_net_srp_salt,
+		&SRP_GROUP, &SRP_PRIME, &usbip_net_srp_verifier);
+	if (ret < 0)
+		return ret;
+
+	gnutls_srp_set_server_credentials_function(usbip_net_srp_cred,
+		net_srp_callback);
+
+	return GNUTLS_E_SUCCESS;
+}
 #endif
 
-/*
- * Connect to the server. Performs the TCP connection attempt
- * and - if necessary - the TLS handshake used for authentication.
- */
-int usbip_net_connect(char *hostname)
+#ifdef HAVE_LIBWRAP
+static int tcpd_auth(int connfd)
 {
-	int sockfd;
+	struct request_info request;
+	int rc;
+
+	request_init(&request, RQ_DAEMON, PROGNAME, RQ_FILE, connfd, 0);
+	fromhost(&request);
+	rc = hosts_access(&request);
+	if (rc == 0)
+		return -1;
+
+	return 0;
+}
+#endif
+
+int usbip_net_accept(int listenfd, struct usbip_connection *conn)
+{
+	int connfd;
+	struct sockaddr_storage ss;
+	socklen_t len = sizeof(ss);
+	char host[NI_MAXHOST], port[NI_MAXSERV];
+	int rc;
+
+	memset(&ss, 0, sizeof(ss));
+
+	connfd = accept(listenfd, (struct sockaddr *)&ss, &len);
+	if (connfd < 0) {
+		err("failed to accept connection");
+		return -1;
+	}
+
+	rc = getnameinfo((struct sockaddr *)&ss, len, host, sizeof(host),
+			 port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV);
+	if (rc)
+		err("getnameinfo: %s", gai_strerror(rc));
+
+#ifdef HAVE_LIBWRAP
+	rc = tcpd_auth(connfd);
+	if (rc < 0) {
+		info("denied access from %s", host);
+		close(connfd);
+		return -1;
+	}
+#endif
+	info("connection from %s, port %s", host, port);
+
+	conn->sockfd = connfd;
+	conn->have_crypto = 0;
+	conn->server = 1;
+
+	return 0;
+}
+
+int usbip_net_connect(char *hostname, struct usbip_connection *conn)
+{
+	conn->sockfd = usbip_net_tcp_connect(hostname, usbip_port_string);
+	if (conn->sockfd < 0) {
+		err("TCP connection attempt failed: %s",
+				gai_strerror(conn->sockfd));
+		return ERR_SYSERR;
+	}
 
-	sockfd = usbip_net_tcp_connect(hostname, usbip_port_string);
-	if (sockfd < 0)
-		return sockfd;
+	conn->have_crypto = 0;
+	conn->server = 0;
 
 #ifdef HAVE_GNUTLS
 	if (usbip_srp_password) {
 		int rc;
 		uint16_t code = OP_REP_STARTTLS;
 
-		rc = usbip_net_send_op_common(sockfd, OP_REQ_STARTTLS, 0);
+		rc = usbip_net_send_op_common(conn, OP_REQ_STARTTLS, 0);
 		if (rc < 0) {
+			close(conn->sockfd);
 			err("usbip_net_send_op_common failed");
-			return EAI_SYSTEM;
+			return ERR_SYSERR;
 		}
 
-		rc = usbip_net_recv_op_common(sockfd, &code);
+		rc = usbip_net_recv_op_common(conn, &code);
 		if (rc < 0) {
 			err("STARTTLS attempt failed: %s",
 				usbip_net_strerror(rc));
-			return -1;
+			close(conn->sockfd);
+			return ERR_SYSERR;
 		}
 
-		rc = usbip_net_srp_handshake(sockfd);
+		rc = usbip_net_srp_client_handshake(conn);
 		if (rc < 0) {
 			err("Unable to perform TLS handshake (wrong password?): %s",
 				gnutls_strerror(rc));
-			close(sockfd);
-			return EAI_SYSTEM;
+			close(conn->sockfd);
+			return ERR_SYSERR;
 		}
+
+		conn->have_crypto = 1;
 	}
 #endif
 
-	return sockfd;
+	return 0;
+}
+
+void usbip_net_bye(struct usbip_connection *conn)
+{
+#ifdef HAVE_GNUTLS
+	if (conn->have_crypto) {
+		gnutls_bye(conn->session, GNUTLS_SHUT_RDWR);
+
+		gnutls_deinit(conn->session);
+		if (!conn->server)
+			gnutls_srp_free_client_credentials(conn->srp_client_cred);
+
+		conn->have_crypto = 0;
+	}
+#else
+	(void)conn;
+#endif
 }
diff --git a/drivers/staging/usbip/userspace/src/usbip_network.h b/drivers/staging/usbip/userspace/src/usbip_network.h
index 6a41fd8..cdc6769 100644
--- a/drivers/staging/usbip/userspace/src/usbip_network.h
+++ b/drivers/staging/usbip/userspace/src/usbip_network.h
@@ -9,6 +9,10 @@
 #include "../config.h"
 #endif
 
+#ifdef HAVE_GNUTLS
+#include <gnutls/gnutls.h>
+#endif
+
 #include <sys/types.h>
 #include <sysfs/libsysfs.h>
 
@@ -25,6 +29,19 @@ extern char *usbip_port_string;
 extern char *usbip_srp_password;
 void usbip_setup_port_number(char *arg);
 
+/*
+ * Connection handle
+ */
+struct usbip_connection {
+#ifdef HAVE_GNUTLS
+	gnutls_session_t session;
+	gnutls_srp_client_credentials_t srp_client_cred;
+#endif
+	int have_crypto;
+	int sockfd;
+	int server;
+};
+
 /* ---------------------------------------------------------------------- */
 /* Common header for all the kinds of PDUs. */
 struct op_common {
@@ -44,6 +61,7 @@ struct op_common {
 #define ERR_PERM       0x06
 #define ERR_NOTFOUND   0x07
 #define ERR_NOAUTH     0x08
+#define ERR_INUSE      0x09
 	uint32_t status; /* op_code status (for reply) */
 
 } __attribute__((packed));
@@ -194,19 +212,40 @@ void usbip_net_pack_usb_device(int pack, struct usbip_usb_device *udev);
 void usbip_net_pack_usb_interface(int pack, struct usbip_usb_interface *uinf);
 const char *usbip_net_strerror(int status);
 
-ssize_t usbip_net_recv(int sockfd, void *buff, size_t bufflen);
-ssize_t usbip_net_send(int sockfd, void *buff, size_t bufflen);
-int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status);
+ssize_t usbip_net_recv(struct usbip_connection *conn, void *buff,
+		       size_t bufflen);
+ssize_t usbip_net_send(struct usbip_connection *conn, void *buff,
+		       size_t bufflen);
+int usbip_net_send_op_common(struct usbip_connection *conn, uint32_t code,
+			     uint32_t status);
 /*
  * Receive opcode.
  * Returns: 0 on success, negative error code (that may be passed to
  * usbip_net_strerror) on failure.
  */
-int usbip_net_recv_op_common(int sockfd, uint16_t *code);
+int usbip_net_recv_op_common(struct usbip_connection *conn, uint16_t *code);
 int usbip_net_set_reuseaddr(int sockfd);
 int usbip_net_set_nodelay(int sockfd);
 int usbip_net_set_keepalive(int sockfd);
 int usbip_net_set_v6only(int sockfd);
-int usbip_net_connect(char *hostname);
+/*
+ * Connect to the server. Performs the TCP connection attempt
+ * and - if necessary - the TLS handshake used for authentication.
+ *
+ * Newly generated connection parameters are stored in the - caller
+ * allocated - usbip_connection struct conn.
+ *
+ * Returns:
+ *	0 on success
+ *	negative error code on failure
+ */
+int usbip_net_connect(char *hostname, struct usbip_connection *conn);
+int usbip_net_accept(int listenfd, struct usbip_connection *conn);
+int usbip_net_srp_server_handshake(struct usbip_connection *conn);
+/*
+ * Shuts down the TLS connection, but leaves the socket intact
+ */
+void usbip_net_bye(struct usbip_connection *conn);
+int usbip_net_init_gnutls(void);
 
 #endif /* __USBIP_NETWORK_H */
diff --git a/drivers/staging/usbip/userspace/src/usbipd.c b/drivers/staging/usbip/userspace/src/usbipd.c
index 6550460..6bd97a0 100644
--- a/drivers/staging/usbip/userspace/src/usbipd.c
+++ b/drivers/staging/usbip/userspace/src/usbipd.c
@@ -59,6 +59,7 @@
 #define DEFAULT_PID_FILE "/var/run/" PROGNAME ".pid"
 
 static const char usbip_version_string[] = PACKAGE_STRING;
+static int need_auth;
 
 static const char usbipd_help_string[] =
 	"usage: usbipd [options]\n"
@@ -93,78 +94,6 @@ static const char usbipd_help_string[] =
 	"	-v, --version\n"
 	"		Show version.\n";
 
-static int need_auth;
-#ifdef HAVE_GNUTLS
-static gnutls_datum_t srp_salt, srp_verifier;
-static gnutls_srp_server_credentials_t srp_cred;
-
-#define SRP_GROUP gnutls_srp_2048_group_generator
-#define SRP_PRIME gnutls_srp_2048_group_prime
-
-static int net_srp_callback(gnutls_session_t sess, const char *username,
-	gnutls_datum_t *nsalt, gnutls_datum_t *nverifier, gnutls_datum_t *g,
-	gnutls_datum_t *n)
-{
-	/*
-	 * GnuTLS expects us to allocate all data returned from callbacks
-	 * using gnutls_malloc(), thus, we have to create a fresh copy of
-	 * our static credentials for every connection.
-	 */
-	nsalt->data = gnutls_malloc(srp_salt.size);
-	nverifier->data = gnutls_malloc(srp_verifier.size);
-	if (nsalt->data == NULL || nverifier->data == NULL) {
-		gnutls_free(nsalt->data);
-		gnutls_free(nverifier->data);
-		return -1;
-	}
-	nsalt->size = srp_salt.size;
-	nverifier->size = srp_verifier.size;
-	memcpy(nverifier->data, srp_verifier.data, srp_verifier.size);
-	memcpy(nsalt->data, srp_salt.data, srp_salt.size);
-
-	*g = SRP_GROUP;
-	*n = SRP_PRIME;
-
-	/* We only have a single session, thus, ignore it */
-	(void) sess;
-
-	if (strcmp(username, "dummyuser"))
-		/* User invalid, stored dummy data in g and n. */
-		return 1;
-
-	return 0;
-}
-
-static int net_srp_server_handshake(int connfd)
-{
-	int ret;
-	gnutls_session_t session;
-
-	if (gnutls_init(&session, GNUTLS_SERVER) != 0)
-		return -1;
-	gnutls_priority_set_direct(session, "NORMAL:-KX-ALL:+SRP", NULL);
-	if (gnutls_credentials_set(session, GNUTLS_CRD_SRP, srp_cred) != 0)
-		return -1;
-
-	gnutls_transport_set_int(session, connfd);
-
-	do {
-		ret = gnutls_handshake(session);
-	} while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
-
-	if (ret < 0)
-		err("GnuTLS handshake failed (%s)", gnutls_strerror(ret));
-	else
-		info("GnuTLS handshake completed");
-
-	if (gnutls_bye(session, GNUTLS_SHUT_RDWR) != 0)
-		err("Unable to shutdown TLS connection.");
-	gnutls_deinit(session);
-
-	return ret;
-}
-#endif
-
 static void usbipd_help(void)
 {
 	printf("%s\n", usbipd_help_string);
@@ -225,7 +154,7 @@ static int check_allowed(char *acls, int sockfd)
 	return match;
 }
 
-static int recv_request_import(int sockfd)
+static int recv_request_import(struct usbip_connection *conn)
 {
 	struct op_import_request req;
 	struct op_common reply;
@@ -240,7 +169,7 @@ static int recv_request_import(int sockfd)
 	memset(&req, 0, sizeof(req));
 	memset(&reply, 0, sizeof(reply));
 
-	rc = usbip_net_recv(sockfd, &req, sizeof(req));
+	rc = usbip_net_recv(conn, &req, sizeof(req));
 	if (rc < 0) {
 		dbg("usbip_net_recv failed: import request");
 		return -1;
@@ -258,12 +187,7 @@ static int recv_request_import(int sockfd)
 
 	if (found) {
 		/* should set TCP_NODELAY for usbip */
-		usbip_net_set_nodelay(sockfd);
-
-		/* export device needs a TCP/IP socket descriptor */
-		rc = usbip_host_export_device(edev, sockfd);
-		if (rc < 0)
-			error = ERR_SYSERR;
+		usbip_net_set_nodelay(conn->sockfd);
 
 		/* check for allowed IPs */
 		snprintf(ip_attr_path, sizeof(ip_attr_path), "%s/%s:%d.%d/%s",
@@ -276,7 +200,8 @@ static int recv_request_import(int sockfd)
 			if (rc < 0) {
 				err("Unable to open sysfs");
 				error = ERR_SYSERR;
-			} else if (check_allowed(usbip_acl->value, sockfd) != 1) {
+			} else if (check_allowed(usbip_acl->value,
+						conn->sockfd) != 1) {
 				info("Access denied to device %s",
 					edev->udev.busid);
 				error = ERR_PERM;
@@ -285,12 +210,22 @@ static int recv_request_import(int sockfd)
 		} else {
 			err("failed to get ip list");
 		}
+
+		/*
+		 * There is a race condition here: Other clients might
+		 * take it, as this check doesn't lock the device
+		 * However, this seems hardly avoidable here.
+		 */
+		if (edev->status != SDEV_ST_AVAILABLE) {
+			error = ERR_INUSE;
+			found = 0;
+		}
 	} else {
 		info("requested device not found: %s", req.busid);
 		error = ERR_NOTFOUND;
 	}
 
-	rc = usbip_net_send_op_common(sockfd, OP_REP_IMPORT, error);
+	rc = usbip_net_send_op_common(conn, OP_REP_IMPORT, error);
 	if (rc < 0) {
 		dbg("usbip_net_send_op_common failed: %#0x", OP_REP_IMPORT);
 		return -1;
@@ -304,18 +239,27 @@ static int recv_request_import(int sockfd)
 	memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev));
 	usbip_net_pack_usb_device(1, &pdu_udev);
 
-	rc = usbip_net_send(sockfd, &pdu_udev, sizeof(pdu_udev));
+	rc = usbip_net_send(conn, &pdu_udev, sizeof(pdu_udev));
 	if (rc < 0) {
 		dbg("usbip_net_send failed: devinfo");
 		return -1;
 	}
 
+	usbip_net_bye(conn);
+
+	/* export device needs a TCP/IP socket descriptor */
+	rc = usbip_host_export_device(edev, conn->sockfd);
+	if (rc < 0) {
+		err("usbip_host_export_device");
+		return -1;
+	}
+
 	dbg("import request busid %s: complete", req.busid);
 
 	return 0;
 }
 
-static int send_reply_devlist(int connfd)
+static int send_reply_devlist(struct usbip_connection *conn)
 {
 	struct usbip_exported_device *edev;
 	struct usbip_usb_device pdu_udev;
@@ -332,14 +276,14 @@ static int send_reply_devlist(int connfd)
 	}
 	info("exportable devices: %d", reply.ndev);
 
-	rc = usbip_net_send_op_common(connfd, OP_REP_DEVLIST, ERR_OK);
+	rc = usbip_net_send_op_common(conn, OP_REP_DEVLIST, ERR_OK);
 	if (rc < 0) {
 		dbg("usbip_net_send_op_common failed: %#0x", OP_REP_DEVLIST);
 		return -1;
 	}
 	PACK_OP_DEVLIST_REPLY(1, &reply);
 
-	rc = usbip_net_send(connfd, &reply, sizeof(reply));
+	rc = usbip_net_send(conn, &reply, sizeof(reply));
 	if (rc < 0) {
 		dbg("usbip_net_send failed: %#0x", OP_REP_DEVLIST);
 		return -1;
@@ -351,7 +295,7 @@ static int send_reply_devlist(int connfd)
 		memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev));
 		usbip_net_pack_usb_device(1, &pdu_udev);
 
-		rc = usbip_net_send(connfd, &pdu_udev, sizeof(pdu_udev));
+		rc = usbip_net_send(conn, &pdu_udev, sizeof(pdu_udev));
 		if (rc < 0) {
 			dbg("usbip_net_send failed: pdu_udev");
 			return -1;
@@ -362,7 +306,7 @@ static int send_reply_devlist(int connfd)
 			memcpy(&pdu_uinf, &edev->uinf[i], sizeof(pdu_uinf));
 			usbip_net_pack_usb_interface(1, &pdu_uinf);
 
-			rc = usbip_net_send(connfd, &pdu_uinf,
+			rc = usbip_net_send(conn, &pdu_uinf,
 					    sizeof(pdu_uinf));
 			if (rc < 0) {
 				dbg("usbip_net_send failed: pdu_uinf");
@@ -374,20 +318,20 @@ static int send_reply_devlist(int connfd)
 	return 0;
 }
 
-static int recv_request_devlist(int connfd)
+static int recv_request_devlist(struct usbip_connection *conn)
 {
 	struct op_devlist_request req;
 	int rc;
 
 	memset(&req, 0, sizeof(req));
 
-	rc = usbip_net_recv(connfd, &req, sizeof(req));
+	rc = usbip_net_recv(conn, &req, sizeof(req));
 	if (rc < 0) {
 		dbg("usbip_net_recv failed: devlist request");
 		return -1;
 	}
 
-	rc = send_reply_devlist(connfd);
+	rc = send_reply_devlist(conn);
 	if (rc < 0) {
 		dbg("send_reply_devlist failed");
 		return -1;
@@ -396,7 +340,7 @@ static int recv_request_devlist(int connfd)
 	return 0;
 }
 
-static int recv_pdu(int connfd)
+static int recv_pdu(struct usbip_connection *conn)
 {
 	int auth = !need_auth, cont = 1, ret;
 
@@ -413,19 +357,19 @@ static int recv_pdu(int connfd)
 	while (cont) {
 		uint16_t code = OP_UNSPEC;
 
-		ret = usbip_net_recv_op_common(connfd, &code);
+		ret = usbip_net_recv_op_common(conn, &code);
 		if (ret < 0) {
 			dbg("could not receive opcode: %#0x: %s", code,
 				usbip_net_strerror(ret));
 			return -1;
 		}
 
-		info("received request: %#0x(%d)", code, connfd);
+		info("received request: %#0x", code);
 
 		/* We require an authenticated encryption */
 		if (!auth && code != OP_REQ_STARTTLS) {
 			info("Unauthenticated connection attempt");
-			usbip_net_send_op_common(connfd, OP_REPLY, ERR_AUTHREQ);
+			usbip_net_send_op_common(conn, OP_REPLY, ERR_AUTHREQ);
 			return -1;
 		}
 
@@ -433,15 +377,16 @@ static int recv_pdu(int connfd)
 #ifdef HAVE_GNUTLS
 		case OP_REQ_STARTTLS:
 			if (!need_auth) {
-				usbip_net_send_op_common(connfd, OP_REPLY,
+				usbip_net_send_op_common(conn, OP_REPLY,
 					ERR_NOAUTH);
 				err("Unexpected TLS handshake attempt (client "
 					"uses password, server doesn't)");
 				ret = -1;
 			} else {
-				usbip_net_send_op_common(connfd, OP_REPLY,
+				usbip_net_send_op_common(conn, OP_REPLY,
 					ERR_OK);
-				ret = net_srp_server_handshake(connfd);
+				err("Starting handshake");
+				ret = usbip_net_srp_server_handshake(conn);
 				if (ret != 0)
 					err("TLS handshake failed");
 				auth = 1;
@@ -449,11 +394,11 @@ static int recv_pdu(int connfd)
 			break;
 #endif
 		case OP_REQ_DEVLIST:
-			ret = recv_request_devlist(connfd);
+			ret = recv_request_devlist(conn);
 			cont = 0;
 			break;
 		case OP_REQ_IMPORT:
-			ret = recv_request_import(connfd);
+			ret = recv_request_import(conn);
 			cont = 0;
 			break;
 		case OP_REQ_DEVINFO:
@@ -464,9 +409,9 @@ static int recv_pdu(int connfd)
 		}
 
 		if (ret == 0)
-			info("request %#0x(%d): complete", code, connfd);
+			info("request %#0x: complete", code);
 		else {
-			info("request %#0x(%d): failed", code, connfd);
+			info("request %#0x: failed", code);
 			break;
 		}
 	}
@@ -474,71 +419,23 @@ static int recv_pdu(int connfd)
 	return ret;
 }
 
-#ifdef HAVE_LIBWRAP
-static int tcpd_auth(int connfd)
-{
-	struct request_info request;
-	int rc;
-
-	request_init(&request, RQ_DAEMON, PROGNAME, RQ_FILE, connfd, 0);
-	fromhost(&request);
-	rc = hosts_access(&request);
-	if (rc == 0)
-		return -1;
-
-	return 0;
-}
-#endif
-
-static int do_accept(int listenfd)
-{
-	int connfd;
-	struct sockaddr_storage ss;
-	socklen_t len = sizeof(ss);
-	char host[NI_MAXHOST], port[NI_MAXSERV];
-	int rc;
-
-	memset(&ss, 0, sizeof(ss));
-
-	connfd = accept(listenfd, (struct sockaddr *)&ss, &len);
-	if (connfd < 0) {
-		err("failed to accept connection");
-		return -1;
-	}
-
-	rc = getnameinfo((struct sockaddr *)&ss, len, host, sizeof(host),
-			 port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV);
-	if (rc)
-		err("getnameinfo: %s", gai_strerror(rc));
-
-#ifdef HAVE_LIBWRAP
-	rc = tcpd_auth(connfd);
-	if (rc < 0) {
-		info("denied access from %s", host);
-		close(connfd);
-		return -1;
-	}
-#endif
-	info("connection from %s, port %s", host, port);
-
-	return connfd;
-}
-
 int process_request(int listenfd)
 {
 	pid_t childpid;
-	int connfd;
+	struct usbip_connection conn;
+	int rc;
 
-	connfd = do_accept(listenfd);
-	if (connfd < 0)
+	rc = usbip_net_accept(listenfd, &conn);
+	if (rc < 0)
 		return -1;
 	childpid = fork();
 	if (childpid == 0) {
 		close(listenfd);
-		recv_pdu(connfd);
+		recv_pdu(&conn);
+		usbip_net_bye(&conn);
 		exit(0);
 	}
-	close(connfd);
+	close(conn.sockfd);
 	return 0;
 }
 
@@ -775,37 +672,6 @@ static int do_standalone_mode(int daemonize, int ipv4, int ipv6)
 	return 0;
 }
 
-#ifdef HAVE_GNUTLS
-static int usbip_init_gnutls(void)
-{
-	int ret;
-
-	gnutls_global_init();
-
-	srp_salt.data = gnutls_malloc(16);
-	if (!srp_salt.data)
-		return GNUTLS_E_MEMORY_ERROR;
-
-	ret = gnutls_rnd(GNUTLS_RND_NONCE, srp_salt.data, 16);
-	if (ret < 0)
-		return ret;
-	srp_salt.size = 16;
-
-	ret = gnutls_srp_allocate_server_credentials(&srp_cred);
-	if (ret < 0)
-		return ret;
-
-	ret = gnutls_srp_verifier("dummyuser", optarg, &srp_salt, &SRP_GROUP,
-		&SRP_PRIME, &srp_verifier);
-	if (ret < 0)
-		return ret;
-
-	gnutls_srp_set_server_credentials_function(srp_cred, net_srp_callback);
-
-	return GNUTLS_E_SUCCESS;
-}
-#endif
-
 int main(int argc, char *argv[])
 {
 	static const struct option longopts[] = {
@@ -859,7 +725,7 @@ int main(int argc, char *argv[])
 		case 's':
 #ifdef HAVE_GNUTLS
 			need_auth = 1;
-			ret = usbip_init_gnutls();
+			ret = usbip_net_init_gnutls();
 			if (ret < 0) {
 				err("Unable to initialize GnuTLS: %s",
 					gnutls_strerror(ret));
-- 
1.8.4.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv4 11/16] staging: usbip: Exchange session keys in userspace
  2013-10-19 14:39         ` [PATCHv4 00/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
                             ` (9 preceding siblings ...)
  2013-10-19 14:39           ` [PATCHv4 10/16] staging: usbip: TLS for all userspace communication Dominik Paulus
@ 2013-10-19 14:39           ` Dominik Paulus
  2013-10-19 14:39           ` [PATCHv4 12/16] staging: usbip: Pass session keys to the kernel Dominik Paulus
                             ` (4 subsequent siblings)
  15 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-10-19 14:39 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Dominik Paulus, Tobias Polzer, Greg Kroah-Hartman,
	Anthony Foiani, devel, linux-kernel, Kurt Kanzenbach,
	linux-kernel, tobias.polzer, Ilija Hadzic

In preparation for the kernel crypto support, we exchange two - randomly
generated - session keys between usbip and usbipd to be used for
encrypting all traffic generated in kernelspace. We use two different
128-bit keys, one for sending and one for receiving. Both are generated
by the client (usbip, probably has more entropy available than the
server) and transferred over the already established TLS connection.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 .../staging/usbip/userspace/libsrc/usbip_common.h  | 21 ++++++++++
 .../usbip/userspace/libsrc/usbip_host_driver.c     |  5 ++-
 .../usbip/userspace/libsrc/usbip_host_driver.h     |  3 +-
 .../staging/usbip/userspace/libsrc/vhci_driver.c   | 19 +++------
 .../staging/usbip/userspace/libsrc/vhci_driver.h   |  9 ++---
 drivers/staging/usbip/userspace/src/usbip_attach.c | 47 +++++++++++++++++++++-
 drivers/staging/usbip/userspace/src/usbipd.c       | 32 ++++++++++++---
 7 files changed, 105 insertions(+), 31 deletions(-)

diff --git a/drivers/staging/usbip/userspace/libsrc/usbip_common.h b/drivers/staging/usbip/userspace/libsrc/usbip_common.h
index 938ad1c..f804c04 100644
--- a/drivers/staging/usbip/userspace/libsrc/usbip_common.h
+++ b/drivers/staging/usbip/userspace/libsrc/usbip_common.h
@@ -127,6 +127,27 @@ struct usbip_usb_device {
 	uint8_t bNumInterfaces;
 } __attribute__((packed));
 
+
+/*
+ * These structs contain the configuration
+ * data to be passed to the kernel
+ */
+struct host_conf {
+	int sockfd;
+	uint8_t use_crypto;
+	uint8_t key1[16];
+	uint8_t key2[16];
+};
+struct vhci_conf {
+	uint8_t port;
+	int sockfd;
+	uint32_t devid;
+	uint32_t speed;
+	uint8_t use_crypto;
+	uint8_t key1[16];
+	uint8_t key2[16];
+};
+
 #define to_string(s)	#s
 
 void dump_usb_interface(struct usbip_usb_interface *);
diff --git a/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c b/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c
index 71a449c..dd93493 100644
--- a/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c
+++ b/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c
@@ -332,7 +332,8 @@ int usbip_host_refresh_device_list(void)
 	return 0;
 }
 
-int usbip_host_export_device(struct usbip_exported_device *edev, int sockfd)
+int usbip_host_export_device(struct usbip_exported_device *edev,
+			     struct host_conf *conf)
 {
 	char attr_name[] = "usbip_sockfd";
 	char attr_path[SYSFS_PATH_MAX];
@@ -366,7 +367,7 @@ int usbip_host_export_device(struct usbip_exported_device *edev, int sockfd)
 		return -1;
 	}
 
-	snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n", sockfd);
+	snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n", conf->sockfd);
 	dbg("write: %s", sockfd_buff);
 
 	ret = sysfs_write_attribute(attr, sockfd_buff, strlen(sockfd_buff));
diff --git a/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.h b/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.h
index 34fd14c..b76e41c 100644
--- a/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.h
+++ b/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.h
@@ -42,7 +42,8 @@ int usbip_host_driver_open(void);
 void usbip_host_driver_close(void);
 
 int usbip_host_refresh_device_list(void);
-int usbip_host_export_device(struct usbip_exported_device *edev, int sockfd);
+int usbip_host_export_device(struct usbip_exported_device *edev,
+			     struct host_conf *conf);
 struct usbip_exported_device *usbip_host_get_device(int num);
 
 #endif /* __USBIP_HOST_DRIVER_H */
diff --git a/drivers/staging/usbip/userspace/libsrc/vhci_driver.c b/drivers/staging/usbip/userspace/libsrc/vhci_driver.c
index 1091bb2..d1d45bb 100644
--- a/drivers/staging/usbip/userspace/libsrc/vhci_driver.c
+++ b/drivers/staging/usbip/userspace/libsrc/vhci_driver.c
@@ -467,8 +467,8 @@ int usbip_vhci_get_free_port(void)
 	return -1;
 }
 
-int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
-		uint32_t speed) {
+int usbip_vhci_attach_device(struct vhci_conf *conf)
+{
 	struct sysfs_attribute *attr_attach;
 	char buff[200]; /* what size should be ? */
 	int ret;
@@ -481,7 +481,7 @@ int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
 	}
 
 	snprintf(buff, sizeof(buff), "%u %u %u %u",
-			port, sockfd, devid, speed);
+			conf->port, conf->sockfd, conf->devid, conf->speed);
 	dbg("writing: %s", buff);
 
 	ret = sysfs_write_attribute(attr_attach, buff, strlen(buff));
@@ -490,25 +490,16 @@ int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
 		return -1;
 	}
 
-	dbg("attached port: %d", port);
+	dbg("attached port: %d", conf->port);
 
 	return 0;
 }
 
-static unsigned long get_devid(uint8_t busnum, uint8_t devnum)
+unsigned long get_devid(uint8_t busnum, uint8_t devnum)
 {
 	return (busnum << 16) | devnum;
 }
 
-/* will be removed */
-int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum,
-		uint8_t devnum, uint32_t speed)
-{
-	int devid = get_devid(busnum, devnum);
-
-	return usbip_vhci_attach_device2(port, sockfd, devid, speed);
-}
-
 int usbip_vhci_detach_device(uint8_t port)
 {
 	struct sysfs_attribute  *attr_detach;
diff --git a/drivers/staging/usbip/userspace/libsrc/vhci_driver.h b/drivers/staging/usbip/userspace/libsrc/vhci_driver.h
index 89949aa..325d0fa 100644
--- a/drivers/staging/usbip/userspace/libsrc/vhci_driver.h
+++ b/drivers/staging/usbip/userspace/libsrc/vhci_driver.h
@@ -55,13 +55,10 @@ int  usbip_vhci_refresh_device_list(void);
 
 
 int usbip_vhci_get_free_port(void);
-int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
-		uint32_t speed);
-
-/* will be removed */
-int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum,
-		uint8_t devnum, uint32_t speed);
+int usbip_vhci_attach_device(struct vhci_conf *conf);
 
 int usbip_vhci_detach_device(uint8_t port);
 
+unsigned long get_devid(uint8_t busnum, uint8_t devnum);
+
 #endif /* __VHCI_DRIVER_H */
diff --git a/drivers/staging/usbip/userspace/src/usbip_attach.c b/drivers/staging/usbip/userspace/src/usbip_attach.c
index 25c68e2..e339a05 100644
--- a/drivers/staging/usbip/userspace/src/usbip_attach.c
+++ b/drivers/staging/usbip/userspace/src/usbip_attach.c
@@ -29,6 +29,13 @@
 #include <unistd.h>
 #include <errno.h>
 
+#include "../config.h"
+
+#ifdef HAVE_GNUTLS
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#endif
+
 #include "vhci_driver.h"
 #include "usbip_common.h"
 #include "usbip_network.h"
@@ -91,6 +98,38 @@ static int import_device(struct usbip_connection *conn,
 {
 	int rc;
 	int port;
+	struct vhci_conf conf;
+
+	conf.use_crypto = 0;
+#ifdef HAVE_GNUTLS
+	if (conn->have_crypto) {
+		dbg("Generating session key and sending it to client");
+
+		rc = gnutls_rnd(GNUTLS_RND_RANDOM, conf.key1,
+				sizeof(conf.key1));
+		if (rc < 0) {
+			err("Session key generation failed: %s",
+					gnutls_strerror(rc));
+			return -1;
+		}
+		rc = gnutls_rnd(GNUTLS_RND_RANDOM, conf.key2,
+				sizeof(conf.key2));
+		if (rc < 0) {
+			err("Session key generation failed: %s",
+					gnutls_strerror(rc));
+			return -1;
+		}
+
+		if (usbip_net_send(conn, (void *) conf.key1, sizeof(conf.key1))
+				< 0 || usbip_net_send(conn, (void *) conf.key2,
+					sizeof(conf.key2)) < 0) {
+			err("Unable to send session key to client");
+			return -1;
+		}
+
+		conf.use_crypto = 1;
+	}
+#endif
 
 	rc = usbip_vhci_driver_open();
 	if (rc < 0) {
@@ -106,9 +145,13 @@ static int import_device(struct usbip_connection *conn,
 	}
 
 	usbip_net_bye(conn);
-	rc = usbip_vhci_attach_device(port, conn->sockfd, udev->busnum,
-				      udev->devnum, udev->speed);
 
+	conf.port = port;
+	conf.sockfd = conn->sockfd;
+	conf.devid = get_devid(udev->busnum, udev->devnum);
+	conf.speed = udev->speed;
+
+	rc = usbip_vhci_attach_device(&conf);
 	if (rc < 0) {
 		err("import device");
 		usbip_vhci_driver_close();
diff --git a/drivers/staging/usbip/userspace/src/usbipd.c b/drivers/staging/usbip/userspace/src/usbipd.c
index 6bd97a0..1a80bb6 100644
--- a/drivers/staging/usbip/userspace/src/usbipd.c
+++ b/drivers/staging/usbip/userspace/src/usbipd.c
@@ -245,13 +245,33 @@ static int recv_request_import(struct usbip_connection *conn)
 		return -1;
 	}
 
-	usbip_net_bye(conn);
+	if (found) {
+		struct host_conf conf = {
+			.sockfd = conn->sockfd,
+			.use_crypto = 0
+		};
 
-	/* export device needs a TCP/IP socket descriptor */
-	rc = usbip_host_export_device(edev, conn->sockfd);
-	if (rc < 0) {
-		err("usbip_host_export_device");
-		return -1;
+#ifdef HAVE_GNUTLS
+		if (conn->have_crypto) {
+			if (usbip_net_recv(conn, (void *) conf.key1,
+						sizeof(conf.key1)) < 0 ||
+					usbip_net_recv(conn, (void *) conf.key2,
+						sizeof(conf.key2)) < 0) {
+				err("Unable to receive session key");
+				return -1;
+			}
+			conf.use_crypto = 1;
+		}
+#endif
+
+		usbip_net_bye(conn);
+		/* export device needs a TCP/IP socket descriptor */
+		conf.sockfd = conn->sockfd;
+		rc = usbip_host_export_device(edev, &conf);
+		if (rc < 0) {
+			err("usbip_host_export_device");
+			return -1;
+		}
 	}
 
 	dbg("import request busid %s: complete", req.busid);
-- 
1.8.4.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv4 12/16] staging: usbip: Pass session keys to the kernel
  2013-10-19 14:39         ` [PATCHv4 00/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
                             ` (10 preceding siblings ...)
  2013-10-19 14:39           ` [PATCHv4 11/16] staging: usbip: Exchange session keys in userspace Dominik Paulus
@ 2013-10-19 14:39           ` Dominik Paulus
  2013-10-25 15:02             ` Dan Carpenter
  2013-10-19 14:39           ` [PATCHv4 13/16] staging: usbip: Wrap kernel_sendmsg()/recvmsg() Dominik Paulus
                             ` (3 subsequent siblings)
  15 siblings, 1 reply; 53+ messages in thread
From: Dominik Paulus @ 2013-10-19 14:39 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Dominik Paulus, Tobias Polzer, Greg Kroah-Hartman,
	Anthony Foiani, devel, linux-kernel, Kurt Kanzenbach,
	linux-kernel, tobias.polzer, Ilija Hadzic

This extends the userspace code to write the generated session keys to
sysfs in hexadecimal encoding after establishing the connection.
The kernel code is modified to parse the session keys.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/stub_dev.c                   | 26 +++++++++++++++++++-
 drivers/staging/usbip/usbip_common.h               | 10 ++++++++
 .../staging/usbip/userspace/libsrc/usbip_common.c  | 14 +++++++++++
 .../staging/usbip/userspace/libsrc/usbip_common.h  |  2 ++
 .../usbip/userspace/libsrc/usbip_host_driver.c     | 15 ++++++++----
 .../staging/usbip/userspace/libsrc/vhci_driver.c   |  8 ++++---
 drivers/staging/usbip/vhci_sysfs.c                 | 28 +++++++++++++++++++++-
 7 files changed, 94 insertions(+), 9 deletions(-)

diff --git a/drivers/staging/usbip/stub_dev.c b/drivers/staging/usbip/stub_dev.c
index 0e33542..d7d5b74 100644
--- a/drivers/staging/usbip/stub_dev.c
+++ b/drivers/staging/usbip/stub_dev.c
@@ -87,13 +87,37 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
 	int sockfd = 0;
 	struct socket *socket;
 	ssize_t err = -EINVAL;
+	char sendkey[USBIP_KEYSIZE], recvkey[USBIP_KEYSIZE];
+	char sendkey_hex[2 * USBIP_KEYSIZE + 1];
+	char recvkey_hex[2 * USBIP_KEYSIZE + 1];
 
 	if (!sdev) {
 		dev_err(dev, "sdev is null\n");
 		return -ENODEV;
 	}
 
-	sscanf(buf, "%d", &sockfd);
+	/*
+	 * Read symmetric crypto keys. They are randomly
+	 * generated by userspace and passed to the kernel
+	 * via sysfs (encoded in hexadecimal)
+	 */
+	if (sscanf(buf, "%d %d %32s %32s", &sockfd, &sdev->ud.use_crypto,
+			sendkey_hex, recvkey_hex) < 1) {
+		dev_err(dev, "Invalid write to sysfs: Invalid sockfd\n");
+		return -EINVAL;
+	}
+	if (sdev->ud.use_crypto) {
+		int i;
+		dev_info(dev, "Using encrypted data transport\n");
+		for (i = USBIP_KEYSIZE - 1; i >= 0; --i) {
+			sendkey_hex[2 * (i + 1)] = 0;
+			sscanf(sendkey_hex + (2 * i), "%2hhX", &sendkey[i]);
+		}
+		for (i = USBIP_KEYSIZE - 1; i >= 0; --i) {
+			recvkey_hex[2 * (i + 1)] = 0;
+			sscanf(recvkey_hex + (2 * i), "%2hhX", &recvkey[i]);
+		}
+	}
 
 	if (sockfd != -1) {
 		dev_info(dev, "stub up\n");
diff --git a/drivers/staging/usbip/usbip_common.h b/drivers/staging/usbip/usbip_common.h
index 7e6c543..96c87ee 100644
--- a/drivers/staging/usbip/usbip_common.h
+++ b/drivers/staging/usbip/usbip_common.h
@@ -32,6 +32,13 @@
 
 #define USBIP_VERSION "1.0.0"
 
+/*
+ * Length of symmetric keys. Currently, this should be fixed at 16 bytes.
+ * Will break code if changed, look at userspace and stub_dev.c/vhci_sysfs.c
+ * where this constant is used before changing.
+ */
+#define USBIP_KEYSIZE 16
+
 #undef pr_fmt
 
 #ifdef DEBUG
@@ -290,6 +297,9 @@ struct usbip_device {
 		void (*reset)(struct usbip_device *);
 		void (*unusable)(struct usbip_device *);
 	} eh_ops;
+
+	/* Crypto support */
+	int use_crypto;
 };
 
 #define kthread_get_run(threadfn, data, namefmt, ...)			   \
diff --git a/drivers/staging/usbip/userspace/libsrc/usbip_common.c b/drivers/staging/usbip/userspace/libsrc/usbip_common.c
index 17e08e0..1ec9cc9 100644
--- a/drivers/staging/usbip/userspace/libsrc/usbip_common.c
+++ b/drivers/staging/usbip/userspace/libsrc/usbip_common.c
@@ -290,3 +290,17 @@ void usbip_names_get_class(char *buff, size_t size, uint8_t class,
 
 	snprintf(buff, size, "%s / %s / %s (%02x/%02x/%02x)", c, s, p, class, subclass, protocol);
 }
+
+/*
+ * Converts a 16-byte key to hexadecimal to be pushed to kernelspace
+ *
+ * Output buffer must be at least 33 bytes long
+ */
+char *keytohex(unsigned char *key, char *out)
+{
+	int i;
+	out[32] = 0;
+	for (i = 0; i != 16; ++i)
+		sprintf(out + (2 * i), "%02X", key[i]);
+	return out;
+}
diff --git a/drivers/staging/usbip/userspace/libsrc/usbip_common.h b/drivers/staging/usbip/userspace/libsrc/usbip_common.h
index f804c04..a5d4d21 100644
--- a/drivers/staging/usbip/userspace/libsrc/usbip_common.h
+++ b/drivers/staging/usbip/userspace/libsrc/usbip_common.h
@@ -168,4 +168,6 @@ void usbip_names_get_product(char *buff, size_t size, uint16_t vendor,
 void usbip_names_get_class(char *buff, size_t size, uint8_t class,
 			   uint8_t subclass, uint8_t protocol);
 
+char *keytohex(unsigned char *key, char *out);
+
 #endif /* __USBIP_COMMON_H */
diff --git a/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c b/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c
index dd93493..dfcb22d 100644
--- a/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c
+++ b/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c
@@ -338,7 +338,7 @@ int usbip_host_export_device(struct usbip_exported_device *edev,
 	char attr_name[] = "usbip_sockfd";
 	char attr_path[SYSFS_PATH_MAX];
 	struct sysfs_attribute *attr;
-	char sockfd_buff[30];
+	char sockfd_buff[512];
 	int ret;
 
 	if (edev->status != SDEV_ST_AVAILABLE) {
@@ -367,10 +367,17 @@ int usbip_host_export_device(struct usbip_exported_device *edev,
 		return -1;
 	}
 
-	snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n", conf->sockfd);
-	dbg("write: %s", sockfd_buff);
+	{
+		char key1[33], key2[33];
+		snprintf(sockfd_buff, sizeof(sockfd_buff), "%d %d %s %s\n",
+				conf->sockfd, conf->use_crypto,
+				keytohex(conf->key2, key2),
+				keytohex(conf->key1, key1));
+		dbg("write: %s", sockfd_buff);
+	}
 
-	ret = sysfs_write_attribute(attr, sockfd_buff, strlen(sockfd_buff));
+	ret = sysfs_write_attribute(attr, (const char *) sockfd_buff,
+			strlen(sockfd_buff));
 	if (ret < 0) {
 		dbg("sysfs_write_attribute failed: sockfd %s to %s",
 		    sockfd_buff, attr_path);
diff --git a/drivers/staging/usbip/userspace/libsrc/vhci_driver.c b/drivers/staging/usbip/userspace/libsrc/vhci_driver.c
index d1d45bb..66cdfaf 100644
--- a/drivers/staging/usbip/userspace/libsrc/vhci_driver.c
+++ b/drivers/staging/usbip/userspace/libsrc/vhci_driver.c
@@ -470,7 +470,7 @@ int usbip_vhci_get_free_port(void)
 int usbip_vhci_attach_device(struct vhci_conf *conf)
 {
 	struct sysfs_attribute *attr_attach;
-	char buff[200]; /* what size should be ? */
+	char buff[512], key1[33], key2[33];
 	int ret;
 
 	attr_attach = sysfs_get_device_attr(vhci_driver->hc_device, "attach");
@@ -480,8 +480,10 @@ int usbip_vhci_attach_device(struct vhci_conf *conf)
 		return -1;
 	}
 
-	snprintf(buff, sizeof(buff), "%u %u %u %u",
-			conf->port, conf->sockfd, conf->devid, conf->speed);
+	snprintf(buff, sizeof(buff), "%u %u %u %u %d %s %s",
+			conf->port, conf->sockfd, conf->devid, conf->speed,
+			conf->use_crypto, keytohex(conf->key1, key1),
+			keytohex(conf->key2, key2));
 	dbg("writing: %s", buff);
 
 	ret = sysfs_write_attribute(attr_attach, buff, strlen(buff));
diff --git a/drivers/staging/usbip/vhci_sysfs.c b/drivers/staging/usbip/vhci_sysfs.c
index 9b51586..1ef3f25 100644
--- a/drivers/staging/usbip/vhci_sysfs.c
+++ b/drivers/staging/usbip/vhci_sysfs.c
@@ -174,14 +174,40 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
 	struct socket *socket;
 	int sockfd = 0;
 	__u32 rhport = 0, devid = 0, speed = 0;
+	unsigned char sendkey[USBIP_KEYSIZE], recvkey[USBIP_KEYSIZE];
+	char sendkey_hex[2*USBIP_KEYSIZE+1], recvkey_hex[2*USBIP_KEYSIZE+1];
+	int use_crypto = 0;
 
 	/*
 	 * @rhport: port number of vhci_hcd
 	 * @sockfd: socket descriptor of an established TCP connection
 	 * @devid: unique device identifier in a remote host
 	 * @speed: usb device speed in a remote host
+	 * @use_crypto: Whether to use an encrypted connection
+	 * @recvkey_hex/@sendkey_hex: Hexadecimal encoded 128bit
+	 *              symmetric encryption keys to be used.
+	 *              Generated randomly for each connection
+	 *              by userspace.
 	 */
-	sscanf(buf, "%u %u %u %u", &rhport, &sockfd, &devid, &speed);
+	sendkey_hex[0] = 0;
+	recvkey_hex[0] = 0;
+	sscanf(buf, "%u %u %u %u %d %32s %32s", &rhport, &sockfd,
+			&devid, &speed, &use_crypto, sendkey_hex,
+			recvkey_hex);
+	if (use_crypto && strlen(sendkey_hex) == 32 &&
+			strlen(recvkey_hex) == 32) {
+		int i;
+		dev_info(dev, "Using encrypted data transport\n");
+		/* Decode decimal representation */
+		for (i = USBIP_KEYSIZE - 1; i >= 0; --i) {
+			sendkey_hex[2 * (i + 1)] = 0;
+			sscanf(sendkey_hex + (2 * i), "%2hhX", &sendkey[i]);
+		}
+		for (i = USBIP_KEYSIZE - 1; i >= 0; --i) {
+			recvkey_hex[2 * (i + 1)] = 0;
+			sscanf(recvkey_hex + (2 * i), "%2hhX", &recvkey[i]);
+		}
+	}
 
 	usbip_dbg_vhci_sysfs("rhport(%u) sockfd(%u) devid(%u) speed(%u)\n",
 			     rhport, sockfd, devid, speed);
-- 
1.8.4.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv4 13/16] staging: usbip: Wrap kernel_sendmsg()/recvmsg()
  2013-10-19 14:39         ` [PATCHv4 00/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
                             ` (11 preceding siblings ...)
  2013-10-19 14:39           ` [PATCHv4 12/16] staging: usbip: Pass session keys to the kernel Dominik Paulus
@ 2013-10-19 14:39           ` Dominik Paulus
  2013-10-19 14:39           ` [PATCHv4 14/16] staging: usbip: Add encryption support to kernel Dominik Paulus
                             ` (2 subsequent siblings)
  15 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-10-19 14:39 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Dominik Paulus, Tobias Polzer, Greg Kroah-Hartman,
	Anthony Foiani, devel, linux-kernel, Kurt Kanzenbach,
	linux-kernel, tobias.polzer, Ilija Hadzic

This adds two simple wrappers around kernel_sendmsg() and
kernel_recvmsg() that can be extended to perform additional
cryptographic operations on the data before sending it.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/stub_rx.c      |  2 +-
 drivers/staging/usbip/stub_tx.c      |  6 ++--
 drivers/staging/usbip/usbip_common.c | 66 +++++++++++++++++++-----------------
 drivers/staging/usbip/usbip_common.h |  7 +++-
 drivers/staging/usbip/vhci_rx.c      |  2 +-
 drivers/staging/usbip/vhci_tx.c      |  4 +--
 6 files changed, 47 insertions(+), 40 deletions(-)

diff --git a/drivers/staging/usbip/stub_rx.c b/drivers/staging/usbip/stub_rx.c
index db48a78..6ba9969 100644
--- a/drivers/staging/usbip/stub_rx.c
+++ b/drivers/staging/usbip/stub_rx.c
@@ -553,7 +553,7 @@ static void stub_rx_pdu(struct usbip_device *ud)
 	memset(&pdu, 0, sizeof(pdu));
 
 	/* receive a pdu header */
-	ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu));
+	ret = usbip_recv(ud, &pdu, sizeof(pdu));
 	if (ret != sizeof(pdu)) {
 		dev_err(dev, "recv a header, %d\n", ret);
 		usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
diff --git a/drivers/staging/usbip/stub_tx.c b/drivers/staging/usbip/stub_tx.c
index cd5326a..9a9f9e6 100644
--- a/drivers/staging/usbip/stub_tx.c
+++ b/drivers/staging/usbip/stub_tx.c
@@ -257,8 +257,7 @@ static int stub_send_ret_submit(struct stub_device *sdev)
 			iovnum++;
 		}
 
-		ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg,
-						iov,  iovnum, txsize);
+		ret = usbip_sendmsg(&sdev->ud, &msg, iov, iovnum, txsize);
 		if (ret != txsize) {
 			dev_err(&sdev->interface->dev,
 				"sendmsg failed!, retval %d for %zd\n",
@@ -332,8 +331,7 @@ static int stub_send_ret_unlink(struct stub_device *sdev)
 		iov[0].iov_len  = sizeof(pdu_header);
 		txsize += sizeof(pdu_header);
 
-		ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, iov,
-				     1, txsize);
+		ret = usbip_sendmsg(&sdev->ud, &msg, iov, 1, txsize);
 		if (ret != txsize) {
 			dev_err(&sdev->interface->dev,
 				"sendmsg failed!, retval %d for %zd\n",
diff --git a/drivers/staging/usbip/usbip_common.c b/drivers/staging/usbip/usbip_common.c
index e3fc749..c225f7a 100644
--- a/drivers/staging/usbip/usbip_common.c
+++ b/drivers/staging/usbip/usbip_common.c
@@ -339,7 +339,7 @@ void usbip_dump_header(struct usbip_header *pdu)
 EXPORT_SYMBOL_GPL(usbip_dump_header);
 
 /* Receive data over TCP/IP. */
-int usbip_recv(struct socket *sock, void *buf, int size)
+int usbip_recv(struct usbip_device *ud, void *buf, int size)
 {
 	int result;
 	struct msghdr msg;
@@ -352,34 +352,27 @@ int usbip_recv(struct socket *sock, void *buf, int size)
 
 	usbip_dbg_xmit("enter\n");
 
-	if (!sock || !buf || !size) {
-		pr_err("invalid arg, sock %p buff %p size %d\n", sock, buf,
-		       size);
+	if (!ud || !buf || !size) {
+		pr_err("invalid arg, ud %p buff %p size %d\n", ud, buf, size);
 		return -EINVAL;
 	}
 
-	do {
-		sock->sk->sk_allocation = GFP_NOIO;
-		iov.iov_base    = buf;
-		iov.iov_len     = size;
-		msg.msg_name    = NULL;
-		msg.msg_namelen = 0;
-		msg.msg_control = NULL;
-		msg.msg_controllen = 0;
-		msg.msg_namelen    = 0;
-		msg.msg_flags      = MSG_NOSIGNAL;
-
-		result = kernel_recvmsg(sock, &msg, &iov, 1, size, MSG_WAITALL);
-		if (result <= 0) {
-			pr_debug("receive sock %p buf %p size %u ret %d total %d\n",
-				 sock, buf, size, result, total);
-			goto err;
-		}
-
-		size -= result;
-		buf += result;
-		total += result;
-	} while (size > 0);
+	ud->tcp_socket->sk->sk_allocation = GFP_NOIO;
+	iov.iov_base    = buf;
+	iov.iov_len     = size;
+	msg.msg_name    = NULL;
+	msg.msg_namelen = 0;
+	msg.msg_control = NULL;
+	msg.msg_controllen = 0;
+	msg.msg_namelen    = 0;
+	msg.msg_flags      = MSG_NOSIGNAL;
+
+	result = usbip_recvmsg(ud, &msg, &iov, 1, size, MSG_WAITALL);
+	if (result < 0) {
+		pr_debug("receive sock %p buf %p size %u ret %d total %d\n",
+			 ud->tcp_socket, buf, size, result, total);
+		return result;
+	}
 
 	if (usbip_dbg_flag_xmit) {
 		if (!in_interrupt())
@@ -393,9 +386,6 @@ int usbip_recv(struct socket *sock, void *buf, int size)
 			 osize, result, size, total);
 	}
 
-	return total;
-
-err:
 	return result;
 }
 EXPORT_SYMBOL_GPL(usbip_recv);
@@ -634,6 +624,20 @@ static void usbip_pack_iso(struct usbip_iso_packet_descriptor *iso,
 	}
 }
 
+int usbip_recvmsg(struct usbip_device *ud, struct msghdr *msg,
+		  struct kvec *vec, size_t num, size_t size, int flags)
+{
+	return kernel_recvmsg(ud->tcp_socket, msg, vec, num, size, flags);
+}
+EXPORT_SYMBOL_GPL(usbip_recvmsg);
+
+int usbip_sendmsg(struct usbip_device *ud, struct msghdr *msg,
+		  struct kvec *vec, size_t num, size_t size)
+{
+	return kernel_sendmsg(ud->tcp_socket, msg, vec, num, size);
+}
+EXPORT_SYMBOL_GPL(usbip_sendmsg);
+
 /* must free buffer */
 struct usbip_iso_packet_descriptor*
 usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen)
@@ -680,7 +684,7 @@ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb)
 	if (!buff)
 		return -ENOMEM;
 
-	ret = usbip_recv(ud->tcp_socket, buff, size);
+	ret = usbip_recv(ud, buff, size);
 	if (ret != size) {
 		dev_err(&urb->dev->dev, "recv iso_frame_descriptor, %d\n",
 			ret);
@@ -785,7 +789,7 @@ int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb)
 	if (!(size > 0))
 		return 0;
 
-	ret = usbip_recv(ud->tcp_socket, urb->transfer_buffer, size);
+	ret = usbip_recv(ud, urb->transfer_buffer, size);
 	if (ret != size) {
 		dev_err(&urb->dev->dev, "recv xbuf, %d\n", ret);
 		if (ud->side == USBIP_STUB) {
diff --git a/drivers/staging/usbip/usbip_common.h b/drivers/staging/usbip/usbip_common.h
index 96c87ee..bdad29f 100644
--- a/drivers/staging/usbip/usbip_common.h
+++ b/drivers/staging/usbip/usbip_common.h
@@ -323,7 +323,7 @@ struct usbip_device {
 void usbip_dump_urb(struct urb *purb);
 void usbip_dump_header(struct usbip_header *pdu);
 
-int usbip_recv(struct socket *sock, void *buf, int size);
+int usbip_recv(struct usbip_device *ui, void *buf, int size);
 struct socket *sockfd_to_socket(unsigned int sockfd);
 
 void usbip_pack_pdu(struct usbip_header *pdu, struct urb *urb, int cmd,
@@ -344,6 +344,11 @@ void usbip_stop_eh(struct usbip_device *ud);
 void usbip_event_add(struct usbip_device *ud, unsigned long event);
 int usbip_event_happened(struct usbip_device *ud);
 
+int usbip_recvmsg(struct usbip_device *ui, struct msghdr *msg,
+		  struct kvec *vec, size_t num, size_t size, int flags);
+int usbip_sendmsg(struct usbip_device *ui, struct msghdr *msg,
+		  struct kvec *vec, size_t num, size_t size);
+
 static inline int interface_to_busnum(struct usb_interface *interface)
 {
 	struct usb_device *udev = interface_to_usbdev(interface);
diff --git a/drivers/staging/usbip/vhci_rx.c b/drivers/staging/usbip/vhci_rx.c
index d07fcb5..34706a8 100644
--- a/drivers/staging/usbip/vhci_rx.c
+++ b/drivers/staging/usbip/vhci_rx.c
@@ -207,7 +207,7 @@ static void vhci_rx_pdu(struct usbip_device *ud)
 	memset(&pdu, 0, sizeof(pdu));
 
 	/* receive a pdu header */
-	ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu));
+	ret = usbip_recv(ud, &pdu, sizeof(pdu));
 	if (ret < 0) {
 		if (ret == -ECONNRESET)
 			pr_info("connection reset by peer\n");
diff --git a/drivers/staging/usbip/vhci_tx.c b/drivers/staging/usbip/vhci_tx.c
index 409fd99..09663e6 100644
--- a/drivers/staging/usbip/vhci_tx.c
+++ b/drivers/staging/usbip/vhci_tx.c
@@ -115,7 +115,7 @@ static int vhci_send_cmd_submit(struct vhci_device *vdev)
 			txsize += len;
 		}
 
-		ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 3, txsize);
+		ret = usbip_sendmsg(&vdev->ud, &msg, iov, 3, txsize);
 		if (ret != txsize) {
 			pr_err("sendmsg failed!, ret=%d for %zd\n", ret,
 			       txsize);
@@ -184,7 +184,7 @@ static int vhci_send_cmd_unlink(struct vhci_device *vdev)
 		iov[0].iov_len  = sizeof(pdu_header);
 		txsize += sizeof(pdu_header);
 
-		ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 1, txsize);
+		ret = usbip_sendmsg(&vdev->ud, &msg, iov, 1, txsize);
 		if (ret != txsize) {
 			pr_err("sendmsg failed!, ret=%d for %zd\n", ret,
 			       txsize);
-- 
1.8.4.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv4 14/16] staging: usbip: Add encryption support to kernel
  2013-10-19 14:39         ` [PATCHv4 00/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
                             ` (12 preceding siblings ...)
  2013-10-19 14:39           ` [PATCHv4 13/16] staging: usbip: Wrap kernel_sendmsg()/recvmsg() Dominik Paulus
@ 2013-10-19 14:39           ` Dominik Paulus
  2013-10-19 14:39           ` [PATCHv4 15/16] staging: usbip: Update documentation Dominik Paulus
  2013-10-19 14:39           ` [PATCHv4 16/16] staging: usbip: Increment version number to 1.2.1 Dominik Paulus
  15 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-10-19 14:39 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Dominik Paulus, Tobias Polzer, Greg Kroah-Hartman,
	Anthony Foiani, devel, linux-kernel, Kurt Kanzenbach,
	linux-kernel, tobias.polzer, Ilija Hadzic

This adds code performing the actual encryption and authentication
operations in the usbip kernel code. The whole data stream may now be
encrypted and authenticated with AES-GCM and symmetric 128 bit keys.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/Kconfig        |   2 +-
 drivers/staging/usbip/stub.h         |   3 +
 drivers/staging/usbip/stub_dev.c     |   8 +
 drivers/staging/usbip/usbip_common.c | 349 ++++++++++++++++++++++++++++++++++-
 drivers/staging/usbip/usbip_common.h |  24 +++
 drivers/staging/usbip/vhci_hcd.c     |   4 +-
 drivers/staging/usbip/vhci_sysfs.c   |  10 +
 7 files changed, 396 insertions(+), 4 deletions(-)

diff --git a/drivers/staging/usbip/Kconfig b/drivers/staging/usbip/Kconfig
index 8860009..87220af 100644
--- a/drivers/staging/usbip/Kconfig
+++ b/drivers/staging/usbip/Kconfig
@@ -1,6 +1,6 @@
 config USBIP_CORE
 	tristate "USB/IP support"
-	depends on USB && NET
+	depends on USB && NET && CRYPTO_GCM && CRYPTO_AES
 	default N
 	---help---
 	  This enables pushing USB packets over IP to allow remote
diff --git a/drivers/staging/usbip/stub.h b/drivers/staging/usbip/stub.h
index cfe75d1..2aaea3a 100644
--- a/drivers/staging/usbip/stub.h
+++ b/drivers/staging/usbip/stub.h
@@ -26,6 +26,9 @@
 #include <linux/types.h>
 #include <linux/usb.h>
 #include <linux/wait.h>
+#include <linux/crypto.h>
+#include <linux/err.h>
+#include <linux/scatterlist.h>
 
 #define STUB_BUSID_OTHER 0
 #define STUB_BUSID_REMOV 1
diff --git a/drivers/staging/usbip/stub_dev.c b/drivers/staging/usbip/stub_dev.c
index d7d5b74..46008f7 100644
--- a/drivers/staging/usbip/stub_dev.c
+++ b/drivers/staging/usbip/stub_dev.c
@@ -21,6 +21,7 @@
 #include <linux/file.h>
 #include <linux/kthread.h>
 #include <linux/module.h>
+#include <linux/kfifo.h>
 
 #include "usbip_common.h"
 #include "stub.h"
@@ -137,6 +138,12 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
 
 		spin_unlock_irq(&sdev->ud.lock);
 
+		if (sdev->ud.use_crypto) {
+			err = usbip_init_crypto(&sdev->ud, sendkey, recvkey);
+			if (err < 0)
+				goto err;
+		}
+
 		sdev->ud.tcp_rx = kthread_get_run(stub_rx_loop, &sdev->ud,
 						  "stub_rx");
 		sdev->ud.tcp_tx = kthread_get_run(stub_tx_loop, &sdev->ud,
@@ -292,6 +299,7 @@ static void stub_shutdown_connection(struct usbip_device *ud)
 	}
 
 	/* 3. free used data */
+	usbip_deinit_crypto(ud);
 	stub_device_cleanup_urbs(sdev);
 
 	/* 4. free stub_unlink */
diff --git a/drivers/staging/usbip/usbip_common.c b/drivers/staging/usbip/usbip_common.c
index c225f7a..afed948 100644
--- a/drivers/staging/usbip/usbip_common.c
+++ b/drivers/staging/usbip/usbip_common.c
@@ -26,6 +26,8 @@
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <net/sock.h>
+#include <linux/scatterlist.h>
+#include <linux/crypto.h>
 
 #include "usbip_common.h"
 
@@ -624,17 +626,360 @@ static void usbip_pack_iso(struct usbip_iso_packet_descriptor *iso,
 	}
 }
 
+int usbip_init_crypto(struct usbip_device *ud, unsigned char *sendkey, unsigned
+		char *recvkey)
+{
+	int ret;
+
+	ud->use_crypto = 1;
+
+	ud->tfm_recv = crypto_alloc_aead("gcm(aes)", 0, 0);
+	if (IS_ERR(ud->tfm_recv))
+		return PTR_ERR(ud->tfm_recv);
+	ud->tfm_send = crypto_alloc_aead("gcm(aes)", 0, 0);
+	if (IS_ERR(ud->tfm_send)) {
+		ret = PTR_ERR(ud->tfm_send);
+		goto err_free_recv;
+	}
+	ret = kfifo_alloc(&ud->recv_queue, RECVQ_SIZE, GFP_KERNEL);
+	if (ret)
+		goto err_free_send;
+
+	if (crypto_aead_setkey(ud->tfm_send, sendkey, USBIP_KEYSIZE) ||
+	    crypto_aead_setkey(ud->tfm_recv, recvkey, USBIP_KEYSIZE) ||
+	    crypto_aead_setauthsize(ud->tfm_send, USBIP_AUTHSIZE) ||
+	    crypto_aead_setauthsize(ud->tfm_recv, USBIP_AUTHSIZE)) {
+		ret = -EINVAL;
+		goto err_free_fifo;
+	}
+
+	ud->ctr_send = 0;
+	ud->ctr_recv = 0;
+
+	return 0;
+
+err_free_fifo:
+	kfifo_free(&ud->recv_queue);
+err_free_send:
+	crypto_free_aead(ud->tfm_send);
+err_free_recv:
+	crypto_free_aead(ud->tfm_recv);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usbip_init_crypto);
+
+void usbip_deinit_crypto(struct usbip_device *ud)
+{
+	if (ud->use_crypto) {
+		crypto_free_aead(ud->tfm_send);
+		crypto_free_aead(ud->tfm_recv);
+		kfifo_free(&ud->recv_queue);
+		ud->use_crypto = 0;
+	}
+}
+EXPORT_SYMBOL_GPL(usbip_deinit_crypto);
+
+struct tcrypt_result {
+	struct completion completion;
+	int err;
+};
+
+static void tcrypt_complete(struct crypto_async_request *req, int err)
+{
+	struct tcrypt_result *res = req->data;
+
+	if (err == -EINPROGRESS)
+		return;
+
+	res->err = err;
+	complete(&res->completion);
+}
+
+#define USBIP_ENCRYPT 1
+#define USBIP_DECRYPT 0
+/*
+ * Perform encryption/decryption on one chunk of data.
+ * Uses global crypto state stored in usbip_device.
+ * Parameters:
+ * encrypt: USBIP_ENCRYPT to perform encryption, USBIP_DECRYPT to perform
+ * decryption
+ * packetsize: Size of the encrypted packet, including the authentication tag,
+ * not including the associated data (length field).
+ * plaintext and ciphertext have to be appropiately managed by the caller
+ * (i.e. they must be at least packetsize bytes long).
+ * Returns: 0 on success, negative error code on failure
+ */
+static int usbip_crypt(struct usbip_device *ud, int encrypt,
+		uint32_t packetsize, unsigned char *plaintext,
+		unsigned char *ciphertext)
+{
+	struct crypto_aead *tfm;
+	struct aead_request *req;
+	struct tcrypt_result result;
+	struct scatterlist plain, cipher, assoc;
+	char iv[16];
+	u64 *iv_num;
+	u64 iv_net;
+	const int plainsize = packetsize - USBIP_AUTHSIZE;
+	int ret;
+
+	/* Currently, this is guaranteed by the caller */
+	if (packetsize < USBIP_AUTHSIZE)
+		return -EINVAL;
+
+	memset(iv, 0, sizeof(iv));
+	if (encrypt) {
+		tfm = ud->tfm_send;
+		iv_num = &ud->ctr_send;
+	} else {
+		tfm = ud->tfm_recv;
+		iv_num = &ud->ctr_recv;
+	}
+	iv_net = cpu_to_be64(*iv_num);
+	memcpy(iv, &iv_net, sizeof(iv_net));
+
+	req = aead_request_alloc(tfm, GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	init_completion(&result.completion);
+	aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+				  tcrypt_complete, &result);
+
+	sg_init_one(&cipher, ciphertext, packetsize);
+	sg_init_one(&plain, plaintext, plainsize);
+	crypto_aead_clear_flags(tfm, ~0);
+
+	if (encrypt)
+		aead_request_set_crypt(req, &plain, &cipher, plainsize, iv);
+	else
+		aead_request_set_crypt(req, &cipher, &plain, packetsize, iv);
+	packetsize = cpu_to_be32(packetsize);
+	sg_init_one(&assoc, &packetsize, sizeof(packetsize));
+	/* Associated data: Unencrypted length tag */
+	aead_request_set_assoc(req, &assoc, sizeof(packetsize));
+
+	if (encrypt)
+		ret = crypto_aead_encrypt(req);
+	else
+		ret = crypto_aead_decrypt(req);
+
+	switch (ret) {
+	case 0: /* Success */
+		break;
+	case -EINPROGRESS:
+	case -EBUSY:
+		wait_for_completion(&result.completion);
+		ret = result.err;
+		break;
+	default:
+		aead_request_free(req);
+		return ret;
+	}
+
+	aead_request_free(req);
+
+	(*iv_num)++; /* Increment IV */
+
+	return ret;
+}
+
+/*
+ * Wrapper to kernel_recvmsg. If necessary, also does the necessary decryption.
+ * If decryption is enabled, you _MUST_ pass 1 as parameter for num, i.e.
+ * only receive into a single continuous buffer.
+ */
 int usbip_recvmsg(struct usbip_device *ud, struct msghdr *msg,
 		  struct kvec *vec, size_t num, size_t size, int flags)
 {
-	return kernel_recvmsg(ud->tcp_socket, msg, vec, num, size, flags);
+	int ret;
+	size_t total = 0;
+	unsigned char *plainbuf, *cipherbuf;
+
+	if (!ud->use_crypto)
+		return kernel_recvmsg(ud->tcp_socket, msg, vec, num, size,
+				flags);
+
+	if (vec[0].iov_len < size)
+		return -EINVAL;
+	if (num != 1)
+		return -EINVAL;
+
+	plainbuf = kmalloc(USBIP_PACKETSIZE, GFP_KERNEL);
+	if (!plainbuf)
+		return -ENOMEM;
+	cipherbuf = kmalloc(USBIP_PACKETSIZE, GFP_KERNEL);
+	if (!cipherbuf) {
+		kfree(plainbuf);
+		return -ENOMEM;
+	}
+
+	while (total < size) {
+		uint32_t packetsize;
+		struct kvec recvvec;
+
+		/*
+		 * We use a global kfifo to buffer unrequested plaintext bytes.
+		 * Flush this buffer first before receiving new data.
+		 */
+		if (kfifo_len(&ud->recv_queue)) {
+			size_t next = min_t(size_t, kfifo_len(&ud->recv_queue),
+					size - total);
+			/* No error checking necessary - see previous line */
+			ret = kfifo_out(&ud->recv_queue, vec[0].iov_base + total, next);
+			total += next;
+			continue;
+		}
+
+		/* See usbip_sendmsg() for the format of one encrypted packet */
+
+		/*
+		 * Receive size of next crypto packet
+		 */
+		recvvec.iov_base = &packetsize;
+		recvvec.iov_len = sizeof(packetsize);
+
+		ret = kernel_recvmsg(ud->tcp_socket, msg, &recvvec, 1,
+				sizeof(packetsize), flags);
+		packetsize = be32_to_cpu(packetsize);
+		if (ret < 0) {
+			total = ret;
+			goto err;
+		} else if (ret != sizeof(packetsize)) {
+			total = -EBADMSG;
+			goto err;
+		}
+
+		if (packetsize > USBIP_PACKETSIZE) {
+			total = -EBADMSG;
+			goto err;
+		}
+
+		/*
+		 * Receive the rest of the packet
+		 */
+		recvvec.iov_base = cipherbuf;
+		recvvec.iov_len = packetsize;
+		ret = kernel_recvmsg(ud->tcp_socket, msg, &recvvec, 1,
+				packetsize, flags);
+		if (ret <= 0) {
+			total = ret;
+			goto err;
+		} else if (ret != packetsize) {
+			total = -EBADMSG;
+			goto err;
+		}
+
+		/*
+		 * Decrypt the packet. This will also authenticate the length
+		 * field
+		 */
+		ret = usbip_crypt(ud, 0, packetsize, plainbuf, cipherbuf);
+		if (ret) {
+			total = ret;
+			goto err;
+		}
+
+		/*
+		 * Add this packet to our global buffer (It will be stored in
+		 * the user buffer in the next loop iteration) No error
+		 * checking necessary - we already know the packet is going to
+		 * fit.
+		 */
+		(void) kfifo_in(&ud->recv_queue, plainbuf, packetsize -
+				USBIP_AUTHSIZE);
+	}
+
+err:
+	kfree(plainbuf);
+	kfree(cipherbuf);
+
+	return total;
 }
 EXPORT_SYMBOL_GPL(usbip_recvmsg);
 
 int usbip_sendmsg(struct usbip_device *ud, struct msghdr *msg,
 		  struct kvec *vec, size_t num, size_t size)
 {
-	return kernel_sendmsg(ud->tcp_socket, msg, vec, num, size);
+	int i = 0, ret, offset = 0;
+	size_t total = 0;
+	unsigned char *cipherbuf;
+
+	if (!ud->use_crypto)
+		return kernel_sendmsg(ud->tcp_socket, msg, vec, num, size);
+
+	cipherbuf = kmalloc(USBIP_PACKETSIZE, GFP_KERNEL);
+	if (!cipherbuf)
+		return -ENOMEM;
+
+	/*
+	 * The receiver has to decrypt whole packets. To avoid the need
+	 * to allocate large buffers at the receiving side, we split the
+	 * data to be sent in USBIP_PACKETSIZE large chunks that can be
+	 * decrypted separately. See below for the format of each chunk.
+	 */
+
+	/* Iterate over all kvecs, splitting them up as necessary. */
+	for (i = 0; i != num && size; ) {
+		/* Compute the remaining number of bytes to send for
+		 * this kvec */
+		size_t plain_size = min(vec[i].iov_len - offset,
+				min_t(size_t, size, USBIP_DATASIZE));
+		size_t packet_size = plain_size + USBIP_AUTHSIZE;
+		uint32_t packet_size_net = cpu_to_be32(packet_size);
+		struct kvec sendvec[2];
+
+		if (plain_size == 0) {
+			++i;
+			offset = 0;
+			continue;
+		}
+
+		/*
+		 * One encrypted packet consists of:
+		 *  - An unencrypted, authenticated length tag (exactly 4
+		 *    bytes) containing the length of the packet.
+		 *  - Up to USBIP_PACKETSIZE - USBIP_AUTHSIZE bytes of user
+		 *    payload, encrypted
+		 *  - Exactly USBIP_AUTHSIZE bytes authentication tag.
+		 * Note: The packet length is also authenticated, but has
+		 * - for obvious reasons - to be sent in plaintext. This
+		 * packet format will be parsed by usbip_recvmsg (see above).
+		 */
+		ret = usbip_crypt(ud, 1, packet_size, vec[i].iov_base + offset,
+				cipherbuf);
+		if (ret) {
+			total = ret;
+			goto out;
+		}
+
+		/* Length field */
+		sendvec[0].iov_base = &packet_size_net;
+		sendvec[0].iov_len = sizeof(packet_size_net);
+		/* Payload and authentication tag */
+		sendvec[1].iov_base = cipherbuf;
+		sendvec[1].iov_len = packet_size;
+		ret = kernel_sendmsg(ud->tcp_socket, msg, sendvec,
+				ARRAY_SIZE(sendvec),
+				sendvec[0].iov_len + sendvec[1].iov_len);
+		if (ret < 0) {
+			total = ret;
+			goto out;
+		}
+		if (ret != sendvec[0].iov_len + sendvec[1].iov_len) {
+			total = -EPROTO;
+			goto out;
+		}
+		offset += plain_size;
+		size -= plain_size;
+		total += plain_size;
+	}
+
+out:
+	kfree(cipherbuf);
+
+	return total;
 }
 EXPORT_SYMBOL_GPL(usbip_sendmsg);
 
diff --git a/drivers/staging/usbip/usbip_common.h b/drivers/staging/usbip/usbip_common.h
index bdad29f..8e2855e 100644
--- a/drivers/staging/usbip/usbip_common.h
+++ b/drivers/staging/usbip/usbip_common.h
@@ -29,15 +29,30 @@
 #include <linux/types.h>
 #include <linux/usb.h>
 #include <linux/wait.h>
+#include <linux/kfifo.h>
 
 #define USBIP_VERSION "1.0.0"
 
 /*
+ * Length of the authentication tag associated with each packet, in bytes. Can
+ * be set to 4, 8, 12, 13, 14, 15 or 16. See crypto_gcm_setauthsize in
+ * crypto/gcm.c. Increasing this will increase crypto protocol overhead.
+ */
+#define USBIP_AUTHSIZE 4
+/*
  * Length of symmetric keys. Currently, this should be fixed at 16 bytes.
  * Will break code if changed, look at userspace and stub_dev.c/vhci_sysfs.c
  * where this constant is used before changing.
  */
 #define USBIP_KEYSIZE 16
+/*
+ * Maximum size of encrypted packets. Decreasing this will increase overhead
+ * and decrease memory usage.
+ */
+#define USBIP_PACKETSIZE 1024
+#define RECVQ_SIZE (2*USBIP_PACKETSIZE)
+
+#define USBIP_DATASIZE (USBIP_PACKETSIZE - USBIP_AUTHSIZE)
 
 #undef pr_fmt
 
@@ -300,6 +315,11 @@ struct usbip_device {
 
 	/* Crypto support */
 	int use_crypto;
+	struct crypto_aead *tfm_recv;
+	struct crypto_aead *tfm_send;
+	/* Counters to be used as IVs */
+	u64 ctr_send, ctr_recv;
+	DECLARE_KFIFO_PTR(recv_queue, char);
 };
 
 #define kthread_get_run(threadfn, data, namefmt, ...)			   \
@@ -323,6 +343,10 @@ struct usbip_device {
 void usbip_dump_urb(struct urb *purb);
 void usbip_dump_header(struct usbip_header *pdu);
 
+int usbip_init_crypto(struct usbip_device *ud, unsigned char *sendkey,
+		unsigned char *recvkey);
+void usbip_deinit_crypto(struct usbip_device *ud);
+
 int usbip_recv(struct usbip_device *ui, void *buf, int size);
 struct socket *sockfd_to_socket(unsigned int sockfd);
 
diff --git a/drivers/staging/usbip/vhci_hcd.c b/drivers/staging/usbip/vhci_hcd.c
index e810ad5..55290c1 100644
--- a/drivers/staging/usbip/vhci_hcd.c
+++ b/drivers/staging/usbip/vhci_hcd.c
@@ -786,7 +786,9 @@ static void vhci_shutdown_connection(struct usbip_device *ud)
 		kthread_stop_put(vdev->ud.tcp_tx);
 		vdev->ud.tcp_tx = NULL;
 	}
-	pr_info("stop threads\n");
+	pr_info("stopped threads\n");
+
+	usbip_deinit_crypto(&vdev->ud);
 
 	/* active connection is closed */
 	if (vdev->ud.tcp_socket) {
diff --git a/drivers/staging/usbip/vhci_sysfs.c b/drivers/staging/usbip/vhci_sysfs.c
index 1ef3f25..ce46c16 100644
--- a/drivers/staging/usbip/vhci_sysfs.c
+++ b/drivers/staging/usbip/vhci_sysfs.c
@@ -20,6 +20,8 @@
 #include <linux/kthread.h>
 #include <linux/file.h>
 #include <linux/net.h>
+#include <linux/crypto.h>
+#include <linux/kfifo.h>
 
 #include "usbip_common.h"
 #include "vhci.h"
@@ -227,6 +229,13 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
 	/* begin a lock */
 	spin_lock(&the_controller->lock);
 	vdev = port_to_vdev(rhport);
+	if (use_crypto) {
+		int ret = usbip_init_crypto(&vdev->ud, sendkey, recvkey);
+		if (ret < 0) {
+			spin_unlock(&the_controller->lock);
+			return ret;
+		}
+	}
 	spin_lock(&vdev->ud.lock);
 
 	if (vdev->ud.status != VDEV_ST_NULL) {
@@ -237,6 +246,7 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
 		fput(socket->file);
 
 		dev_err(dev, "port %d already used\n", rhport);
+		usbip_deinit_crypto(&vdev->ud);
 		return -EINVAL;
 	}
 
-- 
1.8.4.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv4 15/16] staging: usbip: Update documentation
  2013-10-19 14:39         ` [PATCHv4 00/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
                             ` (13 preceding siblings ...)
  2013-10-19 14:39           ` [PATCHv4 14/16] staging: usbip: Add encryption support to kernel Dominik Paulus
@ 2013-10-19 14:39           ` Dominik Paulus
  2013-10-19 14:39           ` [PATCHv4 16/16] staging: usbip: Increment version number to 1.2.1 Dominik Paulus
  15 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-10-19 14:39 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Tobias Polzer, Dominik Paulus, Greg Kroah-Hartman,
	Anthony Foiani, devel, linux-kernel, Kurt Kanzenbach,
	linux-kernel, tobias.polzer, Ilija Hadzic

From: Tobias Polzer <tobias.polzer@fau.de>

README was updated and cleaned. It now contains just one example, which
was updated to use encryption. Also, the new crypto behaviour is
documented. The usbip "port" command has been removed from the README,
as it isn't supported by newer userland versions. One dead link was
removed from the checklist section.

Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
---
 drivers/staging/usbip/userspace/README       | 72 +++++++++++-----------------
 drivers/staging/usbip/userspace/doc/usbipd.8 |  4 +-
 2 files changed, 31 insertions(+), 45 deletions(-)

diff --git a/drivers/staging/usbip/userspace/README b/drivers/staging/usbip/userspace/README
index 00a1658..01ea616 100644
--- a/drivers/staging/usbip/userspace/README
+++ b/drivers/staging/usbip/userspace/README
@@ -12,56 +12,53 @@
     - sysfsutils >= 2.0.0
 	sysfsutils library
 
-    - libwrap0-dev
+    - libwrap0-dev (optional)
 	tcp wrapper library
 
     - gcc >= 4.0
 
     - libtool, automake >= 1.9, autoconf >= 2.5.0, pkg-config
 
+    - libgnutls-dev >= 3.0 (libgnutls28-dev on debian) (optional)
+
 
 [Install]
     0. Generate configuration scripts.
 	$ ./autogen.sh
 
     1. Compile & install the userspace utilities.
-	$ ./configure [--with-tcp-wrappers=no] [--with-usbids-dir=<dir>]
+	$ ./configure [--with-tcp-wrappers=no] [--with-usbids-dir=<dir>] [--with-gnutls]
 	$ make install
 
     2. Compile & install USB/IP drivers.
 
 
 [Usage]
-    server:# (Physically attach your USB device.)
-
-    server:# insmod usbip-core.ko
-    server:# insmod usbip-host.ko
-
-    server:# usbipd -D
-	- Start usbip daemon.
-
-    server:# usbip list -l
-	- List driver assignments for USB devices.
 
-    server:# usbip bind --busid 1-2
-	- Bind usbip-host.ko to the device with busid 1-2.
-	- The USB device 1-2 is now exportable to other hosts!
-	- Use `usbip unbind --busid 1-2' to stop exporting the device.
+See usbip(8) and usbipd(8).
 
-    client:# insmod usbip-core.ko
-    client:# insmod vhci-hcd.ko
+[Security considerations]
+By default, all of the usbip network traffic is unencrypted and
+unauthenticated. As it is mostly parsed in staging quality kernel code, you
+should use usbip in this mode only in absolutely trusted environments.
 
-    client:# usbip list --remote <host>
-	- List exported USB devices on the <host>.
+In addition to the usual methods for secure network tunneling - SSH tunnels,
+IPsec, etc. -, usbip version 1.2.1 supports out-of-the-box crypto for all of
+the network traffic. This requires usbip to be compiled with GnuTLS enabled
+(configure switch: --with-gnutls). Crypto support can be enabled by using
+password authentication. If the --auth flag is set, usbip will not only
+authenticate client and server using the shared passphrase, but also encrypt
+and authenticate all of the following traffic. For the userspace traffic,
+GnuTLS is used, the kernel traffic is encrypted and authenticated using AES-GCM
+with 128bit keys. The session keys are randomly generated and exchanged for
+in userspace for each connection.
 
-    client:# usbip attach --remote <host> --busid 1-2
-	- Connect the remote USB device.
-
-    client:# usbip port
-	- Show virtual port status.
-
-    client:# usbip detach --port <port>
-	- Detach the USB device.
+The encryption support has been designed to offer perfect forward secrecy and
+decent security even if using rather weak passwords. Strong passwords are
+mainly needed to provide proper authorization (this is still important, see
+above!) and to secure against man-in-the-middle attacks.
+However, the crypto code still lacks complete review and code auditing. Do not
+rely on it for strong security.
 
 
 [Example]
@@ -72,7 +69,7 @@ Physically attach your USB devices to this host.
 
     trois:# insmod path/to/usbip-core.ko
     trois:# insmod path/to/usbip-host.ko
-    trois:# usbipd -D
+    trois:# usbipd -sVerySecret -D
 
 In another terminal, let's look up what USB devices are physically
 attached to this host.
@@ -135,7 +132,7 @@ exportable on the host.
     deux:# insmod path/to/usbip-core.ko
     deux:# insmod path/to/vhci-hcd.ko
 
-    deux:# usbip list --remote 10.0.0.3
+    deux:# usbip --auth VerySecret list --remote 10.0.0.3
     Exportable USB devices
     ======================
      - 10.0.0.3
@@ -163,20 +160,9 @@ exportable on the host.
 
 Attach a remote USB device:
 
-    deux:# usbip attach --remote 10.0.0.3 --busid 1-1
+    deux:# usbip --auth VerySecret attach --remote 10.0.0.3 --busid 1-1
     port 0 attached
 
-Show the devices attached to this client:
-
-    deux:# usbip port
-    Port 00: <Port in Use> at Full Speed(12Mbps)
-	   Prolific Technology, Inc. : unknown product (067b:3507)
-	   6-1 -> usbip://10.0.0.3:3240/1-1  (remote bus/dev 001/004)
-	   6-1:1.0 used by usb-storage
-			  /sys/class/scsi_device/0:0:0:0/device
-			  /sys/class/scsi_host/host0/device
-			  /sys/block/sda/device
-
 Detach the imported device:
 
     deux:# usbip detach --port 0
@@ -184,8 +170,6 @@ Detach the imported device:
 
 
 [Checklist]
-    - See 'Debug Tips' on the project wiki.
-	- http://usbip.wiki.sourceforge.net/how-to-debug-usbip
     - usbip-host.ko must be bound to the target device.
 	- See /proc/bus/usb/devices and find "Driver=..." lines of the device.
     - Shutdown firewall.
diff --git a/drivers/staging/usbip/userspace/doc/usbipd.8 b/drivers/staging/usbip/userspace/doc/usbipd.8
index 8beb95a..3fcd9ed 100644
--- a/drivers/staging/usbip/userspace/doc/usbipd.8
+++ b/drivers/staging/usbip/userspace/doc/usbipd.8
@@ -56,7 +56,9 @@ Listen on TCP/IP port PORT.
 .HP
 \fB\-s\fR, \fB\-\-auth\fR
 .IP
-Sets the password to be used for client authentication. If -a is used, the server will only accept connections from authenticated clients. Note: USB traffic will still be unencrypted, this currently only serves for authentication.
+Sets the password to be used for client authentication. If -a is used, the
+server will only accept connections from authenticated clients. This will also
+encrypt the whole usbip traffic, including kernel traffic, using 128bit AES.
 .PP
 
 \fB\-h\fR, \fB\-\-help\fR
-- 
1.8.4.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCHv4 16/16] staging: usbip: Increment version number to 1.2.1
  2013-10-19 14:39         ` [PATCHv4 00/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
                             ` (14 preceding siblings ...)
  2013-10-19 14:39           ` [PATCHv4 15/16] staging: usbip: Update documentation Dominik Paulus
@ 2013-10-19 14:39           ` Dominik Paulus
  15 siblings, 0 replies; 53+ messages in thread
From: Dominik Paulus @ 2013-10-19 14:39 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Dominik Paulus, Tobias Polzer, Greg Kroah-Hartman,
	Anthony Foiani, devel, linux-kernel, Kurt Kanzenbach,
	linux-kernel, tobias.polzer, Ilija Hadzic

Also increment the kernel module version number to match the
userspace version, as compatibility with old userspace utilities
is now at least partially broken.

Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
---
 drivers/staging/usbip/usbip_common.h         | 2 +-
 drivers/staging/usbip/userspace/configure.ac | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/staging/usbip/usbip_common.h b/drivers/staging/usbip/usbip_common.h
index 8e2855e..3d40b86 100644
--- a/drivers/staging/usbip/usbip_common.h
+++ b/drivers/staging/usbip/usbip_common.h
@@ -31,7 +31,7 @@
 #include <linux/wait.h>
 #include <linux/kfifo.h>
 
-#define USBIP_VERSION "1.0.0"
+#define USBIP_VERSION "1.2.1"
 
 /*
  * Length of the authentication tag associated with each packet, in bytes. Can
diff --git a/drivers/staging/usbip/userspace/configure.ac b/drivers/staging/usbip/userspace/configure.ac
index bc08b81..26dabe3 100644
--- a/drivers/staging/usbip/userspace/configure.ac
+++ b/drivers/staging/usbip/userspace/configure.ac
@@ -1,7 +1,7 @@
 dnl Process this file with autoconf to produce a configure script.
 
 AC_PREREQ(2.59)
-AC_INIT([usbip-utils], [1.1.1], [linux-usb@vger.kernel.org])
+AC_INIT([usbip-utils], [1.2.1], [linux-usb@vger.kernel.org])
 
 CURRENT=0
 REVISION=1
-- 
1.8.4.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCHv4 06/16] staging: usbip: Add proper error reporting
  2013-10-19 14:39           ` [PATCHv4 06/16] staging: usbip: Add proper error reporting Dominik Paulus
@ 2013-10-25 13:26             ` Dan Carpenter
  0 siblings, 0 replies; 53+ messages in thread
From: Dan Carpenter @ 2013-10-25 13:26 UTC (permalink / raw)
  To: Dominik Paulus
  Cc: Anthony Foiani, devel, linux-kernel, Greg Kroah-Hartman,
	Kurt Kanzenbach, tobias.polzer, linux-kernel, Ilija Hadzic

On Sat, Oct 19, 2013 at 04:39:09PM +0200, Dominik Paulus wrote:
> +const char *usbip_net_strerror(int status)
> +{
> +	static const char *const errs[] = {
> +		/* ERR_OK */ "Success",
> +		/* ERR_NA */ "Command failed",
> +		/* ERR_MISMATCH */ "Protocol version mismatch",
> +		/* ERR_SYSERR */ "System error",
> +		/* ERR_UNEXPECTED */ "Unexpected opcode received",
> +		/* ERR_AUTHREQ */ "Server requires authentication",
> +		/* ERR_PERM */ "Permission denied",
> +		/* ERR_NOTFOUND */ "Requested device not found",
> +		/* ERR_NOAUTH */ "Server doesn't support authentication"
> +	};
> +	if (status < 0)
> +		status = -status;
> +	if (status >= (int) (sizeof(errs) / sizeof(*errs)))

Not a big deal, but for future reference, the cast here is unneeded.

> +		return "Invalid";
> +	return errs[status];
>  }


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCHv4 10/16] staging: usbip: TLS for all userspace communication
  2013-10-19 14:39           ` [PATCHv4 10/16] staging: usbip: TLS for all userspace communication Dominik Paulus
@ 2013-10-25 14:45             ` Dan Carpenter
  0 siblings, 0 replies; 53+ messages in thread
From: Dan Carpenter @ 2013-10-25 14:45 UTC (permalink / raw)
  To: Dominik Paulus
  Cc: Anthony Foiani, devel, linux-kernel, Greg Kroah-Hartman,
	Kurt Kanzenbach, tobias.polzer, linux-kernel, Ilija Hadzic

On Sat, Oct 19, 2013 at 04:39:13PM +0200, Dominik Paulus wrote:
> @@ -104,8 +105,10 @@ static int import_device(int sockfd, struct usbip_usb_device *udev)
>  		return -1;
>  	}
>  
> -	rc = usbip_vhci_attach_device(port, sockfd, udev->busnum,
> +	usbip_net_bye(conn);
> +	rc = usbip_vhci_attach_device(port, conn->sockfd, udev->busnum,
>  				      udev->devnum, udev->speed);
> +
>  	if (rc < 0) {

Don't put a blank line between the function call and the check.  They
logically are one idea.

>  
> -	rc = usbip_net_recv(sockfd, (void *) &reply, sizeof(reply));
> +	rc = usbip_net_recv(conn, (void *) &reply, sizeof(reply));

There is no need to cast to void here, btw.  That's just noise.

>  	do {
> -		if (sending)
> -			nbytes = send(sockfd, buff, bufflen, 0);
> +		if (!conn->have_crypto && sending)
> +			nbytes = send(conn->sockfd, buff, bufflen, 0);
> +		else if (!conn->have_crypto && !sending)
> +			nbytes = recv(conn->sockfd, buff, bufflen, MSG_WAITALL);
> +#ifdef HAVE_GNUTLS
> +		else if (sending)
> +			nbytes = gnutls_record_send(conn->session, buff, bufflen);
>  		else
> -			nbytes = recv(sockfd, buff, bufflen, MSG_WAITALL);
> +			nbytes = gnutls_record_recv(conn->session, buff, bufflen);
> +#else
> +		/*
> +		 * Assertion to let gcc be able to infer proper initialization
> +		 * of nbytes.
> +		 */
> +		assert(!conn->have_crypto);
> +#endif

This is messy and I feel like it should be abstracted into a function
so we can hide the ifdef in a header file.

		if (sending)
			nbytes = usbip_send(conn, buff, bufflen, 0);
		else
			nbytes = usbip_recv(...

We'd still have the ifdef but hidden away.


> +int usbip_net_srp_server_handshake(struct usbip_connection *conn)
> +{
> +	int ret;
> +
> +	if (gnutls_init(&conn->session, GNUTLS_SERVER) != 0)
> +		return -1;
> +	gnutls_priority_set_direct(conn->session, "NORMAL:-KX-ALL:+SRP", NULL);
> +	if (gnutls_credentials_set(conn->session, GNUTLS_CRD_SRP,
> +		usbip_net_srp_cred) != 0)
> +		return -1;
> +

Kernel style is more beautiful:

	ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_SRP,
				     usbip_net_srp_cred);
	if (ret)
		return ret;

> +void usbip_net_bye(struct usbip_connection *conn)
> +{
> +#ifdef HAVE_GNUTLS
> +	if (conn->have_crypto) {
> +		gnutls_bye(conn->session, GNUTLS_SHUT_RDWR);
> +
> +		gnutls_deinit(conn->session);
> +		if (!conn->server)
> +			gnutls_srp_free_client_credentials(conn->srp_client_cred);
> +
> +		conn->have_crypto = 0;
> +	}
> +#else
> +	(void)conn;

What is this about?  I assume that GCC warns, but which version of GCC
are you using because that sounds horrible.

> +#endif

regards,
dan carpenter

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCHv4 12/16] staging: usbip: Pass session keys to the kernel
  2013-10-19 14:39           ` [PATCHv4 12/16] staging: usbip: Pass session keys to the kernel Dominik Paulus
@ 2013-10-25 15:02             ` Dan Carpenter
  0 siblings, 0 replies; 53+ messages in thread
From: Dan Carpenter @ 2013-10-25 15:02 UTC (permalink / raw)
  To: Dominik Paulus
  Cc: Anthony Foiani, devel, linux-kernel, Greg Kroah-Hartman,
	Kurt Kanzenbach, tobias.polzer, linux-kernel, Ilija Hadzic

On Sat, Oct 19, 2013 at 04:39:15PM +0200, Dominik Paulus wrote:
> @@ -367,10 +367,17 @@ int usbip_host_export_device(struct usbip_exported_device *edev,
>  		return -1;
>  	}
>  
> -	snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n", conf->sockfd);
> -	dbg("write: %s", sockfd_buff);
> +	{
> +		char key1[33], key2[33];
> +		snprintf(sockfd_buff, sizeof(sockfd_buff), "%d %d %s %s\n",
> +				conf->sockfd, conf->use_crypto,
> +				keytohex(conf->key2, key2),
> +				keytohex(conf->key1, key1));
> +		dbg("write: %s", sockfd_buff);
> +	}

This is gross.  Don't do that.  My gut says to just delete the debug
code but I hate debug code by default.

> -	ret = sysfs_write_attribute(attr, sockfd_buff, strlen(sockfd_buff));
> +	ret = sysfs_write_attribute(attr, (const char *) sockfd_buff,

Don't put a space between the cast and the variable.

	ret = sysfs_write_attribute(attr, (const char *)sockfd_buff,

regards,
dan carpenter

^ permalink raw reply	[flat|nested] 53+ messages in thread

end of thread, other threads:[~2013-10-25 15:04 UTC | newest]

Thread overview: 53+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-09-13  9:55 [PATCHv2 0/11] staging: usbip: Userland crypto and ACLs Dominik Paulus
2013-09-13  9:55 ` [PATCHv2 01/11] staging: usbip: Fix IPv6 support in usbipd Dominik Paulus
2013-09-13  9:55 ` [PATCHv2 02/11] staging: usbip: Add support for client authentication Dominik Paulus
2013-09-13  9:55 ` [PATCHv2 03/11] staging: usbip: Add kernel support for client ACLs Dominik Paulus
2013-09-13  9:55 ` [PATCHv2 04/11] staging: usbip: Add CIDR matching helper functions Dominik Paulus
2013-09-13  9:55 ` [PATCHv2 05/11] staging: usbip: Add ACL support to usbip bind Dominik Paulus
2013-09-13  9:55 ` [PATCHv2 06/11] staging: usbip: Add support for ACLs in usbipd Dominik Paulus
2013-09-13  9:55 ` [PATCHv2 07/11] staging: usbip: Add proper error reporting Dominik Paulus
2013-09-13  9:55 ` [PATCHv2 08/11] staging: usbip: Handle usbip being started as user Dominik Paulus
2013-09-13  9:55 ` [PATCHv2 09/11] staging: usbip: Improve debug output Dominik Paulus
2013-09-13  9:56 ` [PATCHv2 10/11] staging: usbip: Separate protocol/program version Dominik Paulus
2013-09-13  9:56 ` [PATCHv2 11/11] staging: usbip: Increment version to 1.2.0 Dominik Paulus
2013-09-25 23:36 ` [PATCHv2 0/11] staging: usbip: Userland crypto and ACLs Greg Kroah-Hartman
2013-09-28 17:41   ` Dominik Paulus
2013-09-28 17:42   ` [PATCHv3 01/16] staging: usbip: Add support for client authentication Dominik Paulus
2013-09-28 17:42     ` [PATCHv3 02/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
2013-09-30  8:29       ` Dan Carpenter
2013-09-28 17:42     ` [PATCHv3 03/16] staging: usbip: Add CIDR matching helper functions Dominik Paulus
2013-09-28 17:42     ` [PATCHv3 04/16] staging: usbip: Add ACL support to usbip bind Dominik Paulus
2013-09-28 17:42     ` [PATCHv3 05/16] staging: usbip: Add support for ACLs in usbipd Dominik Paulus
2013-09-28 17:42     ` [PATCHv3 06/16] staging: usbip: Add proper error reporting Dominik Paulus
2013-09-28 17:42     ` [PATCHv3 07/16] staging: usbip: Handle usbip being started as user Dominik Paulus
2013-09-28 17:42     ` [PATCHv3 08/16] staging: usbip: Improve debug output Dominik Paulus
2013-09-28 17:42     ` [PATCHv3 09/16] staging: usbip: Separate protocol/program version Dominik Paulus
2013-09-28 17:42     ` [PATCHv3 10/16] staging: usbip: TLS for all userspace communication Dominik Paulus
2013-09-28 17:42     ` [PATCHv3 11/16] staging: usbip: Exchange session keys in userspace Dominik Paulus
2013-09-28 17:42     ` [PATCHv3 12/16] staging: usbip: Pass session keys to the kernel Dominik Paulus
2013-09-28 17:42     ` [PATCHv3 13/16] staging: usbip: Wrap kernel_sendmsg()/recvmsg() Dominik Paulus
2013-09-30 12:42       ` Dan Carpenter
2013-09-28 17:42     ` [PATCHv3 14/16] staging: usbip: Add encryption support to kernel Dominik Paulus
2013-09-30 12:38       ` Dan Carpenter
2013-10-19 14:39         ` [PATCHv4 00/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
2013-10-19 14:39           ` [PATCHv4 01/16] staging: usbip: Add support for client authentication Dominik Paulus
2013-10-19 14:39           ` [PATCHv4 02/16] staging: usbip: Add kernel support for client ACLs Dominik Paulus
2013-10-19 14:39           ` [PATCHv4 03/16] staging: usbip: Add CIDR matching helper functions Dominik Paulus
2013-10-19 14:39           ` [PATCHv4 04/16] staging: usbip: Add ACL support to usbip bind Dominik Paulus
2013-10-19 14:39           ` [PATCHv4 05/16] staging: usbip: Add support for ACLs in usbipd Dominik Paulus
2013-10-19 14:39           ` [PATCHv4 06/16] staging: usbip: Add proper error reporting Dominik Paulus
2013-10-25 13:26             ` Dan Carpenter
2013-10-19 14:39           ` [PATCHv4 07/16] staging: usbip: Handle usbip being started as user Dominik Paulus
2013-10-19 14:39           ` [PATCHv4 08/16] staging: usbip: Improve debug output Dominik Paulus
2013-10-19 14:39           ` [PATCHv4 09/16] staging: usbip: Separate protocol/program version Dominik Paulus
2013-10-19 14:39           ` [PATCHv4 10/16] staging: usbip: TLS for all userspace communication Dominik Paulus
2013-10-25 14:45             ` Dan Carpenter
2013-10-19 14:39           ` [PATCHv4 11/16] staging: usbip: Exchange session keys in userspace Dominik Paulus
2013-10-19 14:39           ` [PATCHv4 12/16] staging: usbip: Pass session keys to the kernel Dominik Paulus
2013-10-25 15:02             ` Dan Carpenter
2013-10-19 14:39           ` [PATCHv4 13/16] staging: usbip: Wrap kernel_sendmsg()/recvmsg() Dominik Paulus
2013-10-19 14:39           ` [PATCHv4 14/16] staging: usbip: Add encryption support to kernel Dominik Paulus
2013-10-19 14:39           ` [PATCHv4 15/16] staging: usbip: Update documentation Dominik Paulus
2013-10-19 14:39           ` [PATCHv4 16/16] staging: usbip: Increment version number to 1.2.1 Dominik Paulus
2013-09-28 17:42     ` [PATCHv3 15/16] staging: usbip: Update documentation Dominik Paulus
2013-09-28 17:42     ` [PATCHv3 16/16] staging: usbip: Increment version number to 1.2.1 Dominik Paulus

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).