lkml.org 
[lkml]   [2013]   [Sep]   [28]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCHv3 01/16] staging: usbip: Add support for client authentication
    Date
    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


    \
     
     \ /
      Last update: 2013-09-28 20:01    [W:4.612 / U:0.008 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site