So this patch is just posted here for public record. It applies against the latest CVS. Rich. [1] https://www.redhat.com/archives/libvir-list/2007-March/thread.html#00333 -- Emerging Technologies, Red Hat http://et.redhat.com/~rjones/ 64 Baker Street, London, W1U 7DF Mobile: +44 7866 314 421 Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SL4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Directors: Michael Cunningham (USA), Charlie Peters (USA) and David Owens (Ireland)
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/configure.in libvirt-oldremote/configure.in --- libvirt-cvs/configure.in 2007-04-04 15:39:43.000000000 +0100 +++ libvirt-oldremote/configure.in 2007-04-04 13:40:43.000000000 +0100 @@ -42,9 +42,15 @@ AC_PATH_PROG(TAR, tar, /bin/tar) AC_PATH_PROG(XMLLINT, xmllint, /usr/bin/xmllint) AC_PATH_PROG(XSLTPROC, xsltproc, /usr/bin/xsltproc) +AC_PATH_PROG(LOGGER, logger) + +AC_DEFINE_UNQUOTED(LOGGER, "$LOGGER", + [Define the location of the external 'logger' program, or + undefine to disable use of external 'logger'. This is + used by libvirtd to write to syslog.]) dnl Availability of various common functions. -AC_CHECK_FUNCS([regexec]) +AC_CHECK_FUNCS([lrand48_r regexec]) dnl Make sure we have an ANSI compiler AM_C_PROTOTYPES @@ -264,6 +270,17 @@ AC_SUBST(LIBXML_CONFIG) AC_SUBST(LIBXML_MIN_VERSION) +dnl GnuTLS library +AC_CHECK_HEADER([gnutls/gnutls.h], + [], + AC_MSG_ERROR([You must install the GnuTLS development package in order to compile libvirt])) +AC_CHECK_LIB(gnutls, gnutls_handshake, + [], + [AC_MSG_ERROR([You must install the GnuTLS library in order to compile and run libvirt])]) + +dnl /dev/urandom +AC_CHECK_FILES([/dev/urandom]) + dnl virsh libraries AC_CHECK_LIB(curses, initscr, [VIRSH_LIBS="$VIRSH_LIBS -lcurses"], diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/include/libvirt/virterror.h libvirt-oldremote/include/libvirt/virterror.h --- libvirt-cvs/include/libvirt/virterror.h 2007-03-16 11:04:46.000000000 +0000 +++ libvirt-oldremote/include/libvirt/virterror.h 2007-04-04 12:54:22.000000000 +0100 @@ -49,6 +49,7 @@ VIR_FROM_CONF, /* Error in the configuration file handling */ VIR_FROM_QEMU, /* Error at the QEMU daemon */ VIR_FROM_NET, /* Error when operating on a network */ + VIR_FROM_REMOTE /* Error from remote driver */ } virErrorDomain; @@ -120,6 +121,8 @@ VIR_ERR_INVALID_NETWORK, /* invalid network object */ VIR_ERR_NETWORK_EXIST, /* the network already exist */ VIR_ERR_SYSTEM_ERROR, /* general system call failure */ + VIR_ERR_RPC, /* some sort of RPC error */ + VIR_ERR_RPC_BAD_CONNECTION, /* bad connection in RPC */ } virErrorNumber; /** diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/.cvsignore libvirt-oldremote/src/.cvsignore --- libvirt-cvs/src/.cvsignore 2007-02-23 17:15:42.000000000 +0000 +++ libvirt-oldremote/src/.cvsignore 2007-03-07 12:27:20.000000000 +0000 @@ -5,6 +5,11 @@ *.lo *.la virsh +libvirtd +remote_rpc_clnt.c +remote_rpc_svc.c +remote_rpc_xdr.c +remote_rpc.h *.gcda *.gcno *.gcov diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/driver.h libvirt-oldremote/src/driver.h --- libvirt-cvs/src/driver.h 2007-04-04 15:39:43.000000000 +0100 +++ libvirt-oldremote/src/driver.h 2007-04-04 15:26:42.000000000 +0100 @@ -20,6 +20,7 @@ VIR_DRV_XEN_UNIFIED = 1, VIR_DRV_TEST = 2, VIR_DRV_QEMU = 3, + VIR_DRV_REMOTE = 4, } virDrvNo; diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/libvirt.c libvirt-oldremote/src/libvirt.c --- libvirt-cvs/src/libvirt.c 2007-04-04 15:39:43.000000000 +0100 +++ libvirt-oldremote/src/libvirt.c 2007-04-04 15:27:37.000000000 +0100 @@ -28,6 +28,7 @@ #include "test.h" #include "xen_unified.h" #include "qemu_internal.h" +#include "remote_internal.h" /* * TODO: @@ -64,6 +65,7 @@ * Note that the order is important: the first ones have a higher * priority when calling virConnectOpen. */ + if (remoteRegister () == -1) return -1; #ifdef WITH_XEN if (xenUnifiedRegister () == -1) return -1; #endif @@ -375,6 +377,7 @@ * * Returns NULL in case of error, a static zero terminated string otherwise. */ +/* See also: https://www.redhat.com/archives/libvir-list/2007-February/msg00096.html */ const char * virConnectGetType(virConnectPtr conn) { diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/libvirtd.c libvirt-oldremote/src/libvirtd.c --- libvirt-cvs/src/libvirtd.c 1970-01-01 01:00:00.000000000 +0100 +++ libvirt-oldremote/src/libvirtd.c 2007-04-04 14:39:17.000000000 +0100 @@ -0,0 +1,1053 @@ +/* + * libvirtd: This is a small server to be used in conjunction with + * the "remote" driver (remote_internal.c). + * + * Copyright (C) 2007 Red Hat, Inc. + * + * See COPYING.LIB for the license of this software. + * + * Richard Jones <rjones@xxxxxxxxxx> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <malloc.h> +#include <time.h> +#include <rpc/rpc.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <netdb.h> +#include <errno.h> +#include <getopt.h> +#include <assert.h> +#include <fnmatch.h> +#include <gnutls/gnutls.h> +#include <stdint.h> + +#include "config.h" +#include "sunrpc/svc_gnutls.h" +#include "sunrpc/svc_tcp2.h" +#include "sunrpc/svc_unix2.h" +#include "remote_internal.h" +#include "remote_rpc.h" +#include "conf.h" +#include "libvirt/virterror.h" +#include "libvirt/libvirt.h" +#include "internal.h" + +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) + +/* Set by the command line. */ +static int debug = 0, quiet = 0; + +/* Defaults for configuration file elements. */ +int listen_tls = 1; +int listen_tcp = 0; +int listen_unix = 1; +const char *tls_port = LIBVIRTD_TLS_PORT; +const char *tcp_port = LIBVIRTD_TCP_PORT; +const char *unix_socket = LIBVIRTD_UNIX_SOCKET; + +int tls_no_verify_certificate = 0; +int tls_no_verify_address = 0; +const char **tls_allowed_clients = 0; + +/* XXX Still to decide where these certificates should be located. */ +const char *key_file = "serverkey.pem"; +const char *cert_file = "servercert.pem"; +const char *ca_file = "demoCA/cacert.pem"; +const char *crl_file = ""; + +static void copy_error (remote_error *err, virErrorPtr verr); +static void out_of_memory_error (remote_error *err); +static void bad_connection_error (remote_error *err); + +static virConnectPtr lookup_connection (struct svc_req *req); +static void set_connection (struct svc_req *req, virConnectPtr conn); + +/*----------------------------------------------------------------------*/ +/* Server side of the remote procedure calls. */ + +remote_open_ret * +remote_open_1_svc (char **name, struct svc_req *req) +{ + static struct remote_open_ret ret; + + if (lookup_connection (req) != 0) { + ret.status = -1; + bad_connection_error (&ret.remote_open_ret_u.err); + } else { + pid_t pid = getpid (); + + /* If the name is "", convert it to NULL. */ + char *real_name = *name; + if (strcmp (real_name, "") == 0) real_name = NULL; + + /* Log connection name. */ + fprintf (stderr, "libvirtd (%ld): open \"%s\"\n", + (long) pid, real_name); + + virConnectPtr conn = virConnectOpen (real_name); + + if (conn) { + set_connection (req, conn); + ret.status = 0; + } else { + ret.status = -1; + copy_error (&ret.remote_open_ret_u.err, + virConnGetLastError (conn)); + } + } + + return &ret; +} + +remote_close_ret * +remote_close_1_svc (void *vp ATTRIBUTE_UNUSED, + struct svc_req *req) +{ + static struct remote_close_ret ret; + virConnectPtr conn = lookup_connection (req); + + if (!conn) { + ret.status = -1; + bad_connection_error (&ret.remote_close_ret_u.err); + } else { + int r = virConnectClose (conn); + + if (r == 0) { + ret.status = 0; + set_connection (req, 0); + } else { + ret.status = -1; + copy_error (&ret.remote_close_ret_u.err, + virConnGetLastError (conn)); + } + } + + return &ret; +} + +remote_type_ret * +remote_type_1_svc (void *vp ATTRIBUTE_UNUSED, + struct svc_req *req) +{ + static struct remote_type_ret ret; + virConnectPtr conn = lookup_connection (req); + + if (!conn) { + ret.status = -1; + bad_connection_error (&ret.remote_type_ret_u.err); + } else { + const char *type = virConnectGetType (conn); + + if (type) { + ret.status = 0; + ret.remote_type_ret_u.type = (char *) type; // string is static + } else { + ret.status = -1; + copy_error (&ret.remote_type_ret_u.err, + virConnGetLastError (conn)); + } + } + + return &ret; +} + +remote_version_ret * +remote_version_1_svc (void *vp ATTRIBUTE_UNUSED, + struct svc_req *req) +{ + static struct remote_version_ret ret; + virConnectPtr conn = lookup_connection (req); + + if (!conn) { + ret.status = -1; + bad_connection_error (&ret.remote_version_ret_u.err); + } else { + unsigned long hvVer; + if (virConnectGetVersion (conn, &hvVer) == 0) { + ret.status = 0; + ret.remote_version_ret_u.hvVer = hvVer; + } else { + ret.status = -1; + copy_error (&ret.remote_version_ret_u.err, + virConnGetLastError (conn)); + } + } + + return &ret; +} + +remote_nodeGetInfo_ret * +remote_nodegetinfo_1_svc (void *vp ATTRIBUTE_UNUSED, + struct svc_req *req) +{ + static struct remote_nodeGetInfo_ret ret; + virConnectPtr conn = lookup_connection (req); + + if (!conn) { + ret.status = -1; + bad_connection_error (&ret.remote_nodeGetInfo_ret_u.err); + } else { + static virNodeInfo info; + if (virNodeGetInfo (conn, &info) == 0) { + ret.status = 0; + ret.remote_nodeGetInfo_ret_u.info.model = info.model; + ret.remote_nodeGetInfo_ret_u.info.memory = info.memory; + ret.remote_nodeGetInfo_ret_u.info.cpus = info.cpus; + ret.remote_nodeGetInfo_ret_u.info.mhz = info.mhz; + ret.remote_nodeGetInfo_ret_u.info.nodes = info.nodes; + ret.remote_nodeGetInfo_ret_u.info.sockets = info.sockets; + ret.remote_nodeGetInfo_ret_u.info.cores = info.cores; + ret.remote_nodeGetInfo_ret_u.info.threads = info.threads; + } else { + ret.status = -1; + copy_error (&ret.remote_nodeGetInfo_ret_u.err, + virConnGetLastError (conn)); + } + } + + return &ret; +} + +// Just a number large enough to use for validation of the +// externally produced maxids. +#define MAX_DOMAINS 10000 + +remote_listDomains_ret * +remote_listdomains_1_svc (int *maxids, + struct svc_req *req) +{ + static struct remote_listDomains_ret ret; + virConnectPtr conn = lookup_connection (req); + + if (!conn) { + ret.status = -1; + bad_connection_error (&ret.remote_listDomains_ret_u.err); + // Sanity-check maxids before allocating the on-stack array. + } else if (*maxids < 0 || *maxids > MAX_DOMAINS) { + ret.status = -1; + out_of_memory_error (&ret.remote_listDomains_ret_u.err); + } else { + int ids[*maxids]; + int len = virConnectListDomains (conn, ids, *maxids); + + if (len >= 0) { + ret.status = 0; + ret.remote_listDomains_ret_u.ids.ids_len = len; + ret.remote_listDomains_ret_u.ids.ids_val = ids; + } else { + ret.status = -1; + copy_error (&ret.remote_listDomains_ret_u.err, + virConnGetLastError (conn)); + } + } + + return &ret; +} + +remote_numOfDomains_ret * +remote_numofdomains_1_svc (void *vp ATTRIBUTE_UNUSED, + struct svc_req *req) +{ + static struct remote_numOfDomains_ret ret; + virConnectPtr conn = lookup_connection (req); + + if (!conn) { + ret.status = -1; + bad_connection_error (&ret.remote_numOfDomains_ret_u.err); + } else { + int nr = virConnectNumOfDomains (conn); + + if (nr >= 0) { + ret.status = 0; + ret.remote_numOfDomains_ret_u.nr_domains = nr; + } else { + ret.status = -1; + copy_error (&ret.remote_numOfDomains_ret_u.err, + virConnGetLastError (conn)); + } + } + + return &ret; +} + +remote_domainCreateLinux_ret * +remote_domaincreatelinux_1_svc (remote_domainCreateLinux_args *args, + struct svc_req *req) +{ + static struct remote_domainCreateLinux_ret ret; + virConnectPtr conn = lookup_connection (req); + + if (!conn) { + ret.status = -1; + bad_connection_error (&ret.remote_domainCreateLinux_ret_u.err); + } else { + virDomainPtr dom = virDomainCreateLinux (conn, + args->xmlDesc, args->flags); + + if (dom) { + ret.status = 0; + // XXX No idea if this is the right thing to do. + ret.remote_domainCreateLinux_ret_u.domain = + malloc (sizeof *ret.remote_domainCreateLinux_ret_u.domain); + ret.remote_domainCreateLinux_ret_u.domain->name = + (char *) dom->name; + memcpy + (ret.remote_domainCreateLinux_ret_u.domain->uuid, + dom->uuid, + VIR_UUID_BUFLEN); + } else { + ret.status = -1; + copy_error (&ret.remote_domainCreateLinux_ret_u.err, + virConnGetLastError (conn)); + } + } + + return &ret; +} + + + + + + +/* You cannot use NULL for string<> in SunRPC. Instead, pass + * empty strings (using nasty casting to get around lack of + * any sort of const-correctness in SunRPC code). Convert + * empty strings on the client side back to NULLs. + */ +static char *null_string = (char *) ""; + +/* Handle translation between <virterror.h> and what we send + * back to the client over the wire. + */ +static void +copy_error (remote_error *err, virErrorPtr verr) +{ + /* No error, even though status code of the call indicates one. + * Return a "internal error" indication. + */ + if (!verr) { + err->code = VIR_ERR_INTERNAL_ERROR; + err->domain = 0; + err->message = null_string; + err->level = VIR_ERR_ERROR; + err->dom = NULL; + err->str1 = null_string; + err->str2 = null_string; + err->str3 = null_string; + err->int1 = 0; + err->int2 = 0; + err->net = NULL; + return; + } + + err->code = verr->code; + err->domain = verr->domain; + err->message = verr->message ? : null_string; + err->level = verr->level; + if (verr->dom) { + // XXX I have no idea if this is the right way to do this. + err->dom = malloc (sizeof *err->dom); + err->dom->name = verr->dom->name; + memcpy (err->dom->uuid, verr->dom->uuid, VIR_UUID_BUFLEN); + } + else err->dom = NULL; + err->str1 = verr->str1 ? : null_string; + err->str2 = verr->str2 ? : null_string; + err->str3 = verr->str3 ? : null_string; + err->int1 = verr->int1; + err->int2 = verr->int2; + if (verr->net) { + // XXX I have no idea if this is the right way to do this. + err->net = malloc (sizeof *err->net); + err->net->name = verr->net->name; + memcpy (err->net->uuid, verr->net->uuid, VIR_UUID_BUFLEN); + } else err->net = NULL; +} + +/* We have a few of our own errors. */ +static void +out_of_memory_error (remote_error *err) +{ + char *msg = (char *) "out of memory"; + err->code = VIR_ERR_NO_MEMORY; + err->domain = VIR_FROM_REMOTE; + err->message = msg; + err->level = VIR_ERR_ERROR; + err->dom = NULL; + err->str1 = msg; + err->str2 = null_string; + err->str3 = null_string; + err->int1 = 0; + err->int2 = 0; + err->net = NULL; +} + +static void +bad_connection_error (remote_error *err) +{ + char *msg = (char *) "bad connection"; + err->code = VIR_ERR_RPC_BAD_CONNECTION; + err->domain = VIR_FROM_REMOTE; + err->message = msg; + err->level = VIR_ERR_ERROR; + err->dom = NULL; + err->str1 = msg; + err->str2 = null_string; + err->str3 = null_string; + err->int1 = 0; + err->int2 = 0; + err->net = NULL; +} + +/*----------------------------------------------------------------------*/ +/* Map SunRPC connections to virConnectPtr. */ + +/* SunRPC is "connectionless", but in fact when used over TCP it is + * really connection-oriented. If your TCP connection goes down, + * the client needs to manually reconnect, which the remote client + * never does. So we associate virConnectPtr with an actual TCP + * connection. + * + * The questions are: (1) How and where do we store this association? + * (2) How can we clean up when the connection goes away? + * + * So for (1) we note that each server-side callback gets a pointer + * to struct svc_req, which contains a pointer to the transport + * (SVCXPRT *). It turns out (you need to read the code closely) + * that each transport pointer is unique to the connection, so we + * use that. + * + * For (2) we have modified the transports so that we can supply a + * cleanup callback which is called when the connection goes away. + * + * Now all we need is a mapping from SVCXPRT * to virConnectPtr, + * and a function to clean these up. + */ + +static struct virconnmap { + struct virconnmap *next; + + /* Key. */ + SVCXPRT *xprt; + + /* conn may be NULL in the case where a mapping exists, but the + * virConnectPtr has either not been created yet, or has been + * properly closed using the remote_close call. + */ + virConnectPtr conn; +} *virconnmap = 0; + +static void log_peer (int sock); + +/* Callback from transport: create a new mapping. */ +static int +create_mapping (SVCXPRT *xprt) +{ + log_peer (xprt->xp_sock); + + /* It's an internal error if it exists already. */ + struct virconnmap *p; + for (p = virconnmap; p; p = p->next) + if (p->xprt == xprt) { + fprintf (stderr, "libvirtd: internal error: create_mapping called but mapping already exists\n"); + return -1; + } + + /* Add the mapping. */ + p = malloc (sizeof *p); + if (p == 0) { + perror ("malloc"); + return -1; + } + p->next = virconnmap; + virconnmap = p; + p->xprt = xprt; + p->conn = 0; + return 0; +} + +/* Callback from transport: destroy an existing mapping. */ +static void +destroy_mapping (SVCXPRT *xprt) +{ + /* Log connection closed. */ + fprintf (stderr, "libvirtd (%d): connection closed\n", getpid ()); + + struct virconnmap *p, *lastp; + for (p = virconnmap, lastp = 0; p; lastp = p, p = p->next) + if (p->xprt == xprt) + goto found_it; + + /* Mapping not found - that's an internal error. */ + fprintf (stderr, "libvirtd: internal error: destroy_mapping called but mapping not found\n"); + return; + + found_it: + /* Do we need to clean up the connection? If the client called + * remote_close then conn will be NULL. Otherwise the connection + * has been dropped without a clean close, so we close it here. + */ + if (p->conn) + (void) virConnectClose (p->conn); + + /* Remove from linked list. */ + if (lastp) lastp->next = p->next; + else virconnmap = p->next; + free (p); +} + +/* Look up a connection in the mapping table. */ +static virConnectPtr +lookup_connection (struct svc_req *req) +{ + SVCXPRT *xprt = req->rq_xprt; + + struct virconnmap *p; + for (p = virconnmap; p; p = p->next) + if (p->xprt == xprt) + return p->conn; + + fprintf (stderr, "libvirtd: internal error: cannot find connection in mapping table\n"); + return NULL; +} + +/* Set connection in the mapping table. */ +static void +set_connection (struct svc_req *req, virConnectPtr conn) +{ + SVCXPRT *xprt = req->rq_xprt; + + struct virconnmap *p; + for (p = virconnmap; p; p = p->next) + if (p->xprt == xprt) { + p->conn = conn; + return; + } + + abort (); // Should never happen. +} + +/* Log the peer (client) IP address. */ +static void +log_peer (int sock) +{ + /* Log the connection to stderr. It will end up in syslog if + * logger is running. + */ + pid_t pid = getpid (); + int r; + fprintf (stderr, "libvirtd (%ld): new connection\n", (long) pid); + struct sockaddr_storage addr; + socklen_t addrlen = sizeof addr; + r = getpeername (sock, (struct sockaddr *) &addr, &addrlen); + if (r == -1) + perror ("getpeername"); + else { + if (addr.ss_family == AF_UNIX) + /* getnameinfo doesn't do anything sensible with AF_UNIX + * addresses, but doesn't fail either (it sets host to + * "localhost" and leaves serv as random), so print those + * out manually instead. + */ + fprintf (stderr, "libvirtd (%ld): connection from Unix domain socket (may include connections over ssh and ext transports)\n", + (long) pid); + else { + char host[NI_MAXHOST] = { '\0' }, serv[NI_MAXSERV] = { '\0' }; + r = getnameinfo ((struct sockaddr *) &addr, addrlen, + host, sizeof host, + serv, sizeof serv, + NI_NUMERICHOST | NI_NUMERICSERV); + if (r != 0) + fprintf (stderr, "libvirtd (%ld): getnameinfo: %s\n", + (long) pid, gai_strerror (r)); + else + fprintf (stderr, "libvirtd (%ld): connection from %s port %s\n", + (long) pid, host, serv); + } + } +} + +/*----------------------------------------------------------------------*/ +/* Main function. */ + +static gnutls_certificate_credentials_t x509_cred; +static gnutls_dh_params_t dh_params; + +static void *my_malloc (size_t); +static char *my_strdup (const char *); + +static void generate_dh_params (void); +static int make_sockets (int *fds, int max_fds, int *nfds_r, + const char *service); +static int check_allowed_client (void *, const char *addr); + +int +main (int argc, char *argv[]) +{ + int arg; + const char *conffile = LIBVIRTD_CONFIGURATION_FILE; + + // Read the command line. + while ((arg = getopt (argc, argv, "df:q")) != -1) { + switch (arg) + { + case 'f': + conffile = optarg; + break; + + case 'q': + quiet = 1; + break; + + case 'd': + debug = 1; + break; + + case '?': + fprintf (stderr, + "libvirtd [-dq] [-f conffile]\n" + " -d Debug mode (don't fork into bg)\n" + " -q Quiet\n" + " -f conffile Use configuration file (default: %s)\n" + , conffile + ); + exit (1); + } + } + + // Read the configuration file. If it's not there, proceed with defaults. + virConfPtr conf = virConfReadFile (conffile); + if (conf) { + virConfValuePtr p; + +#define CHECK_TYPE(name,typ) if (p && p->type != (typ)) { \ + fprintf (stderr, "libvirtd: %s: %s: expected type " #typ "\n", \ + conffile, (name)); \ + exit (1); \ + } + + p = virConfGetValue (conf, "listen_tls"); + CHECK_TYPE ("listen_tls", VIR_CONF_LONG); + listen_tls = p ? p->l : listen_tls; + + p = virConfGetValue (conf, "listen_tcp"); + CHECK_TYPE ("listen_tcp", VIR_CONF_LONG); + listen_tcp = p ? p->l : listen_tcp; + + p = virConfGetValue (conf, "listen_unix"); + CHECK_TYPE ("listen_unix", VIR_CONF_LONG); + listen_unix = p ? p->l : listen_unix; + + p = virConfGetValue (conf, "tls_port"); + CHECK_TYPE ("tls_port", VIR_CONF_STRING); + tls_port = p ? my_strdup (p->str) : tls_port; + + p = virConfGetValue (conf, "tcp_port"); + CHECK_TYPE ("tcp_port", VIR_CONF_STRING); + tcp_port = p ? my_strdup (p->str) : tcp_port; + + p = virConfGetValue (conf, "unix_socket"); + CHECK_TYPE ("unix_socket", VIR_CONF_STRING); + unix_socket = p ? my_strdup (p->str) : unix_socket; + + p = virConfGetValue (conf, "tls_no_verify_certificate"); + CHECK_TYPE ("tls_no_verify_certificate", VIR_CONF_LONG); + tls_no_verify_certificate = p ? p->l : tls_no_verify_certificate; + + p = virConfGetValue (conf, "tls_no_verify_address"); + CHECK_TYPE ("tls_no_verify_address", VIR_CONF_LONG); + tls_no_verify_address = p ? p->l : tls_no_verify_address; + + p = virConfGetValue (conf, "tls_allowed_clients"); + if (p) + { + switch (p->type) + { + case VIR_CONF_STRING: + tls_allowed_clients = my_malloc (2 * sizeof (char *)); + tls_allowed_clients[0] = my_strdup (p->str); + tls_allowed_clients[1] = 0; + break; + + case VIR_CONF_LIST: { + int i, len = 0; + virConfValuePtr pp; + for (pp = p->list; pp; pp = p->next) + len++; + tls_allowed_clients = + my_malloc ((1+len) * sizeof (char *)); + for (i = 0, pp = p->list; pp; ++i, pp = p->next) { + if (pp->type != VIR_CONF_STRING) { + fprintf (stderr, "libvirtd: %s: tls_allowed_clients: should be a string or list of strings\n", conffile); + exit (1); + } + tls_allowed_clients[i] = my_strdup (pp->str); + } + tls_allowed_clients[i] = 0; + break; + } + + default: + fprintf (stderr, "libvirtd: %s: tls_allowed_clients: should be a string or list of strings\n", conffile); + exit (1); + } + } + + p = virConfGetValue (conf, "key_file"); + CHECK_TYPE ("key_file", VIR_CONF_STRING); + key_file = p ? my_strdup (p->str) : key_file; + + p = virConfGetValue (conf, "cert_file"); + CHECK_TYPE ("cert_file", VIR_CONF_STRING); + cert_file = p ? my_strdup (p->str) : cert_file; + + p = virConfGetValue (conf, "ca_file"); + CHECK_TYPE ("ca_file", VIR_CONF_STRING); + ca_file = p ? my_strdup (p->str) : ca_file; + + p = virConfGetValue (conf, "crl_file"); + CHECK_TYPE ("crl_file", VIR_CONF_STRING); + crl_file = p ? my_strdup (p->str) : crl_file; + + virConfFree (conf); + } + + // This may not be an error. cf. /etc/exports + if (!listen_tls && !listen_tcp && !listen_unix) { + fprintf (stderr, "libvirtd: all services disabled, so exiting\n"); + exit (0); + } + + if (listen_tls) { + int err; + + /* Initialise GnuTLS. */ + gnutls_global_init (); + + err = gnutls_certificate_allocate_credentials (&x509_cred); + if (err) { gnutls_perror (err); exit (1); } + if (ca_file && ca_file[0] != '\0') { + if (!quiet) + fprintf (stderr, "libvirtd: loading CA cert from %s\n", + ca_file); + err = gnutls_certificate_set_x509_trust_file (x509_cred, ca_file, + GNUTLS_X509_FMT_PEM); + if (err < 0) { gnutls_perror (err); exit (1); } + } + + if (crl_file && crl_file[0] != '\0') { + if (!quiet) + fprintf (stderr, "libvirtd: loading CRL from %s\n", + crl_file); + err = gnutls_certificate_set_x509_crl_file (x509_cred, crl_file, + GNUTLS_X509_FMT_PEM); + if (err < 0) { gnutls_perror (err); exit (1); } + } + + if (cert_file && cert_file[0] != '\0' && + key_file && key_file[0] != '\0') { + if (!quiet) + fprintf (stderr, + "libvirtd: loading cert and key from %s and %s\n", + cert_file, key_file); + err = + gnutls_certificate_set_x509_key_file (x509_cred, + cert_file, key_file, + GNUTLS_X509_FMT_PEM); + if (err < 0) { gnutls_perror (err); exit (1); } + } + + generate_dh_params (); + gnutls_certificate_set_dh_params (x509_cred, dh_params); + + int fds[2]; + int nfds = 0; + if (make_sockets (fds, 2, &nfds, tls_port) == -1) + exit (1); + + int i; + for (i = 0; i < nfds; ++i) { + SVCXPRT *transp = svcgnutls_create (fds[i], 0, 0, + create_mapping, + destroy_mapping, + x509_cred, + tls_no_verify_certificate, + tls_no_verify_address, + NULL, check_allowed_client); + if (!transp) { + fprintf (stderr, "libvirtd: cannot create TLS service on port %s\n", tls_port); + exit (1); + } + + /* Because final arg is 0, this will not register with portmap. */ + if (!svc_register (transp, LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1, + libvirtremote_1, 0)) { + fprintf (stderr, "libvirtd: unable to register (LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1, 0)\n"); + exit (1); + } + } + + if (!quiet) + fprintf (stderr, "libvirtd: TLS service listening on port %s\n", + tls_port); + } + + if (listen_tcp) { + int fds[2]; + int nfds = 0; + if (make_sockets (fds, 2, &nfds, tcp_port) == -1) + exit (1); + + int i; + for (i = 0; i < nfds; ++i) { + SVCXPRT *transp = svctcp2_create (fds[i], 0, 0, + create_mapping, + destroy_mapping); + if (!transp) { + fprintf (stderr, "libvirtd: cannot create TCP service on port %s\n", tcp_port); + exit (1); + } + + /* Because final arg is 0, this will not register with portmap. */ + if (!svc_register (transp, LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1, + libvirtremote_1, 0)) { + fprintf (stderr, "libvirtd: unable to register (LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1, 0)\n"); + exit (1); + } + } + + if (!quiet) + fprintf (stderr, "libvirtd: unsafe TCP service listening on port %s (only use this for testing)\n", + tcp_port); + } + + if (listen_unix) { + /* XXX Not sure if this is the right thing to do. */ + if (unlink (unix_socket) == -1 && errno != ENOENT) { + perror (unix_socket); + exit (1); + } + + int sock = RPC_ANYSOCK; + SVCXPRT *transp = svcunix2_create (sock, 0, 0, + create_mapping, + destroy_mapping, + (char *) unix_socket); + if (!transp) { + fprintf (stderr, "libvirtd: cannot create Unix domain socket service on socket %s\n", unix_socket); + exit (1); + } + + /* Because final arg is 0, this will not register with portmap. */ + if (!svc_register (transp, LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1, + libvirtremote_1, 0)) { + fprintf (stderr, "libvirtd: unable to register (LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1, 0)\n"); + exit (1); + } + + if (!quiet) + fprintf (stderr, "libvirtd: Unix service listening on socket %s\n", + unix_socket); + } + + /* Daemonize. */ + if (!debug) { + if (daemon (0, 0) == -1) { + perror ("daemon"); + exit (2); + } + +#ifdef LOGGER + /* Send stderr to syslog using logger. It's a lot simpler + * to do this. Note that SunRPC in glibc prints lots of + * gumf to stderr and it'd be a load of work to change that. + */ + int fd[2]; + if (pipe (fd) == -1) { + perror ("pipe"); + exit (2); + } + pid_t pid = fork (); + if (pid == -1) { + perror ("fork"); + exit (2); + } + if (pid == 0) { /* Child - logger. */ + const char *args[] = { + "logger [libvirtd]", + "-t", "libvirtd", + "-p", "daemon.notice", + NULL + }; + close (fd[1]); + dup2 (fd[0], 0); + close (fd[0]); + execv (/*LOGGER*/"/tmp/nothere", (char *const *) args); + perror ("execv"); + _exit (1); + } + close (fd[0]); + dup2 (fd[1], 2); + close (fd[1]); +#endif + } + + /* Start up message. Note that this also ensures that we have + * a clear path to syslog (if running logger) or to stderr, + * because we carefully check the return value. So this isn't + * just for vanity. + */ + char msg[] = + "libvirtd: " + "version " TOSTRING(LIBVIR_VERSION_NUMBER) " " + "protocol " TOSTRING(LIBVIRTREMOTE_VERS1) "\n"; + if (fprintf (stderr, "%s", msg) != sizeof msg - 1) { + /* Erm, well we can't write to stderr here ... XXX */ + fprintf (stderr, "libvirtd: cannot write to stderr\n"); + exit (1); + } + + svc_run (); + abort (); /* svc_run should never return. */ +} + +// XXX DH_BITS has to match the value define in svc_gnutls.c +#define DH_BITS 1024 + +static void +generate_dh_params (void) +{ + int err; + + /* Generate Diffie Hellman parameters - for use with DHE + * kx algorithms. These should be discarded and regenerated + * once a day, once a week or once a month. Depending on the + * security requirements. + */ + err = gnutls_dh_params_init (&dh_params); + if (err) { gnutls_perror (err); exit (1); } + err = gnutls_dh_params_generate2 (dh_params, DH_BITS); + if (err) { gnutls_perror (err); exit (1); } +} + +// See: http://people.redhat.com/drepper/userapi-ipv6.html +static int +make_sockets (int *fds, int max_fds, int *nfds_r, const char *service) +{ + struct addrinfo *ai; + struct addrinfo hints; + memset (&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; + hints.ai_socktype = SOCK_STREAM; + + int e = getaddrinfo (NULL, service, &hints, &ai); + if (e != 0) { + fprintf (stderr, "getaddrinfo: %s\n", gai_strerror (e)); + return -1; + } + + struct addrinfo *runp = ai; + while (runp && *nfds_r < max_fds) { + fds[*nfds_r] = socket (runp->ai_family, runp->ai_socktype, + runp->ai_protocol); + if (fds[*nfds_r] == -1) { + perror ("socket"); + return -1; + } + + int opt = 1; + setsockopt (fds[*nfds_r], SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt); + + if (bind (fds[*nfds_r], runp->ai_addr, runp->ai_addrlen) == -1) { + if (errno != EADDRINUSE) { + perror ("bind"); + return -1; + } + close (fds[*nfds_r]); + } + else { + if (listen (fds[*nfds_r], SOMAXCONN) == -1) { + perror ("listen"); + return -1; + } + ++*nfds_r; + } + runp = runp->ai_next; + } + + freeaddrinfo (ai); + + return 0; +} + +/* Check the IP address matches one on the list of wildcards + * tls_allowed_clients. + */ +static int +check_allowed_client (void *vp ATTRIBUTE_UNUSED, const char *addr) +{ + const char **wildcards = tls_allowed_clients; + + /* User has not set up a list of tls_allowed_clients. */ + if (!wildcards) return 1; + + if (debug) + fprintf (stderr, + "check_allowed_client: trying to match addr %s\n", addr); + + while (*wildcards) { + if (fnmatch (*wildcards, addr, 0) == 0) + return 1; + wildcards++; + } + + if (debug) + fprintf (stderr, + "check_allowed_client: addr %s did not match\n", addr); + + return 0; +} + +/* Private malloc and strdup functions which always succeed. For + * justification, see http://et.redhat.com/~rjones/ These are only + * for use while the daemon is starting up. Once started we don't + * want memory allocations to abort, since that's a DoS. + */ +static void * +my_malloc (size_t n) +{ + void *ptr = malloc (n); + if (ptr == 0) abort (); + return ptr; +} + +static char * +my_strdup (const char *str) +{ + char *str2 = strdup (str); + if (str2 == 0) abort (); + return str2; +} + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/libvirt_sym.version libvirt-oldremote/src/libvirt_sym.version --- libvirt-cvs/src/libvirt_sym.version 2007-03-16 11:04:46.000000000 +0000 +++ libvirt-oldremote/src/libvirt_sym.version 2007-03-16 11:13:17.000000000 +0000 @@ -63,6 +63,10 @@ virDomainAttachDevice; virDomainDetachDevice; + _virConfReadFile; + _virConfGetValue; + _virConfFree; + virConnectNumOfNetworks; virConnectListNetworks; virConnectNumOfDefinedNetworks; diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/Makefile.am libvirt-oldremote/src/Makefile.am --- libvirt-cvs/src/Makefile.am 2007-04-04 15:39:43.000000000 +0100 +++ libvirt-oldremote/src/Makefile.am 2007-04-04 15:43:51.000000000 +0100 @@ -3,6 +3,7 @@ INCLUDES = -I$(top_builddir)/include -I@top_srcdir@/include @LIBXML_CFLAGS@ -I@top_srcdir@/qemud \ -DBINDIR=\""$(libexecdir)"\" -DSBINDIR=\""$(sbindir)"\" -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ -DLOCAL_STATE_DIR=\""$(localstatedir)"\" \ + -DSYSCONFDIR=\""$(sysconfdir)"\" \ -DGETTEXT_PACKAGE=\"$(PACKAGE)\" $(WARN_CFLAGS) $(LIBVIRT_FEATURES) DEPS = libvirt.la LDADDS = @STATIC_BINARIES@ libvirt.la @@ -30,10 +31,18 @@ driver.h \ proxy_internal.c proxy_internal.h \ conf.c conf.h \ - xm_internal.c xm_internal.h \ + xm_internal.c xm_internal.h \ + sunrpc/create_xid.h sunrpc/create_xid.c \ + remote_rpc_xdr.c \ + remote_rpc_clnt.c remote_rpc.h \ + sunrpc/clnt_ext.h sunrpc/clnt_gnutls.h \ + sunrpc/clnt_tcp2.h sunrpc/clnt_ext.c \ + sunrpc/clnt_gnutls.c sunrpc/clnt_tcp2.c \ + remote_internal.c remote_internal.h \ qemu_internal.c qemu_internal.h bin_PROGRAMS = virsh +sbin_PROGRAMS = libvirtd virsh_SOURCES = virsh.c console.c console.h virsh_LDFLAGS = $(COVERAGE_LDFLAGS) @@ -41,6 +50,33 @@ virsh_LDADD = $(LDADDS) $(VIRSH_LIBS) virsh_CFLAGS = $(COVERAGE_CFLAGS) +libvirtd_SOURCES = \ + remote_rpc_svc.c remote_rpc_xdr.c \ + sunrpc/svc_gnutls.c sunrpc/svc_gnutls.h \ + sunrpc/svc_tcp2.c sunrpc/svc_tcp2.h \ + sunrpc/svc_unix2.c sunrpc/svc_unix2.h \ + libvirtd.c +libvirtd_LDFLAGS = +libvirtd_DEPENDENCIES = $(DEPS) +libvirtd_LDADD = $(LDADDS) + +# Build client and server stubs. +# 'rpcgen' program comes with glibc. +# This is convoluted because we need to build the server stubs +# without main() (-m option), but you can't just do that in a +# simple way. +remote_rpc_clnt.c remote_rpc.h remote_rpc_svc.c remote_rpc_xdr.c: remote_rpc.x + rpcgen remote_rpc.x + rm -f remote_rpc_svc.c + rpcgen -m remote_rpc.x > remote_rpc_svc.c + mv remote_rpc_xdr.c remote_rpc_xdr.c.bak + perl -w remote_rpc_xdr_fix.pl < remote_rpc_xdr.c.bak > remote_rpc_xdr.c + mv remote_rpc.h remote_rpc.h.bak + perl -w remote_rpc_fix.pl < remote_rpc.h.bak > remote_rpc.h + +remote_rpc_xdr.c: remote_rpc_xdr_fix.pl +remote_rpc.h: remote_rpc_fix.pl + # # target to ease building test programs # @@ -57,4 +93,5 @@ %.cov: .libs/%.o gcov -b -f -o .libs $< > $@ -CLEANFILES = *.cov *.gcov .libs/*.gcda .libs/*.gcno *.gcno *.gcda +CLEANFILES = *.cov *.gcov .libs/*.gcda .libs/*.gcno *.gcno *.gcda \ + remote_rpc_clnt.c remote_rpc.h remote_rpc_svc.c remote_rpc_xdr.c diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/remote_internal.c libvirt-oldremote/src/remote_internal.c --- libvirt-cvs/src/remote_internal.c 1970-01-01 01:00:00.000000000 +0100 +++ libvirt-oldremote/src/remote_internal.c 2007-04-04 14:39:14.000000000 +0100 @@ -0,0 +1,1054 @@ +/* + * remote_internal.c: driver to provide access to libvirtd running + * on a remote machine. + * + * Copyright (C) 2007 Red Hat, Inc. + * + * See COPYING.LIB for the license of this software. + * + * Richard Jones <rjones@xxxxxxxxxx> + */ + +/* Architecture and notes: + * + * virConnectOpen in this driver looks for remote URLs and will + * try some method to connect to a libvirtd running on a remote + * machine. + * + * Local URLs are the standard URLs used by other drivers - + * for example "qemud:///system". + * + * Remote URLs contain either a server name or a remote transport + * name. For example: "qemud://server.example.com/system" contains + * a server name (server.example.com), and "qemud+unix:///system" + * contains a transport (unix). Commonly remote URLs contain + * both, for example: "qemud+tls://server.example.com/system" + * (transport tls, server name server.example.com). + * + * All other vir* calls made on this connection are forwarded + * to the libvirtd daemon which carries out the requested action. + * So for example if you call virDomainCreateLinux, then the + * domain gets created on the remote machine, and virConnectListDomains + * lists domains running on the remote machine. + * + * Connections can be authenticated and encrypted -- it depends + * on the transport selected. + * + * The current implementation uses SunRPC layered over one of: + * - GnuTLS (an SSL/TLS library providing enterprise-level + * authentication and encryption) + * - a local Unix domain socket + * - ssh or another external program such as rsh + * - a plain TCP socket (unencrypted, not recommended for production) + * + * See http://et.redhat.com/~rjones/secure_rpc for an insight into + * the thinking that went into the selection of SunRPC. In + * the future we may use a different RPC system - for example + * XML-RPC would be a logical choice - so for now you should regard + * the protocol used as private and subject to change in future + * versions of libvirt without notice. + * + * The URL (name parameter to virConnectOpen) is a URL of the + * following general form: + * + * "driver+transport://server:port/path?var=value&var=value&..." + * + * Transport, server and port are all optional (except that you must + * have either transport or server as explained above). You may + * have zero or more variables (or omit the query string entirely). + * Driver is the usual libvirt driver, as used on the remote + * machine, and path is specific to driver. + * + * Some examples: + * + * "xend://server/" Remote xend, using TLS, port 16514 + * "qemud+unix:///session" Communicate over Unix domain socket + * to a local libvirtd. + * "qemud+ssh://server/session?command=/opt/openssh/bin/ssh" + * Communicate over ssh to a remote + * libvirtd on server. Control qemu + * on remote. ssh command is located + * in a non-standard place. + * "qemud+tcp://server:5000/session" + * Unencrypted TCP, port 5000. + * "xend+ext:///?command=my_shell_script" + * Run my_shell_script which uses its own + * method (eg. rsh, ssh, ...) to talk to + * the remote libvirtd, controlling a + * remote xend. + * + * [To emphasise - in ALL instances communication is with a remote + * libvirtd, even if that remote libvirtd itself talks to another + * daemon such as xend or qemud]. + * + * The transport is one of: tls, unix, ssh, ext or tcp. If no + * transport is given, the default is to use tls. + * + * For tls, the default port is 16514. For tcp, the default port is + * 16509 (but note that tcp is almost never enabled because it is + * insecure - it's only there for testing). + * + * For ssh: The default port for ssh is 22. You should configure ssh + * so that it doesn't ask for a password (eg. using ssh-agent). The + * remote server should have a recent version of the the netcat program + * installed as 'nc', and the remote libvirtd must be configured to + * listen on a Unix domain socket. The following full command is run: + * ssh -p $port [-l $username] $hostname $netcat -U /var/run/libvirtd/socket + * + * For ext: Only the command you specify is run. It is up to you to + * write this command so that it somehow makes a connection to a + * remote libvirtd, and passes input and output over its stdin/stdout. + * + * The var=value pairs provide optional extra information: + * + * Variable Transport Meaning + * ----------------------------------------- + * command ssh,ext Name or path of external program. + * For ssh this defaults to "ssh". + * For ext you must supply it. + * name (all) Optionally the name used in remote + * virConnectOpen. The default is to + * construct the name by removing + * transport, server name, port and + * variables from the remote URL to + * form a local URL. But if this + * doesn't give the desired result you + * may specify the exact name here. + * socket unix,ssh,ext Name of the Unix domain socket. The + * default is in <remote_internal.h>. + * netcat ssh Name of the netcat program on the + * remote server. Default is "nc". + * no_verify tls If set to a non-zero value, this will + * not check the peer's certificate + * (it will still print a warning). + * The default is to always check. + * + * The value is %-escaped (just like URL encoding), so if you want it + * to contain a literal space use "%20" or "+", if you want it to have + * a literal + character use "%2b", and for a literal % character use "%25". + * + * To provide some forwards compatibility, variables which are not + * understood are ignored (but a warning is printed on stderr). + * + * For the details of the implementation of SunRPC over GnuTLS, etc. + * please see http://et.redhat.com/~rjones/secure_rpc which contains + * simple code samples which will allow you to understand what's + * going on here. + * + * - Richard Jones <rjones@xxxxxxxxxx> + */ + +#define _GNU_SOURCE + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <rpc/rpc.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <netdb.h> +#include <assert.h> +#include <gnutls/gnutls.h> +#include <libxml/uri.h> +#include <stdint.h> + +#include "sunrpc/clnt_ext.h" +#include "sunrpc/clnt_gnutls.h" +#include "sunrpc/clnt_tcp2.h" + +#include "internal.h" +#include "driver.h" +#include "remote_internal.h" +#include "remote_rpc.h" + +#define DEBUG 1 /* Enable verbose messages on stderr. */ + +#define MAGIC 999 /* private_data->magic if OK */ +#define DEAD 998 /* private_data->magic if dead/closed */ + +struct private_data { + int magic; /* Should be MAGIC or DEAD. */ + int sock; /* Socket. */ + CLIENT *cl; /* SunRPC client. */ +}; + +#define GET_PRIVATE(conn,retcode) \ + struct private_data *priv = (struct private_data *) (conn)->privateData; \ + assert (priv); \ + if (priv->magic == DEAD) { \ + error (conn, VIR_ERR_INVALID_ARG, \ + "tried to use a closed or uninitialised handle"); \ + return (retcode); \ + } \ + assert (priv->magic == MAGIC) + +static void error (virConnectPtr conn, virErrorNumber code, const char *info); +static void server_error (virConnectPtr conn, remote_error *err); +static virDomainPtr get_domain_from_name (virConnectPtr conn, remote_domain); +static virNetworkPtr get_network_from_name (virConnectPtr conn, remote_network); + +#define CAFILE "demoCA/cacert.pem" /* XXX */ +#define KEY_FILE "127001key.pem" /* XXX */ +#define CERT_FILE "127001cert.pem" /* XXX */ + +static gnutls_certificate_credentials_t x509_cred; + +static int +initialise_gnutls (void) +{ + static int initialised = 0; + int err; + + if (initialised) return 0; + + gnutls_global_init (); + + /* X509 stuff */ + err = gnutls_certificate_allocate_credentials (&x509_cred); + if (err) { gnutls_perror (err); return -1; } + + /* Set the trusted CA cert. */ +#if DEBUG + fprintf (stderr, "loading CA file %s\n", CAFILE); +#endif + err = + gnutls_certificate_set_x509_trust_file (x509_cred, CAFILE, + GNUTLS_X509_FMT_PEM); + if (err < 0) { gnutls_perror (err); return -1; } + + /* Set the client certificate and private key. */ +#if DEBUG + fprintf (stderr, "loading client cert and key from files %s and %s\n", + CERT_FILE, KEY_FILE); +#endif + err = + gnutls_certificate_set_x509_key_file (x509_cred, + CERT_FILE, KEY_FILE, + GNUTLS_X509_FMT_PEM); + if (err < 0) { gnutls_perror (err); exit (1); } + + initialised = 1; + return 0; +} + +enum transport { + trans_tls, + trans_unix, + trans_ssh, + trans_ext, + trans_tcp +}; + +struct query_var { + int ignore; + char *name, *value; +}; + +static char *get_transport_from_scheme (char *scheme); +static struct query_var **query_split (virConnectPtr conn, const char *query, int *n_r); +static char *query_join (virConnectPtr conn, struct query_var **vars, int *n_r); +static void free_query_vars (struct query_var **vars, int n); + +static int +do_open (virConnectPtr conn, const char *uri_str, + int flags ATTRIBUTE_UNUSED) +{ + if (!uri_str) return VIR_DRV_OPEN_DECLINED; + + /* We have to parse the URL every time to discover whether + * it contains a transport or remote server name. There's no + * way to get around this. + */ + xmlURIPtr uri = xmlParseURI (uri_str); + if (!uri || !uri->scheme) + return VIR_DRV_OPEN_DECLINED; /* Decline - not a URL. */ + + char *transport_str = get_transport_from_scheme (uri->scheme); + if (!uri->server && !transport_str) + return VIR_DRV_OPEN_DECLINED; /* Decline - not a remote URL. */ + + /* What transport? */ + enum transport transport; + if (!transport_str || strcasecmp (transport_str, "tls") == 0) + transport = trans_tls; + else if (strcasecmp (transport_str, "unix") == 0) + transport = trans_unix; + else if (strcasecmp (transport_str, "ssh") == 0) + transport = trans_ssh; + else if (strcasecmp (transport_str, "ext") == 0) + transport = trans_ext; + else if (strcasecmp (transport_str, "tcp") == 0) + transport = trans_tcp; + else { + error (conn, VIR_ERR_INVALID_ARG, + "remote_open: transport in URL not recognised " + "(should be tls|unix|ssh|ext|tcp)"); + return VIR_DRV_OPEN_ERROR; + } + + /* Return code from this function, and the private data. */ + int retcode = VIR_DRV_OPEN_ERROR; + struct private_data priv = { .magic = DEAD }; + char *name = 0, *command = 0, *sockname = 0, *netcat = 0, *username = 0; + int no_verify = 0; + char **cmd_argv = 0; + + /* Remote server defaults to "localhost" if not specified. */ + char *server = strdup (uri->server ? uri->server : "localhost"); + if (!server) { + out_of_memory: + error (conn, VIR_ERR_NO_MEMORY, "remote_open"); + goto failed; + } + char *port; + if (uri->port != 0) { + if (asprintf (&port, "%d", uri->port) == -1) goto out_of_memory; + } else if (transport == trans_tls) { + port = strdup (LIBVIRTD_TLS_PORT); + if (!port) goto out_of_memory; + } else if (transport == trans_tcp) { + port = strdup (LIBVIRTD_TCP_PORT); + if (!port) goto out_of_memory; + } else if (transport == trans_ssh) { + port = strdup ("22"); + if (!port) goto out_of_memory; + if (uri->user) { + username = strdup (uri->user); + if (!username) goto out_of_memory; + } + } else + port = NULL; /* Port not used for unix, ext. */ + + /* Get the variables from the query string. + * Then we need to reconstruct the query string (because + * feasibly it might contain variables needed by the real driver, + * although that won't be the case for now). + */ + int i, n; + struct query_var **vars = query_split (conn, uri->query, &n); + if (n < 0) goto failed; + + for (i = 0; i < n; ++i) { + if (strcasecmp (vars[i]->name, "name") == 0) { + name = strdup (vars[i]->value); + if (!name) goto out_of_memory; + vars[i]->ignore = 1; + } else if (strcasecmp (vars[i]->name, "command") == 0) { + command = strdup (vars[i]->value); + if (!command) goto out_of_memory; + vars[i]->ignore = 1; + } else if (strcasecmp (vars[i]->name, "socket") == 0) { + sockname = strdup (vars[i]->value); + if (!sockname) goto out_of_memory; + vars[i]->ignore = 1; + } else if (strcasecmp (vars[i]->name, "netcat") == 0) { + netcat = strdup (vars[i]->value); + if (!netcat) goto out_of_memory; + vars[i]->ignore = 1; + } else if (strcasecmp (vars[i]->name, "no_verify") == 0) { + no_verify = atoi (vars[i]->value); + vars[i]->ignore = 1; + } else + fprintf (stderr, + "remote_open: passing through variable '%s' to remote end\n", + vars[i]->name); + } + + if (uri->query) xmlFree (uri->query); + uri->query = query_join (conn, vars, &n); + if (n < 0) goto failed; + free_query_vars (vars, n); + + /* For ext transport, command is required. */ + if (transport == trans_ext && !command) { + error (conn, VIR_ERR_INVALID_ARG, "remote_open: for 'ext' transport, command is required"); + goto failed; + } + + /* Construct the original name. */ + if (!name) { + /* Remove the transport (if any) from the scheme. */ + if (transport_str) { + assert (transport_str[-1] == '+'); + transport_str[-1] = '\0'; + } + /* Remove the server name and port number. */ + if (uri->server) xmlFree (uri->server); + uri->server = 0; + uri->port = 0; + + name = (char *) xmlSaveUri (uri); + } + + assert (name); +#if DEBUG + fprintf (stderr, "proceeding with name = %s\n", name); +#endif + + if (initialise_gnutls () == -1) { + error (conn, VIR_ERR_INVALID_ARG, + "remote_open: GnuTLS initialisation failed"); + goto failed; + } + + /* Connect to the remote service. */ + switch (transport) { + case trans_tls: + case trans_tcp: { + // http://people.redhat.com/drepper/userapi-ipv6.html + struct addrinfo *res, *r; + struct addrinfo hints; + memset (&hints, 0, sizeof hints); + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG; + int e = getaddrinfo (server, port, &hints, &res); + if (e != 0) { + error (conn, VIR_ERR_INVALID_ARG, gai_strerror (e)); + goto failed; + } + + /* Try to connect to each returned address in turn. */ + /* XXX This loop contains a subtle problem. In the case + * where a host is accessible over IPv4 and IPv6, it will + * try the IPv4 and IPv6 addresses in turn. However it + * should be able to present different client certificates + * (because the commonName field in a client cert contains + * the client IP address, which is different for IPv4 and + * IPv6). At the moment we only have a single client + * certificate, and no way to specify what address family + * that certificate belongs to. + */ + for (r = res; r; r = r->ai_next) { + priv.sock = RPC_ANYSOCK; + priv.cl = + transport == trans_tls + ? clntgnutls_create (r->ai_family, r->ai_addr, r->ai_addrlen, + x509_cred, + no_verify, server, + LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1, + &priv.sock, 0, 0) + : clnttcp2_create (r->ai_family, r->ai_addr, r->ai_addrlen, + LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1, + &priv.sock, 0, 0); + if (priv.cl) + goto tcp_connected; + } + + freeaddrinfo (res); + error (conn, VIR_ERR_RPC, + clnt_spcreateerror ("could not create SunRPC client")); + goto failed; + + tcp_connected: + freeaddrinfo (res); + + // NB. All versioning is done by SunRPC so we don't need to worry + // that we are connected to an incompatible daemon. + break; + } + + case trans_unix: { + sockname = sockname ? : strdup (LIBVIRTD_UNIX_SOCKET); + +#define UNIX_PATH_MAX(addr) (sizeof (addr).sun_path) + struct sockaddr_un addr; + memset (&addr, 0, sizeof addr); + addr.sun_family = AF_UNIX; + strncpy (addr.sun_path, sockname, UNIX_PATH_MAX (addr)); + + priv.sock = RPC_ANYSOCK; + priv.cl = + clntunix_create (&addr, + LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1, + &priv.sock, 0, 0); + if (!priv.cl) { + error (conn, VIR_ERR_RPC, + clnt_spcreateerror ("could not create SunRPC client")); + goto failed; + } + + break; + } + + case trans_ssh: { + int j, nr_args = username ? 10 : 8; + + command = command ? : strdup ("ssh"); + + // Generate the final command argv[] array. + // ssh -p $port [-l $username] $hostname $netcat -U $sockname [NULL] + cmd_argv = malloc (nr_args * sizeof (char *)); + j = 0; + cmd_argv[j++] = strdup (command); + cmd_argv[j++] = strdup ("-p"); + cmd_argv[j++] = strdup (port); + if (username) { + cmd_argv[j++] = strdup ("-l"); + cmd_argv[j++] = strdup (username); + } + cmd_argv[j++] = strdup (server); + cmd_argv[j++] = strdup (netcat ? netcat : "nc"); + cmd_argv[j++] = strdup ("-U"); + cmd_argv[j++] = strdup (sockname ? sockname : LIBVIRTD_UNIX_SOCKET); + cmd_argv[j++] = 0; + assert (j == nr_args); + /*FALLTHROUGH*/ + } + case trans_ext: + priv.sock = RPC_ANYSOCK; + priv.cl = + clntext_create (command, cmd_argv, + LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1, + &priv.sock, 0, 0); + if (!priv.cl) { + error (conn, VIR_ERR_RPC, + clnt_spcreateerror ("could not create SunRPC client")); + goto failed; + } + } + + // Send name (make the actual driver open RPC). + remote_open_ret *ret = remote_open_1 (&name, priv.cl); + if (!ret) { + error (conn, VIR_ERR_RPC, + clnt_sperror (priv.cl, "remote_open")); + clnt_destroy (priv.cl); + goto failed; + } + if (ret->status == -1) { + server_error (conn, &ret->remote_open_ret_u.err); + goto failed; + } + clnt_freeres (priv.cl, (xdrproc_t) xdr_remote_open_ret, (char *) ret); + + conn->privateData = malloc (sizeof priv); + if (!conn->privateData) { + error (conn, VIR_ERR_NO_MEMORY, "malloc"); + clnt_destroy (priv.cl); + goto failed; + } + priv.magic = MAGIC; + memcpy (conn->privateData, &priv, sizeof priv); + + /* Successful. */ + retcode = VIR_DRV_OPEN_SUCCESS; + /*FALLTHROUGH*/ + + /* Free up the URL and strings. */ + failed: + xmlFreeURI (uri); + if (name) free (name); + if (command) free (command); + if (sockname) free (sockname); + if (netcat) free (netcat); + if (username) free (username); + if (server) free (server); + if (port) free (port); + if (cmd_argv) { + char **cmd_argv_ptr = cmd_argv; + while (*cmd_argv_ptr) { + free (*cmd_argv_ptr); + cmd_argv_ptr++; + } + free (cmd_argv); + } + return retcode; +} + +/* In a string "driver+transport" return a pointer to "transport". */ +static char * +get_transport_from_scheme (char *scheme) +{ + char *p = strchr (scheme, '+'); + return p ? p+1 : 0; +} + +/* Split a query string ("var=variable&var=variable&..."). */ +static struct query_var ** +get_next_var (virConnectPtr conn, + const char *query, int len, struct query_var **vars, int *n_r) +{ + char *str = strndup (query, len); + if (!str) { + error (conn, VIR_ERR_NO_MEMORY, "get_next_var"); + free_query_vars (vars, *n_r); return 0; + } + char *p = strchr (str, '='); + if (!p) { + error (conn, VIR_ERR_INVALID_ARG, + "get_next_var: expecting variable=value in query string"); + free_query_vars (vars, *n_r); return 0; + } + + struct query_var *var = malloc (sizeof (struct query_var)); + if (!var) { + error (conn, VIR_ERR_NO_MEMORY, "get_next_var"); + free (p); free_query_vars (vars, *n_r); return 0; + } + *p = '\0'; + var->name = str; + var->value = p+1; + var->ignore = 0; + (*n_r)++; + struct query_var **new_vars; + new_vars = realloc (vars, *n_r * sizeof (struct query_var *)); + if (!new_vars) { + error (conn, VIR_ERR_NO_MEMORY, "get_next_var"); + free (p); free (var); + free_query_vars (vars, *n_r-1); + return 0; + } + vars = new_vars; + vars[*n_r-1] = var; + return vars; +} + +static struct query_var ** +query_split (virConnectPtr conn, const char *query, int *n_r) +{ + *n_r = 0; + if (!query || query[0] == '\0') return 0; + + struct query_var **vars = 0; + char *p; + while ((p = strchr (query, '&'))) { + vars = get_next_var (conn, query, p-query, vars, n_r); + if (!vars) { *n_r = -1; return 0; } + query = p+1; + } + vars = get_next_var (conn, query, strlen (query), vars, n_r); + if (!vars) { *n_r = -1; return 0; } + return vars; +} + +static char * +query_join (virConnectPtr conn, struct query_var **vars, int *n_r) +{ + int i, len = 0; + + if (vars == 0) return 0; + + for (i = 0; i < *n_r; ++i) + if (!vars[i]->ignore) + len += strlen (vars[i]->name) + strlen (vars[i]->value) + 2; + + char *str = malloc (len); + if (!str) { + error (conn, VIR_ERR_NO_MEMORY, "query_join"); + free_query_vars (vars, *n_r); *n_r = -1; return 0; + } + + len = 0; + for (i = 0; i < *n_r; ++i) + if (!vars[i]->ignore) { + if (len > 0) { + strcpy (str+len, "&"); + len++; + } + strcpy (str+len, vars[i]->name); + len += strlen (vars[i]->name); + strcpy (str+len, "="); + len ++; + strcpy (str+len, vars[i]->value); + len += strlen (vars[i]->value); + } + + return str; +} + +static void +free_query_vars (struct query_var **vars, int n) +{ + int i; + for (i = 0; i < n; ++i) { + free (vars[i]->name); + free (vars[i]); + } + free (vars); +} + +static int +do_close (virConnectPtr conn) +{ + GET_PRIVATE (conn, -1); + + void *nothing = 0; + remote_close_ret *ret = remote_close_1 (¬hing, priv->cl); + if (!ret) { + error (conn, VIR_ERR_RPC, + clnt_sperror (priv->cl, "remote_close")); + failed: + clnt_freeres (priv->cl, + (xdrproc_t) xdr_remote_close_ret, (char *) ret); + return -1; + } + if (ret->status == -1) { + server_error (conn, &ret->remote_close_ret_u.err); + goto failed; + } + + clnt_freeres (priv->cl, (xdrproc_t) xdr_remote_close_ret, (char *) ret); + + // NB. clnt_destroy should close the socket (priv->sock) too. + clnt_destroy (priv->cl); + // Force errors if anyone tries to reuse the closed connection. + priv->magic = DEAD; + + return 0; +} + +/* Remote_open and remote_close functions above are the complex ones. The + * rest just shuffle arguments and pass them along to the remote libvirtd. + */ + +static const char * +do_type (virConnectPtr conn) +{ + GET_PRIVATE (conn, NULL); + + void *nothing = 0; + remote_type_ret *ret = remote_type_1 (¬hing, priv->cl); + if (!ret) { + error (conn, VIR_ERR_RPC, + clnt_sperror (priv->cl, "remote_type")); + failed: + clnt_freeres (priv->cl, + (xdrproc_t) xdr_remote_type_ret, (char *) ret); + return NULL; + } + if (ret->status == -1) { + server_error (conn, &ret->remote_type_ret_u.err); + goto failed; + } + + /* This function is supposed to return a statically allocated string. + * We get around this by saving strings. + * https://www.redhat.com/archives/libvir-list/2007-February/msg00096.html + */ + static struct strings { + struct strings *next; + char *string; + } *strings = 0; + struct strings *p; + for (p = strings; p; p = p->next) { + if (strcmp (p->string, ret->remote_type_ret_u.type) == 0) + break; + } + if (!p) { + p = malloc (sizeof (struct strings)); + char *s = strdup (ret->remote_type_ret_u.type); + if (!p || !s) { + error (conn, VIR_ERR_NO_MEMORY, "remote_type"); + goto failed; + } + p->string = s; + p->next = strings; + strings = p; + } + + clnt_freeres (priv->cl, + (xdrproc_t) xdr_remote_type_ret, (char *) ret); + + return p->string; +} + +static int +do_version (virConnectPtr conn, unsigned long *hvVer) +{ + GET_PRIVATE (conn, -1); + + void *nothing = 0; + remote_version_ret *ret = remote_version_1 (¬hing, priv->cl); + if (!ret) { + error (conn, VIR_ERR_RPC, + clnt_sperror (priv->cl, "remote_version")); + failed: + clnt_freeres (priv->cl, + (xdrproc_t) xdr_remote_version_ret, (char *) ret); + return -1; + } + if (ret->status == -1) { + server_error (conn, &ret->remote_version_ret_u.err); + goto failed; + } + + *hvVer = ret->remote_version_ret_u.hvVer; + + clnt_freeres (priv->cl, + (xdrproc_t) xdr_remote_version_ret, (char *) ret); + return 0; +} + +static inline int +min (int a, int b) +{ + if (a < b) return a; else return b; +} + +static int +do_nodeGetInfo (virConnectPtr conn, virNodeInfo *info) +{ + GET_PRIVATE (conn, -1); + + void *nothing = 0; + remote_nodeGetInfo_ret *ret = + remote_nodegetinfo_1 (¬hing, priv->cl); + if (!ret) { + error (conn, VIR_ERR_RPC, + clnt_sperror (priv->cl, "remote_nodeGetInfo")); + failed: + clnt_freeres (priv->cl, + (xdrproc_t) xdr_remote_nodeGetInfo_ret, (char *) ret); + return -1; + } + if (ret->status == -1) { + server_error (conn, &ret->remote_nodeGetInfo_ret_u.err); + goto failed; + } + + strncpy (info->model, ret->remote_nodeGetInfo_ret_u.info.model, + sizeof info->model); + info->memory = ret->remote_nodeGetInfo_ret_u.info.memory; + info->cpus = ret->remote_nodeGetInfo_ret_u.info.cpus; + info->mhz = ret->remote_nodeGetInfo_ret_u.info.mhz; + info->nodes = ret->remote_nodeGetInfo_ret_u.info.nodes; + info->sockets = ret->remote_nodeGetInfo_ret_u.info.sockets; + info->cores = ret->remote_nodeGetInfo_ret_u.info.cores; + info->threads = ret->remote_nodeGetInfo_ret_u.info.threads; + + clnt_freeres (priv->cl, + (xdrproc_t) xdr_remote_nodeGetInfo_ret, (char *) ret); + + return 0; +} + +static int +do_listDomains (virConnectPtr conn, int *ids, int maxids) +{ + GET_PRIVATE (conn, -1); + + remote_listDomains_ret *ret = remote_listdomains_1 (&maxids, priv->cl); + if (!ret) { + error (conn, VIR_ERR_RPC, + clnt_sperror (priv->cl, "remote_listDomains")); + failed: + clnt_freeres (priv->cl, + (xdrproc_t) xdr_remote_listDomains_ret, (char *) ret); + return -1; + } + if (ret->status == -1) { + server_error (conn, &ret->remote_listDomains_ret_u.err); + goto failed; + } + + int len = min (ret->remote_listDomains_ret_u.ids.ids_len, maxids); + int i; + for (i = 0; i < len; ++i) + ids[i] = ret->remote_listDomains_ret_u.ids.ids_val[i]; + + clnt_freeres (priv->cl, + (xdrproc_t) xdr_remote_listDomains_ret, (char *) ret); + + return len; +} + +static int +do_numOfDomains (virConnectPtr conn) +{ + GET_PRIVATE (conn, -1); + + void *nothing = 0; + remote_numOfDomains_ret *ret = + remote_numofdomains_1 (¬hing, priv->cl); + if (!ret) { + error (conn, VIR_ERR_RPC, + clnt_sperror (priv->cl, "remote_numOfDomains")); + failed: + clnt_freeres (priv->cl, + (xdrproc_t) xdr_remote_numOfDomains_ret, (char *) ret); + return -1; + } + if (ret->status == -1) { + server_error (conn, &ret->remote_numOfDomains_ret_u.err); + goto failed; + } + + int nr_domains = ret->remote_numOfDomains_ret_u.nr_domains; + + clnt_freeres (priv->cl, + (xdrproc_t) xdr_remote_numOfDomains_ret, (char *) ret); + + return nr_domains; +} + +static virDomainPtr +do_domainCreateLinux (virConnectPtr conn, + const char *xmlDesc, unsigned int flags) +{ + GET_PRIVATE (conn, NULL); + + remote_domainCreateLinux_args args = { + .xmlDesc = (char *) xmlDesc, + .flags = flags + }; + remote_domainCreateLinux_ret *ret = + remote_domaincreatelinux_1 (&args, priv->cl); + if (!ret) { + error (conn, VIR_ERR_RPC, + clnt_sperror (priv->cl, "remote_domainCreateLinux")); + failed: + clnt_freeres (priv->cl, + (xdrproc_t) xdr_remote_domainCreateLinux_ret, + (char *) ret); + return NULL; + } + if (ret->status == -1) { + server_error (conn, &ret->remote_domainCreateLinux_ret_u.err); + goto failed; + } + + virDomainPtr dom = + get_domain_from_name (conn, + ret->remote_domainCreateLinux_ret_u.domain); + + clnt_freeres (priv->cl, + (xdrproc_t) xdr_remote_domainCreateLinux_ret, (char *) ret); + + return dom; +} + + + + + + + +/* For errors internal to this library. */ +static void +error (virConnectPtr conn, virErrorNumber code, const char *info) +{ + const char *errmsg; + + // XXX I don't think this is correct use of virErrorMsg. + errmsg = __virErrorMsg (code, info); + __virRaiseError (conn, NULL, NULL, VIR_FROM_REMOTE, + code, VIR_ERR_ERROR, errmsg, info, NULL, 0, 0, + errmsg, info); +} + +/* For errors generated on the server side and sent back to us. */ +static char *server_error_strdup (char *); + +static void +server_error (virConnectPtr conn, remote_error *err) +{ + virDomainPtr dom = get_domain_from_name (conn, err->dom); + virNetworkPtr net = get_network_from_name (conn, err->net); + + char *str1 = server_error_strdup (err->str1); + char *str2 = server_error_strdup (err->str2); + char *str3 = server_error_strdup (err->str3); + + // XXX I don't think this is correct use of virErrorMsg. + const char *msg = __virErrorMsg (err->code, err->message); + + __virRaiseError (conn, dom, net, + err->domain, err->code, err->level, + str1, str2, str3, + err->int1, err->int2, + msg); +} + +static char * +server_error_strdup (char *str) +{ + if (!str || strcmp (str, "") == 0) return NULL; + + // Getting NULL back from strdup is OK since these strings + // are informational. + return strdup (str); +} + +/* Domains and networks are represented on the wire by an + * optional (name, uuid) pair. If omitted, it stands for NULL. + */ +static virDomainPtr +get_domain_from_name (virConnectPtr conn, remote_domain domain) +{ + if (!conn || !domain) return NULL; + return virGetDomain (conn, domain->name, BAD_CAST domain->uuid); +} + +static virNetworkPtr +get_network_from_name (virConnectPtr conn, remote_network network) +{ + if (!conn || !network) return NULL; + return virGetNetwork (conn, network->name, BAD_CAST network->uuid); +} + +static virDriver driver = { + .no = VIR_DRV_REMOTE, + .name = "remote", + .ver = LIBVIRTREMOTE_VERS1, + .open = do_open, + .close = do_close, + .type = do_type, + .version = do_version, + .nodeGetInfo = do_nodeGetInfo, +#if 0 + .getCapabilities = do_getCapabilities, +#endif + .listDomains = do_listDomains, + .numOfDomains = do_numOfDomains, + .domainCreateLinux = do_domainCreateLinux, +#if 0 + .domainLookupByID = do_domainLookupByID, + .domainLookupByUUID = do_domainLookupByUUID, + .domainLookupByName = do_domainLookupByName, + .domainSuspend = do_domainSuspend, + .domainResume = do_domainResume, + .domainShutdown = do_domainShutdown, + .domainReboot = do_domainReboot, + .domainDestroy = do_domainDestroy, + .domainGetOSType = do_domainGetOSType, + .domainGetMaxMemory = do_domainGetMaxMemory, + .domainSetMaxMemory = do_domainSetMaxMemory, + .domainSetMemory = do_domainSetMemory, + .domainGetInfo = do_domainGetInfo, + .domainSave = do_domainSave, + .domainRestore = do_domainRestore, + .domainCoreDump = do_domainCoreDump, + .domainSetVcpus = do_domainSetVcpus, + .domainPinVcpu = do_domainPinVcpu, + .domainGetVcpus = do_domainGetVcpus, + .domainDumpXML = do_domainDumpXML, + .listDefinedDomains = do_listDefinedDomains, + .numOfDefinedDomains = do_numOfDefinedDomains, + .domainCreate = do_domainCreate, + .domainDefineXML = do_domainDefineXML, + .domainUndefine = do_domainUndefine, + .domainAttachDevice = do_domainAttachDevice, + .domainDetachDevice = do_domainDetachDevice, +#endif +}; + +/* Register driver. */ +int +remoteRegister (void) +{ + return virRegisterDriver (&driver); +} + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/remote_internal.h libvirt-oldremote/src/remote_internal.h --- libvirt-cvs/src/remote_internal.h 1970-01-01 01:00:00.000000000 +0100 +++ libvirt-oldremote/src/remote_internal.h 2007-04-04 13:46:57.000000000 +0100 @@ -0,0 +1,44 @@ +/* + * remote_internal.h: driver to provide access to libvirtd running + * on a remote machine. + * + * Copyright (C) 2007 Red Hat, Inc. + * + * See COPYING.LIB for the license of this software. + * + * Richard Jones <rjones@xxxxxxxxxx> + */ + +#ifndef __VIR_REMOTE_INTERNAL_H__ +#define __VIR_REMOTE_INTERNAL_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +extern int remoteRegister (void); + +#define LIBVIRTD_TLS_PORT "16514" +#define LIBVIRTD_TCP_PORT "16509" +#define LIBVIRTD_UNIX_SOCKET (LOCAL_STATE_DIR "/run/libvirt/libvirtd-sock") +#define LIBVIRTD_CONFIGURATION_FILE (SYSCONFDIR "/libvirtd.conf") + +#ifdef __cplusplus +} +#endif + +#endif /* __VIR_REMOTE_INTERNAL_H__ */ + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/remote_rpc_fix.pl libvirt-oldremote/src/remote_rpc_fix.pl --- libvirt-cvs/src/remote_rpc_fix.pl 1970-01-01 01:00:00.000000000 +0100 +++ libvirt-oldremote/src/remote_rpc_fix.pl 2007-03-02 14:12:06.000000000 +0000 @@ -0,0 +1,19 @@ +# Fix remote_rpc.h (generated by rpcgen) so that it compiles +# with warnings turned on. +# +# This code is evil. Arguably better would be just to compile +# remote_rpc.h without -Werror. +# +# Copyright (C) 2007 Red Hat, Inc. +# +# See COPYING for the license of this software. +# +# Richard Jones <rjones@xxxxxxxxxx> + +while (<>) +{ + if (m/^struct remote_name \{/) { + print "extern void libvirtremote_1 (struct svc_req *rqstp, register SVCXPRT *transp);\n"; + } + print; +} diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/remote_rpc.x libvirt-oldremote/src/remote_rpc.x --- libvirt-cvs/src/remote_rpc.x 1970-01-01 01:00:00.000000000 +0100 +++ libvirt-oldremote/src/remote_rpc.x 2007-02-26 19:41:31.000000000 +0000 @@ -0,0 +1,180 @@ +/* -*- C -*- + * remote_rpc.x: Remote procedure call interface for the remote driver. + * Process this file with rpcgen to generate client and server stubs. + * + * Copyright (C) 2007 Red Hat, Inc. + * + * See COPYING.LIB for the license of this software. + * + * Richard Jones <rjones@xxxxxxxxxx> + */ + +/* XXX Get this from <libvirt.h>. */ +#define VIR_UUID_BUFLEN 16 + +/* Domains and networks are represented on the wire by (name, UUID). */ +struct remote_name { + string name<>; + opaque uuid[VIR_UUID_BUFLEN]; +}; +typedef struct remote_name *remote_domain; +typedef struct remote_name *remote_network; + +/* Error message. See <virterror.h> for explanation of fields. */ + +/* NB. Fields "code", "domain" and "level" are really enums. The + * numeric value should remain compatible between libvirt and + * libvirtd. This means, no changing or reordering the enums as + * defined in <virterror.h>. + */ +struct remote_error { + int code; + int domain; + string message<>; + int level; + remote_domain dom; + string str1<>; + string str2<>; + string str3<>; + int int1; + int int2; + remote_network net; +}; + +/* virNodeInfo equivalent. */ +struct remote_node_info { + string model<32>; + hyper memory; + int cpus; + int mhz; + int nodes; + int sockets; + int cores; + int threads; +}; + +/* Return values include a status and either the returned object or + * an error message. + */ +union remote_open_ret switch (int status) { + case 0: + void; + default: + struct remote_error err; +}; + +union remote_close_ret switch (int status) { + case 0: + void; + default: + struct remote_error err; +}; + +union remote_type_ret switch (int status) { + case 0: + string type<>; + default: + struct remote_error err; +}; + +union remote_version_ret switch (int status) { + case 0: + hyper hvVer; + default: + struct remote_error err; +}; + +union remote_nodeGetInfo_ret switch (int status) { + case 0: + struct remote_node_info info; + default: + struct remote_error err; +}; + +union remote_listDomains_ret switch (int status) { + case 0: + int ids<>; + default: + struct remote_error err; +}; + +union remote_numOfDomains_ret switch (int status) { + case 0: + int nr_domains; + default: + struct remote_error err; +}; + +struct remote_domainCreateLinux_args { + string xmlDesc<>; + int flags; +}; + +union remote_domainCreateLinux_ret switch (int status) { + case 0: + remote_domain domain; + default: + struct remote_error err; +}; + +program LIBVIRTREMOTE { + version LIBVIRTREMOTE_VERS1 { + remote_open_ret remote_open (string) = 1; + remote_close_ret remote_close (void) = 2; + remote_type_ret remote_type (void) = 3; + remote_version_ret remote_version (void) = 4; + remote_nodeGetInfo_ret remote_nodeGetInfo (void) = 5; + remote_listDomains_ret remote_listDomains (int) = 6; + remote_numOfDomains_ret remote_numOfDomains (void) = 7; + remote_domainCreateLinux_ret remote_domainCreateLinux + (struct remote_domainCreateLinux_args) = 8; +#if 0 + remote_domainLookupByID_ret remote_domainLookupByID () = 9; + remote_domainLookupByUUID_ret remote_domainLookupByUUID () = 10; + remote_domainLookupByName_ret remote_domainLookupByName () = 11; + remote_domainSuspend_ret remote_domainSuspend () = 12; + remote_domainResume_ret remote_domainResume () = 13; + remote_domainShutdown_ret remote_domainShutdown () = 14; + remote_domainReboot_ret remote_domainReboot () = 15; + remote_domainDestroy_ret remote_domainDestroy () = 16; + remote_domainGetOSType_ret remote_domainGetOSType () = 17; + remote_domainGetMaxMemory_ret remote_domainGetMaxMemory () = 18; + remote_domainSetMaxMemory_ret remote_domainSetMaxMemory () = 19; + remote_domainSetMemory_ret remote_domainSetMemory () = 20; + remote_domainGetInfo_ret remote_domainGetInfo () = 21; + remote_domainSave_ret remote_domainSave () = 22; + remote_domainRestore_ret remote_domainRestore () = 23; + remote_domainCoreDump_ret remote_domainCoreDump () = 24; + remote_domainSetVcpus_ret remote_domainSetVcpus () = 25; + remote_domainPinVcpu_ret remote_domainPinVcpu () = 26; + remote_domainGetVcpus_ret remote_domainGetVcpus () = 27; + remote_domainDumpXML_ret remote_domainDumpXML () = 28; + remote_listDefinedDomains_ret remote_listDefinedDomains () = 29; + remote_numOfDefinedDomains_ret remote_numOfDefinedDomains () = 30; + remote_domainCreate_ret remote_domainCreate () = 31; + remote_domainDefineXML_ret remote_domainDefineXML () = 32; + remote_domainUndefine_ret remote_domainUndefine () = 33; + remote_domainAttachDevice_ret remote_domainAttachDevice () = 34; + remote_domainDetachDevice_ret remote_domainDetachDevice () = 35; +#endif + } = 1; + /* It doesn't really matter what program number we choose here because + * there will only ever be one "program" listening on the assigned + * TCP port number. Nevertheless, choose one from the Sun private + * space. + */ +} = 0x20008080; + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/remote_rpc_xdr_fix.pl libvirt-oldremote/src/remote_rpc_xdr_fix.pl --- libvirt-cvs/src/remote_rpc_xdr_fix.pl 1970-01-01 01:00:00.000000000 +0100 +++ libvirt-oldremote/src/remote_rpc_xdr_fix.pl 2007-03-02 12:25:53.000000000 +0000 @@ -0,0 +1,64 @@ +# Fix remote_rpc_xdr.c (generated by rpcgen) so that it compiles +# with warnings turned on. +# +# This code is evil. Arguably better would be just to compile +# remote_rpc_xdr.c without -Werror. +# +# Copyright (C) 2007 Red Hat, Inc. +# +# See COPYING for the license of this software. +# +# Richard Jones <rjones@xxxxxxxxxx> + +use strict; + +my $in_function = 0; +my @function = (); + +while (<>) { + if (m/^{/) { + $in_function = 1; + print; + next; + } + + if (m/^}/) { + $in_function = 0; + + # Note: The body of the function is in @function. + + # Remove decl of buf, if buf isn't used in the function. + my @uses = grep /\bbuf\b/, @function; + @function = grep !/\bbuf\b/, @function if @uses == 1; + + # Remove decl of i, if i isn't used in the function. + @uses = grep /\bi\b/, @function; + @function = grep !/\bi\b/, @function if @uses == 1; + + # (char **)&objp->... gives: + # warning: dereferencing type-punned pointer will break + # strict-aliasing rules + # so rewrite it. + @uses = grep /\(char \*\*\)\&objp->remote_listDomains_ret_u\.ids\.ids_val/, @function; + if (@uses >= 1) { + unshift @function, + ("\tchar *objp_cp = (char *) objp->remote_listDomains_ret_u.ids.ids_val;\n", + "\tchar **objp_cpp = &objp_cp;\n"); + @function = + map { s{\(char \*\*\)\&objp->remote_listDomains_ret_u\.ids\.ids_val}{objp_cpp}g; $_ } @function; + } + + # The code uses 'IXDR_PUT_LONG' but in a way where the statements + # cannot have any effect (it's not really clear why). Remove them. + @function = grep !/\bIXDR_PUT_LONG\b/, @function; + + print (join ("", @function)); + @function = (); + } + + unless ($in_function) { + print; + } else { + push @function, $_; + } +} diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/clnt_ext.c libvirt-oldremote/src/sunrpc/clnt_ext.c --- libvirt-cvs/src/sunrpc/clnt_ext.c 1970-01-01 01:00:00.000000000 +0100 +++ libvirt-oldremote/src/sunrpc/clnt_ext.c 2007-04-04 13:49:38.000000000 +0100 @@ -0,0 +1,644 @@ +/* + * clnt_ext.c: SunRPC over an external program. This is a modified version + * of clnt_unix.c from glibc which is written to run over a forked + * external program. + * + * Note that there is no corresponding svc_ext.c. It is expected that + * this client will talk to a remote Unix domain socket (ie. svc_unix.c). + * + * Modifications from glibc-2.5 base by Richard Jones <rjones@xxxxxxxxxx>. + */ + +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * clnt_unix.c, Implements a TCP/IP based, client side RPC. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * TCP based RPC supports 'batched calls'. + * A sequence of calls may be batched-up in a send buffer. The rpc call + * return immediately to the client even though the call was not necessarily + * sent. The batching occurs if the results' xdr routine is NULL (0) AND + * the rpc timeout value is zero (see clnt.h, rpc). + * + * Clients should NOT casually batch calls that in fact return results; that is, + * the server side should be aware that a call is batched and not produce any + * return message. Batched calls that produce many result messages can + * deadlock (netlock) the client and the server.... + * + * Now go hang yourself. + */ + +#include <netdb.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <libintl.h> +#include <rpc/rpc.h> +#include <sys/uio.h> +#include <sys/poll.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <rpc/pmap_clnt.h> +#ifdef USE_IN_LIBIO +# include <wchar.h> +#endif + +#include "clnt_ext.h" +#include "create_xid.h" + +#define MCALL_MSG_SIZE 24 + +struct ct_data + { + int ct_sock; + bool_t ct_closeit; + struct timeval ct_wait; + bool_t ct_waitset; /* wait set by clnt_control? */ + struct rpc_err ct_error; + char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg */ + u_int ct_mpos; /* pos after marshal */ + XDR ct_xdrs; + int ct_pid; /* Child PID. */ + }; + +static int readunix (char *, char *, int); +static int writeunix (char *, char *, int); + +static enum clnt_stat clntunix_call (CLIENT *, u_long, xdrproc_t, caddr_t, + xdrproc_t, caddr_t, struct timeval); +static void clntunix_abort (void); +static void clntunix_geterr (CLIENT *, struct rpc_err *); +static bool_t clntunix_freeres (CLIENT *, xdrproc_t, caddr_t); +static bool_t clntunix_control (CLIENT *, int, char *); +static void clntunix_destroy (CLIENT *); + +static const struct clnt_ops unix_ops = +{ + clntunix_call, + clntunix_abort, + clntunix_geterr, + clntunix_freeres, + clntunix_destroy, + clntunix_control +}; + +CLIENT * +clntext_create (char *filename, char *argv[], + u_long prog, u_long vers, + int *sockp, u_int sendsz, u_int recvsz) +{ + CLIENT *h; + struct ct_data *ct = (struct ct_data *) mem_alloc (sizeof (*ct)); + struct rpc_msg call_msg; + int sv[2]; + + h = (CLIENT *) mem_alloc (sizeof (*h)); + if (h == NULL || ct == NULL) + { + struct rpc_createerr *ce = &get_rpc_createerr (); + (void) fprintf (stderr, "clntext_create: out of memory\n"); + ce->cf_stat = RPC_SYSTEMERROR; + ce->cf_error.re_errno = ENOMEM; + goto fooy; + } + + /* Fork off the external process. Use socketpair to create a private + * (unnamed) Unix domain socket to the child process so we don't have + * to faff around with two file descriptors (a la 'pipe(2)'). + */ + if (*sockp < 0) + { + if (socketpair (PF_UNIX, SOCK_STREAM, 0, sv) == -1) + { + struct rpc_createerr *ce = &get_rpc_createerr (); + (void) fprintf (stderr, "clntext_create: socketpair\n"); + ce->cf_stat = RPC_SYSTEMERROR; + ce->cf_error.re_errno = errno; + goto fooy; + } + + ct->ct_pid = fork (); + if (ct->ct_pid == -1) { + struct rpc_createerr *ce = &get_rpc_createerr (); + (void) fprintf (stderr, "clntext_create: fork\n"); + ce->cf_stat = RPC_SYSTEMERROR; + ce->cf_error.re_errno = errno; + goto fooy; + } else if (ct->ct_pid == 0) { /* Child. */ + close (sv[0]); + // Connect socket (sv[1]) to stdin/stdout. + close (0); + if (dup (sv[1]) == -1) perror ("dup"); + close (1); + if (dup (sv[1]) == -1) perror ("dup"); + close (sv[1]); + + // Run the external process. + + if (!argv) { + argv = malloc (2 * sizeof (char *)); + argv[0] = filename; + argv[1] = 0; + } + execvp (filename, argv); + perror (filename); + _exit (1); + } + + /* Parent continues here. */ + close (sv[1]); + *sockp = sv[0]; + ct->ct_closeit = TRUE; + } + else + { + ct->ct_closeit = FALSE; + } + + /* + * Set up private data struct + */ + ct->ct_sock = *sockp; + ct->ct_wait.tv_usec = 0; + ct->ct_waitset = FALSE; + + /* + * Initialize call message + */ + call_msg.rm_xid = _create_xid (); + call_msg.rm_direction = CALL; + call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; + call_msg.rm_call.cb_prog = prog; + call_msg.rm_call.cb_vers = vers; + + /* + * pre-serialize the static part of the call msg and stash it away + */ + xdrmem_create (&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE, + XDR_ENCODE); + if (!xdr_callhdr (&(ct->ct_xdrs), &call_msg)) + { + if (ct->ct_closeit) + close (*sockp); + goto fooy; + } + ct->ct_mpos = XDR_GETPOS (&(ct->ct_xdrs)); + XDR_DESTROY (&(ct->ct_xdrs)); + + /* + * Create a client handle which uses xdrrec for serialization + * and authnone for authentication. + */ + xdrrec_create (&(ct->ct_xdrs), sendsz, recvsz, + (caddr_t) ct, readunix, writeunix); + h->cl_ops = (struct clnt_ops *) &unix_ops; + h->cl_private = (caddr_t) ct; + h->cl_auth = authnone_create (); + return h; + +fooy: + /* + * Something goofed, free stuff and barf + */ + mem_free ((caddr_t) ct, sizeof (struct ct_data)); + mem_free ((caddr_t) h, sizeof (CLIENT)); + return (CLIENT *) NULL; +} + +static enum clnt_stat +clntunix_call (h, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout) + CLIENT *h; + u_long proc; + xdrproc_t xdr_args; + caddr_t args_ptr; + xdrproc_t xdr_results; + caddr_t results_ptr; + struct timeval timeout; +{ + struct ct_data *ct = (struct ct_data *) h->cl_private; + XDR *xdrs = &(ct->ct_xdrs); + struct rpc_msg reply_msg; + u_long x_id; + u_int32_t *msg_x_id = (u_int32_t *) (ct->ct_mcall); /* yuk */ + bool_t shipnow; + int refreshes = 2; + + if (!ct->ct_waitset) + { + ct->ct_wait = timeout; + } + + shipnow = + (xdr_results == (xdrproc_t) 0 && ct->ct_wait.tv_sec == 0 + && ct->ct_wait.tv_usec == 0) ? FALSE : TRUE; + +call_again: + xdrs->x_op = XDR_ENCODE; + ct->ct_error.re_status = RPC_SUCCESS; + x_id = ntohl (--(*msg_x_id)); + if ((!XDR_PUTBYTES (xdrs, ct->ct_mcall, ct->ct_mpos)) || + (!XDR_PUTLONG (xdrs, (long *) &proc)) || + (!AUTH_MARSHALL (h->cl_auth, xdrs)) || + (!(*xdr_args) (xdrs, args_ptr))) + { + if (ct->ct_error.re_status == RPC_SUCCESS) + ct->ct_error.re_status = RPC_CANTENCODEARGS; + (void) xdrrec_endofrecord (xdrs, TRUE); + return ct->ct_error.re_status; + } + if (!xdrrec_endofrecord (xdrs, shipnow)) + return ct->ct_error.re_status = RPC_CANTSEND; + if (!shipnow) + return RPC_SUCCESS; + /* + * Hack to provide rpc-based message passing + */ + if (ct->ct_wait.tv_sec == 0 && ct->ct_wait.tv_usec == 0) + return ct->ct_error.re_status = RPC_TIMEDOUT; + + + /* + * Keep receiving until we get a valid transaction id + */ + xdrs->x_op = XDR_DECODE; + while (TRUE) + { + reply_msg.acpted_rply.ar_verf = _null_auth; + reply_msg.acpted_rply.ar_results.where = NULL; + reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void; + if (!xdrrec_skiprecord (xdrs)) + return ct->ct_error.re_status; + /* now decode and validate the response header */ + if (!xdr_replymsg (xdrs, &reply_msg)) + { + if (ct->ct_error.re_status == RPC_SUCCESS) + continue; + return ct->ct_error.re_status; + } + if (reply_msg.rm_xid == x_id) + break; + } + + /* + * process header + */ + _seterr_reply (&reply_msg, &(ct->ct_error)); + if (ct->ct_error.re_status == RPC_SUCCESS) + { + if (!AUTH_VALIDATE (h->cl_auth, &reply_msg.acpted_rply.ar_verf)) + { + ct->ct_error.re_status = RPC_AUTHERROR; + ct->ct_error.re_why = AUTH_INVALIDRESP; + } + else if (!(*xdr_results) (xdrs, results_ptr)) + { + if (ct->ct_error.re_status == RPC_SUCCESS) + ct->ct_error.re_status = RPC_CANTDECODERES; + } + /* free verifier ... */ + if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) + { + xdrs->x_op = XDR_FREE; + (void) xdr_opaque_auth (xdrs, + &(reply_msg.acpted_rply.ar_verf)); + } + } /* end successful completion */ + else + { + /* maybe our credentials need to be refreshed ... */ + if (refreshes-- && AUTH_REFRESH (h->cl_auth)) + goto call_again; + } /* end of unsuccessful completion */ + return ct->ct_error.re_status; +} + +static void +clntunix_geterr (CLIENT *h, struct rpc_err *errp) +{ + struct ct_data *ct = (struct ct_data *) h->cl_private; + + *errp = ct->ct_error; +} + +static bool_t +clntunix_freeres (cl, xdr_res, res_ptr) + CLIENT *cl; + xdrproc_t xdr_res; + caddr_t res_ptr; +{ + struct ct_data *ct = (struct ct_data *) cl->cl_private; + XDR *xdrs = &(ct->ct_xdrs); + + xdrs->x_op = XDR_FREE; + return (*xdr_res) (xdrs, res_ptr); +} + +static void +clntunix_abort () +{ +} + +static bool_t +clntunix_control (CLIENT *cl, int request, char *info) +{ + struct ct_data *ct = (struct ct_data *) cl->cl_private; + + + switch (request) + { + case CLSET_FD_CLOSE: + ct->ct_closeit = TRUE; + break; + case CLSET_FD_NCLOSE: + ct->ct_closeit = FALSE; + break; + case CLSET_TIMEOUT: + ct->ct_wait = *(struct timeval *) info; + break; + case CLGET_TIMEOUT: + *(struct timeval *) info = ct->ct_wait; + break; + case CLGET_FD: + *(int *)info = ct->ct_sock; + break; + case CLGET_XID: + /* + * use the knowledge that xid is the + * first element in the call structure *. + * This will get the xid of the PREVIOUS call + */ + *(u_long *) info = ntohl (*(u_long *)ct->ct_mcall); + break; + case CLSET_XID: + /* This will set the xid of the NEXT call */ + *(u_long *) ct->ct_mcall = htonl (*(u_long *)info - 1); + /* decrement by 1 as clntunix_call() increments once */ + case CLGET_VERS: + /* + * This RELIES on the information that, in the call body, + * the version number field is the fifth field from the + * begining of the RPC header. MUST be changed if the + * call_struct is changed + */ + *(u_long *) info = ntohl (*(u_long *) (ct->ct_mcall + + 4 * BYTES_PER_XDR_UNIT)); + break; + case CLSET_VERS: + *(u_long *) (ct->ct_mcall + 4 * BYTES_PER_XDR_UNIT) + = htonl (*(u_long *) info); + break; + case CLGET_PROG: + /* + * This RELIES on the information that, in the call body, + * the program number field is the field from the + * begining of the RPC header. MUST be changed if the + * call_struct is changed + */ + *(u_long *) info = ntohl (*(u_long *) (ct->ct_mcall + + 3 * BYTES_PER_XDR_UNIT)); + break; + case CLSET_PROG: + *(u_long *) (ct->ct_mcall + 3 * BYTES_PER_XDR_UNIT) + = htonl(*(u_long *) info); + break; + /* The following are only possible with TI-RPC */ + case CLGET_RETRY_TIMEOUT: + case CLSET_RETRY_TIMEOUT: + case CLGET_SVC_ADDR: + case CLSET_SVC_ADDR: + case CLSET_PUSH_TIMOD: + case CLSET_POP_TIMOD: + default: + return FALSE; + } + return TRUE; +} + + +static void +clntunix_destroy (CLIENT *h) +{ + struct ct_data *ct = + (struct ct_data *) h->cl_private; + + if (ct->ct_closeit) + { + (void) close (ct->ct_sock); + } + XDR_DESTROY (&(ct->ct_xdrs)); + mem_free ((caddr_t) ct, sizeof (struct ct_data)); + mem_free ((caddr_t) h, sizeof (CLIENT)); + + // Wait for child to finish. Print any errors but don't stop. + int status; + if (waitpid (ct->ct_pid, &status, 0) == -1) { + perror ("waitpid"); + return; + } + if (WIFEXITED (status)) { + int s = WEXITSTATUS (status); + if (s != 0) + fprintf (stderr, "clntext_destroy: warning: external command exited with non-zero exit status %d\n", s); + } else if (WIFSIGNALED (status)) { + int s = WTERMSIG (status); + fprintf (stderr, "clntext_destroy: warning: external command died on signal %d\n", s); + } else if (WIFSTOPPED (status)) { + int s = WSTOPSIG (status); + fprintf (stderr, "clntext_destroy: warning: external command stopped on signal %d\n", s); + } +} + +static int +__msgread (int sock, void *data, size_t cnt) +{ + struct iovec iov; + struct msghdr msg; +#ifdef SCM_CREDENTIALS + static char cm[CMSG_SPACE(sizeof (struct ucred))]; +#endif + int len; + + iov.iov_base = data; + iov.iov_len = cnt; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_name = NULL; + msg.msg_namelen = 0; +#ifdef SCM_CREDENTIALS + msg.msg_control = (caddr_t) &cm; + msg.msg_controllen = CMSG_SPACE(sizeof (struct ucred)); +#endif + msg.msg_flags = 0; + +#ifdef SO_PASSCRED + { + int on = 1; + if (setsockopt (sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof (on))) + return -1; + } +#endif + + restart: + len = recvmsg (sock, &msg, 0); + if (len >= 0) + { + if (msg.msg_flags & MSG_CTRUNC || len == 0) + return 0; + else + return len; + } + if (errno == EINTR) + goto restart; + return -1; +} + +static int +__msgwrite (int sock, void *data, size_t cnt) +{ +#ifndef SCM_CREDENTIALS + /* We cannot implement this reliably. */ + __set_errno (ENOSYS); + return -1; +#else + struct iovec iov; + struct msghdr msg; + struct cmsghdr *cmsg = alloca (CMSG_SPACE(sizeof (struct ucred))); + struct ucred cred; + int len; + + /* XXX I'm not sure, if gete?id() is always correct, or if we should use + get?id(). But since keyserv needs geteuid(), we have no other chance. + It would be much better, if the kernel could pass both to the server. */ + cred.pid = getpid (); + cred.uid = geteuid (); + cred.gid = getegid (); + + memcpy (CMSG_DATA(cmsg), &cred, sizeof (struct ucred)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDENTIALS; + cmsg->cmsg_len = sizeof(*cmsg) + sizeof(struct ucred); + + iov.iov_base = data; + iov.iov_len = cnt; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_control = cmsg; + msg.msg_controllen = CMSG_ALIGN(cmsg->cmsg_len); + msg.msg_flags = 0; + + restart: + len = sendmsg (sock, &msg, 0); + if (len >= 0) + return len; + if (errno == EINTR) + goto restart; + return -1; + +#endif +} + + +/* + * Interface between xdr serializer and unix connection. + * Behaves like the system calls, read & write, but keeps some error state + * around for the rpc level. + */ +static int +readunix (char *ctptr, char *buf, int len) +{ + struct ct_data *ct = (struct ct_data *) ctptr; + struct pollfd fd; + int milliseconds = ((ct->ct_wait.tv_sec * 1000) + + (ct->ct_wait.tv_usec / 1000)); + + if (len == 0) + return 0; + + fd.fd = ct->ct_sock; + fd.events = POLLIN; + while (TRUE) + { + switch (poll (&fd, 1, milliseconds)) + { + case 0: + ct->ct_error.re_status = RPC_TIMEDOUT; + return -1; + + case -1: + if (errno == EINTR) + continue; + ct->ct_error.re_status = RPC_CANTRECV; + ct->ct_error.re_errno = errno; + return -1; + } + break; + } + switch (len = __msgread (ct->ct_sock, buf, len)) + { + + case 0: + /* premature eof */ + ct->ct_error.re_errno = ECONNRESET; + ct->ct_error.re_status = RPC_CANTRECV; + len = -1; /* it's really an error */ + break; + + case -1: + ct->ct_error.re_errno = errno; + ct->ct_error.re_status = RPC_CANTRECV; + break; + } + return len; +} + +static int +writeunix (char *ctptr, char *buf, int len) +{ + int i, cnt; + struct ct_data *ct = (struct ct_data *) ctptr; + + for (cnt = len; cnt > 0; cnt -= i, buf += i) + { + if ((i = __msgwrite (ct->ct_sock, buf, cnt)) == -1) + { + ct->ct_error.re_errno = errno; + ct->ct_error.re_status = RPC_CANTSEND; + return -1; + } + } + return len; +} diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/clnt_ext.h libvirt-oldremote/src/sunrpc/clnt_ext.h --- libvirt-cvs/src/sunrpc/clnt_ext.h 1970-01-01 01:00:00.000000000 +0100 +++ libvirt-oldremote/src/sunrpc/clnt_ext.h 2007-02-23 15:59:36.000000000 +0000 @@ -0,0 +1,18 @@ +/* + * clnt_ext.h: Interface to the SunRPC over external program. + * + * Copyright (C) 2007 Red Hat, Inc. + * + * See COPYING.LIB for the license of this software. + * + * Richard Jones <rjones@xxxxxxxxxx> + */ + +#ifndef __CLNT_EXT_H__ +#define __CLNT_EXT_H__ + +extern CLIENT *clntext_create (char *filename, char *argv[], + u_long prog, u_long vers, + int *sockp, u_int sendsz, u_int recvsz); + +#endif /* __CLNT_EXT_H__ */ diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/clnt_gnutls.c libvirt-oldremote/src/sunrpc/clnt_gnutls.c --- libvirt-cvs/src/sunrpc/clnt_gnutls.c 1970-01-01 01:00:00.000000000 +0100 +++ libvirt-oldremote/src/sunrpc/clnt_gnutls.c 2007-03-02 11:38:06.000000000 +0000 @@ -0,0 +1,717 @@ +/* + * clnt_gnutls.c: SunRPC over GnuTLS client. This is a modified version + * of clnt_tcp.c from glibc which is written to run over GnuTLS. + * + * Modifications from glibc-2.5 base by Richard Jones <rjones@xxxxxxxxxx>. + */ + +/* @(#)clnt_tcp.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro"; +#endif + +/* + * clnt_tcp.c, Implements a TCP/IP based, client side RPC. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * TCP based RPC supports 'batched calls'. + * A sequence of calls may be batched-up in a send buffer. The rpc call + * return immediately to the client even though the call was not necessarily + * sent. The batching occurs if the results' xdr routine is NULL (0) AND + * the rpc timeout value is zero (see clnt.h, rpc). + * + * Clients should NOT casually batch calls that in fact return results; that is, + * the server side should be aware that a call is batched and not produce any + * return message. Batched calls that produce many result messages can + * deadlock (netlock) the client and the server.... + * + * Now go hang yourself. + */ + +#include <netdb.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <libintl.h> +#include <rpc/rpc.h> +#include <sys/poll.h> +#include <sys/socket.h> +#include <rpc/pmap_clnt.h> +#ifdef USE_IN_LIBIO +# include <wchar.h> +#endif +#include <gnutls/gnutls.h> +#include <gnutls/x509.h> + +#include "clnt_gnutls.h" +#include "create_xid.h" + +#define MCALL_MSG_SIZE 24 + +struct ct_data + { + int ct_sock; + bool_t ct_closeit; + struct timeval ct_wait; + bool_t ct_waitset; /* wait set by clnt_control? */ + struct rpc_err ct_error; + char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg */ + u_int ct_mpos; /* pos after marshal */ + XDR ct_xdrs; + gnutls_session_t ct_session; /* GnuTLS session. */ + }; + +static int readtcp (char *, char *, int); +static int writetcp (char *, char *, int); + +static enum clnt_stat clnttcp_call (CLIENT *, u_long, xdrproc_t, caddr_t, + xdrproc_t, caddr_t, struct timeval); +static void clnttcp_abort (void); +static void clnttcp_geterr (CLIENT *, struct rpc_err *); +static bool_t clnttcp_freeres (CLIENT *, xdrproc_t, caddr_t); +static bool_t clnttcp_control (CLIENT *, int, char *); +static void clnttcp_destroy (CLIENT *); + +static const struct clnt_ops tcp_ops = +{ + clnttcp_call, + clnttcp_abort, + clnttcp_geterr, + clnttcp_freeres, + clnttcp_destroy, + clnttcp_control +}; + +static int start_gnutls_session (gnutls_certificate_credentials_t xcred, + struct ct_data *ct, int sock, + int no_verify, const char *hostname); +/* + * Create a client handle for a tcp/ip connection. + * If *sockp<0, *sockp is set to a newly created TCP socket and it is + * connected to raddr. If *sockp non-negative then + * raddr is ignored. The rpc/tcp package does buffering + * similar to stdio, so the client must pick send and receive buffer sizes,]; + * 0 => use the default. + * If raddr->sin_port is 0, then a binder on the remote machine is + * consulted for the right port number. + * NB: *sockp is copied into a private area. + * NB: It is the clients responsibility to close *sockp. + * NB: The rpch->cl_auth is set null authentication. Caller may wish to set this + * something more useful. + */ +CLIENT * +clntgnutls_create (int pf, struct sockaddr *raddr, int raddr_size, + gnutls_certificate_credentials_t xcred, + int no_verify, const char *hostname, + u_long prog, u_long vers, + int *sockp, u_int sendsz, u_int recvsz) +{ + CLIENT *h; + struct ct_data *ct; + struct rpc_msg call_msg; + + h = (CLIENT *) mem_alloc (sizeof (*h)); + ct = (struct ct_data *) mem_alloc (sizeof (*ct)); + if (h == NULL || ct == NULL) + { + struct rpc_createerr *ce = &get_rpc_createerr (); + (void) fprintf (stderr, "clnttcp_create: out of memory\n"); + ce->cf_stat = RPC_SYSTEMERROR; + ce->cf_error.re_errno = ENOMEM; + goto fooy; + } + +#if 0 + // RWMJ: Note this will never be supported for portmap because the + // portmap protocol depends pretty fundamentally on IPv4. Don't + /// use portmapper anyway -- it's silly. + /* + * If no port number given ask the pmap for one + */ + if (port == 0) + { + u_short port; + if ((port = pmap_getport (raddr, prog, vers, IPPROTO_TCP)) == 0) + { + mem_free ((caddr_t) ct, sizeof (struct ct_data)); + mem_free ((caddr_t) h, sizeof (CLIENT)); + return ((CLIENT *) NULL); + } + raddr->sin_port = htons (port); + } +#endif + + /* + * If no socket given, open one + */ + if (*sockp < 0) + { + *sockp = socket (pf, SOCK_STREAM, 0); + // RWMJ: Why? + //(void) bindresvport (*sockp, (struct sockaddr_in *) 0); + if ((*sockp < 0) + || (connect (*sockp, raddr, raddr_size) < 0)) + { + struct rpc_createerr *ce = &get_rpc_createerr (); + ce->cf_stat = RPC_SYSTEMERROR; + ce->cf_error.re_errno = errno; + if (*sockp >= 0) + (void) close (*sockp); + goto fooy; + } + ct->ct_closeit = TRUE; + } + else + { + ct->ct_closeit = FALSE; + } + + /* + * Set up private data struct + */ + ct->ct_sock = *sockp; + ct->ct_wait.tv_usec = 0; + ct->ct_waitset = FALSE; + + /* + * Initialize call message + */ + call_msg.rm_xid = _create_xid (); + call_msg.rm_direction = CALL; + call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; + call_msg.rm_call.cb_prog = prog; + call_msg.rm_call.cb_vers = vers; + + /* + * pre-serialize the static part of the call msg and stash it away + */ + xdrmem_create (&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE, + XDR_ENCODE); + if (!xdr_callhdr (&(ct->ct_xdrs), &call_msg)) + { + if (ct->ct_closeit) + { + (void) close (*sockp); + } + goto fooy; + } + ct->ct_mpos = XDR_GETPOS (&(ct->ct_xdrs)); + XDR_DESTROY (&(ct->ct_xdrs)); + + /* + * Create a client handle which uses xdrrec for serialization + * and authnone for authentication. + */ + xdrrec_create (&(ct->ct_xdrs), sendsz, recvsz, + (caddr_t) ct, readtcp, writetcp); + h->cl_ops = (struct clnt_ops *) &tcp_ops; + h->cl_private = (caddr_t) ct; + h->cl_auth = authnone_create (); + + /* Start GnuTLS on this socket. */ + if (start_gnutls_session (xcred, ct, *sockp, no_verify, hostname) == -1) { + if (ct->ct_closeit) + close (*sockp); + goto fooy; + } + + return h; + +fooy: + /* + * Something goofed, free stuff and barf + */ + mem_free ((caddr_t) ct, sizeof (struct ct_data)); + mem_free ((caddr_t) h, sizeof (CLIENT)); + return ((CLIENT *) NULL); +} + +static int verify_certificate (gnutls_session_t session, const char *hostname); + +static int +start_gnutls_session (gnutls_certificate_credentials_t xcred, + struct ct_data *ct, int sock, + int no_verify, const char *hostname) +{ + const int cert_type_priority[3] = { + GNUTLS_CRT_X509, + GNUTLS_CRT_OPENPGP, + 0 + }; + int err; + + /* Initialize TLS session + */ + err = gnutls_init (&ct->ct_session, GNUTLS_CLIENT); + if (err) { gnutls_perror (err); return -1; } + + /* Use default priorities */ + err = gnutls_set_default_priority (ct->ct_session); + if (err) { gnutls_perror (err); return -1; } + err = + gnutls_certificate_type_set_priority (ct->ct_session, cert_type_priority); + if (err) { gnutls_perror (err); return -1; } + + /* put the x509 credentials to the current session + */ + err = gnutls_credentials_set (ct->ct_session, GNUTLS_CRD_CERTIFICATE, xcred); + if (err) { gnutls_perror (err); return -1; } + + gnutls_transport_set_ptr (ct->ct_session, + (gnutls_transport_ptr_t) (long) sock); + + /* Perform the TLS handshake. */ + again: + err = gnutls_handshake (ct->ct_session); + if (err < 0) + { + if (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED) goto again; + fprintf (stderr, "clnt_gnutls: TLS handshake failed\n"); + gnutls_perror (err); + return -1; + } + + /* Verify certificate. */ + if (verify_certificate (ct->ct_session, hostname) == -1) + { + fprintf (stderr, "clnt_gnutls: failed to verify peer's certificate\n"); + if (!no_verify) return -1; + } + + /* At this point, the server is verifying _our_ certificate, IP address, + * etc. If we make the grade, it will send us a '\1' byte. + */ + char buf[1]; + int len; + again_2: + len = gnutls_record_recv (ct->ct_session, buf, 1); + if (len < 0 && len != GNUTLS_E_UNEXPECTED_PACKET_LENGTH) + { + if (len == GNUTLS_E_AGAIN || len == GNUTLS_E_INTERRUPTED) goto again_2; + gnutls_perror (len); + return -1; + } + if (len != 1 || buf[0] != '\1') + { + fprintf (stderr, "clnt_gnutls: server verification (of our certificate or IP address) failed\n"); + return -1; + } + +#if 0 + /* Print session info. */ + print_info (session); +#endif + + return 0; +} + +static int +verify_certificate (gnutls_session_t session, const char *hostname) +{ + int ret; + unsigned int status; + const gnutls_datum_t *certs; + unsigned int nCerts, i; + time_t now; + + if ((ret = gnutls_certificate_verify_peers2 (session, &status)) < 0) { + fprintf(stderr, "clnt_gnutls: verify failed %s\n", gnutls_strerror(ret)); + return -1; + } + + if ((now = time(NULL)) == ((time_t)-1)) { + return -1; + } + + if (status != 0) { + if (status & GNUTLS_CERT_INVALID) + fprintf (stderr, "clnt_gnutls: The certificate is not trusted.\n"); + + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) + fprintf (stderr, "clnt_gnutls: The certificate hasn't got a known issuer.\n"); + + if (status & GNUTLS_CERT_REVOKED) + fprintf (stderr, "clnt_gnutls: The certificate has been revoked.\n"); + + if (status & GNUTLS_CERT_INSECURE_ALGORITHM) + fprintf (stderr, "clnt_gnutls: The certificate uses an insecure algorithm\n"); + + return -1; + } + + if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509) + return -1; + + if (!(certs = gnutls_certificate_get_peers(session, &nCerts))) + return -1; + + for (i = 0 ; i < nCerts ; i++) { + gnutls_x509_crt_t cert; + //printf ("Checking chain %d\n", i); + if (gnutls_x509_crt_init (&cert) < 0) + return -1; + + if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) { + gnutls_x509_crt_deinit (cert); + return -1; + } + + if (gnutls_x509_crt_get_expiration_time (cert) < now) { + fprintf (stderr, "clnt_gnutls: The certificate has expired\n"); + gnutls_x509_crt_deinit (cert); + return -1; + } + + if (gnutls_x509_crt_get_activation_time (cert) > now) { + fprintf (stderr, "clnt_gnutls: The certificate is not yet activated\n"); + gnutls_x509_crt_deinit (cert); + return -1; + } + + if (i == 0) { + if (!gnutls_x509_crt_check_hostname (cert, hostname)) { + fprintf (stderr, "clnt_gnutls: The certificate's owner does not match hostname '%s'\n", + hostname); + gnutls_x509_crt_deinit (cert); + return -1; + } + } + } + + return 0; +} + +static enum clnt_stat +clnttcp_call (h, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout) + CLIENT *h; + u_long proc; + xdrproc_t xdr_args; + caddr_t args_ptr; + xdrproc_t xdr_results; + caddr_t results_ptr; + struct timeval timeout; +{ + struct ct_data *ct = (struct ct_data *) h->cl_private; + XDR *xdrs = &(ct->ct_xdrs); + struct rpc_msg reply_msg; + u_long x_id; + u_int32_t *msg_x_id = (u_int32_t *) (ct->ct_mcall); /* yuk */ + bool_t shipnow; + int refreshes = 2; + + if (!ct->ct_waitset) + { + ct->ct_wait = timeout; + } + + shipnow = + (xdr_results == (xdrproc_t) 0 && ct->ct_wait.tv_sec == 0 + && ct->ct_wait.tv_usec == 0) ? FALSE : TRUE; + +call_again: + xdrs->x_op = XDR_ENCODE; + ct->ct_error.re_status = RPC_SUCCESS; + x_id = ntohl (--(*msg_x_id)); + if ((!XDR_PUTBYTES (xdrs, ct->ct_mcall, ct->ct_mpos)) || + (!XDR_PUTLONG (xdrs, (long *) &proc)) || + (!AUTH_MARSHALL (h->cl_auth, xdrs)) || + (!(*xdr_args) (xdrs, args_ptr))) + { + if (ct->ct_error.re_status == RPC_SUCCESS) + ct->ct_error.re_status = RPC_CANTENCODEARGS; + (void) xdrrec_endofrecord (xdrs, TRUE); + return (ct->ct_error.re_status); + } + if (!xdrrec_endofrecord (xdrs, shipnow)) + return ct->ct_error.re_status = RPC_CANTSEND; + if (!shipnow) + return RPC_SUCCESS; + /* + * Hack to provide rpc-based message passing + */ + if (ct->ct_wait.tv_sec == 0 && ct->ct_wait.tv_usec == 0) + { + return ct->ct_error.re_status = RPC_TIMEDOUT; + } + + + /* + * Keep receiving until we get a valid transaction id + */ + xdrs->x_op = XDR_DECODE; + while (TRUE) + { + reply_msg.acpted_rply.ar_verf = _null_auth; + reply_msg.acpted_rply.ar_results.where = NULL; + reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void; + if (!xdrrec_skiprecord (xdrs)) + return (ct->ct_error.re_status); + /* now decode and validate the response header */ + if (!xdr_replymsg (xdrs, &reply_msg)) + { + if (ct->ct_error.re_status == RPC_SUCCESS) + continue; + return ct->ct_error.re_status; + } + if ((u_int32_t) reply_msg.rm_xid == (u_int32_t) x_id) + break; + } + + /* + * process header + */ + _seterr_reply (&reply_msg, &(ct->ct_error)); + if (ct->ct_error.re_status == RPC_SUCCESS) + { + if (!AUTH_VALIDATE (h->cl_auth, &reply_msg.acpted_rply.ar_verf)) + { + ct->ct_error.re_status = RPC_AUTHERROR; + ct->ct_error.re_why = AUTH_INVALIDRESP; + } + else if (!(*xdr_results) (xdrs, results_ptr)) + { + if (ct->ct_error.re_status == RPC_SUCCESS) + ct->ct_error.re_status = RPC_CANTDECODERES; + } + /* free verifier ... */ + if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) + { + xdrs->x_op = XDR_FREE; + (void) xdr_opaque_auth (xdrs, + &(reply_msg.acpted_rply.ar_verf)); + } + } /* end successful completion */ + else + { + /* maybe our credentials need to be refreshed ... */ + if (refreshes-- && AUTH_REFRESH (h->cl_auth)) + goto call_again; + } /* end of unsuccessful completion */ + return ct->ct_error.re_status; +} + +static void +clnttcp_geterr (h, errp) + CLIENT *h; + struct rpc_err *errp; +{ + struct ct_data *ct = + (struct ct_data *) h->cl_private; + + *errp = ct->ct_error; +} + +static bool_t +clnttcp_freeres (cl, xdr_res, res_ptr) + CLIENT *cl; + xdrproc_t xdr_res; + caddr_t res_ptr; +{ + struct ct_data *ct = (struct ct_data *) cl->cl_private; + XDR *xdrs = &(ct->ct_xdrs); + + xdrs->x_op = XDR_FREE; + return (*xdr_res) (xdrs, res_ptr); +} + +static void +clnttcp_abort () +{ +} + +static bool_t +clnttcp_control (CLIENT *cl, int request, char *info) +{ + struct ct_data *ct = (struct ct_data *) cl->cl_private; + + + switch (request) + { + case CLSET_FD_CLOSE: + ct->ct_closeit = TRUE; + break; + case CLSET_FD_NCLOSE: + ct->ct_closeit = FALSE; + break; + case CLSET_TIMEOUT: + ct->ct_wait = *(struct timeval *) info; + ct->ct_waitset = TRUE; + break; + case CLGET_TIMEOUT: + *(struct timeval *) info = ct->ct_wait; + break; + case CLGET_FD: + *(int *)info = ct->ct_sock; + break; + case CLGET_XID: + /* + * use the knowledge that xid is the + * first element in the call structure *. + * This will get the xid of the PREVIOUS call + */ + *(u_long *)info = ntohl (*(u_long *)ct->ct_mcall); + break; + case CLSET_XID: + /* This will set the xid of the NEXT call */ + *(u_long *)ct->ct_mcall = htonl (*(u_long *)info - 1); + /* decrement by 1 as clnttcp_call() increments once */ + case CLGET_VERS: + /* + * This RELIES on the information that, in the call body, + * the version number field is the fifth field from the + * begining of the RPC header. MUST be changed if the + * call_struct is changed + */ + *(u_long *)info = ntohl (*(u_long *)(ct->ct_mcall + + 4 * BYTES_PER_XDR_UNIT)); + break; + case CLSET_VERS: + *(u_long *)(ct->ct_mcall + 4 * BYTES_PER_XDR_UNIT) + = htonl (*(u_long *)info); + break; + case CLGET_PROG: + /* + * This RELIES on the information that, in the call body, + * the program number field is the field from the + * begining of the RPC header. MUST be changed if the + * call_struct is changed + */ + *(u_long *)info = ntohl(*(u_long *)(ct->ct_mcall + + 3 * BYTES_PER_XDR_UNIT)); + break; + case CLSET_PROG: + *(u_long *)(ct->ct_mcall + 3 * BYTES_PER_XDR_UNIT) + = htonl(*(u_long *)info); + break; + /* The following are only possible with TI-RPC */ + case CLGET_RETRY_TIMEOUT: + case CLSET_RETRY_TIMEOUT: + case CLGET_SVC_ADDR: + case CLSET_SVC_ADDR: + case CLSET_PUSH_TIMOD: + case CLSET_POP_TIMOD: + default: + return FALSE; + } + return TRUE; +} + + +static void +clnttcp_destroy (CLIENT *h) +{ + struct ct_data *ct = + (struct ct_data *) h->cl_private; + + gnutls_bye (ct->ct_session, GNUTLS_SHUT_RDWR); + gnutls_deinit (ct->ct_session); + + if (ct->ct_closeit) + { + (void) close (ct->ct_sock); + } + XDR_DESTROY (&(ct->ct_xdrs)); + mem_free ((caddr_t) ct, sizeof (struct ct_data)); + mem_free ((caddr_t) h, sizeof (CLIENT)); +} + +/* + * Interface between xdr serializer and tcp connection. + * Behaves like the system calls, read & write, but keeps some error state + * around for the rpc level. + */ +static int +readtcp (char *ctptr, char *buf, int len) +{ + struct ct_data *ct = (struct ct_data *)ctptr; + struct pollfd fd; + int milliseconds = (ct->ct_wait.tv_sec * 1000) + + (ct->ct_wait.tv_usec / 1000); + + if (len == 0) + return 0; + + fd.fd = ct->ct_sock; + fd.events = POLLIN; + while (TRUE) + { + switch (poll(&fd, 1, milliseconds)) + { + case 0: + ct->ct_error.re_status = RPC_TIMEDOUT; + return -1; + + case -1: + if (errno == EINTR) + continue; + ct->ct_error.re_status = RPC_CANTRECV; + ct->ct_error.re_errno = errno; + return -1; + } + break; + } + + switch (len = gnutls_record_recv (ct->ct_session, buf, len)) + { + case 0: + /* premature eof */ + ct->ct_error.re_errno = ECONNRESET; + ct->ct_error.re_status = RPC_CANTRECV; + len = -1; /* it's really an error */ + break; + + case -1: + ct->ct_error.re_errno = errno; + ct->ct_error.re_status = RPC_CANTRECV; + break; + } + return len; +} + +static int +writetcp (char *ctptr, char *buf, int len) +{ + struct ct_data *ct = (struct ct_data*)ctptr; + int err; + + again: + if ((err = gnutls_record_send (ct->ct_session, buf, len)) < 0) + { + if (err == GNUTLS_E_INTERRUPTED || err == GNUTLS_E_AGAIN) goto again; + /* XXX Assume here that the underlying errno is preserved. */ + ct->ct_error.re_errno = errno; + ct->ct_error.re_status = RPC_CANTSEND; + return -1; + } + + return len; +} diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/clnt_gnutls.h libvirt-oldremote/src/sunrpc/clnt_gnutls.h --- libvirt-cvs/src/sunrpc/clnt_gnutls.h 1970-01-01 01:00:00.000000000 +0100 +++ libvirt-oldremote/src/sunrpc/clnt_gnutls.h 2007-02-23 15:59:36.000000000 +0000 @@ -0,0 +1,21 @@ +/* + * clnt_gnutls.h: Interface to the SunRPC over GnuTLS. + * + * Copyright (C) 2007 Red Hat, Inc. + * + * See COPYING.LIB for the license of this software. + * + * Richard Jones <rjones@xxxxxxxxxx> + */ + +#ifndef __CLNT_GNUTLS_H__ +#define __CLNT_GNUTLS_H__ + +extern CLIENT *clntgnutls_create (int pf, + struct sockaddr *raddr, int raddr_size, + gnutls_certificate_credentials_t xcred, + int no_verify, const char *hostname, + u_long prog, u_long vers, + int *sockp, u_int sendsz, u_int recvsz); + +#endif /* __CLNT_GNUTLS_H__ */ diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/clnt_tcp2.c libvirt-oldremote/src/sunrpc/clnt_tcp2.c --- libvirt-cvs/src/sunrpc/clnt_tcp2.c 1970-01-01 01:00:00.000000000 +0100 +++ libvirt-oldremote/src/sunrpc/clnt_tcp2.c 2007-03-02 11:38:13.000000000 +0000 @@ -0,0 +1,543 @@ +/* + * clnt_tcp2.c: SunRPC TCP client. This is a very slightly modified + * version of clnt_tcp.c from glibc which removes some of the IPv4 + * assumptions, so this version can be used over IPv4 or IPv6 sockets. + * + * Modifications from glibc-2.5 base by Richard Jones <rjones@xxxxxxxxxx>. + */ + +/* @(#)clnt_tcp.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro"; +#endif + +/* + * clnt_tcp.c, Implements a TCP/IP based, client side RPC. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * TCP based RPC supports 'batched calls'. + * A sequence of calls may be batched-up in a send buffer. The rpc call + * return immediately to the client even though the call was not necessarily + * sent. The batching occurs if the results' xdr routine is NULL (0) AND + * the rpc timeout value is zero (see clnt.h, rpc). + * + * Clients should NOT casually batch calls that in fact return results; that is, + * the server side should be aware that a call is batched and not produce any + * return message. Batched calls that produce many result messages can + * deadlock (netlock) the client and the server.... + * + * Now go hang yourself. + */ + +#include <netdb.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <libintl.h> +#include <rpc/rpc.h> +#include <sys/poll.h> +#include <sys/socket.h> +#include <rpc/pmap_clnt.h> +#ifdef USE_IN_LIBIO +# include <wchar.h> +#endif + +#include "clnt_tcp2.h" +#include "create_xid.h" + +#define MCALL_MSG_SIZE 24 + +struct ct_data + { + int ct_sock; + bool_t ct_closeit; + struct timeval ct_wait; + bool_t ct_waitset; /* wait set by clnt_control? */ + struct rpc_err ct_error; + char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg */ + u_int ct_mpos; /* pos after marshal */ + XDR ct_xdrs; + }; + +static int readtcp (char *, char *, int); +static int writetcp (char *, char *, int); + +static enum clnt_stat clnttcp_call (CLIENT *, u_long, xdrproc_t, caddr_t, + xdrproc_t, caddr_t, struct timeval); +static void clnttcp_abort (void); +static void clnttcp_geterr (CLIENT *, struct rpc_err *); +static bool_t clnttcp_freeres (CLIENT *, xdrproc_t, caddr_t); +static bool_t clnttcp_control (CLIENT *, int, char *); +static void clnttcp_destroy (CLIENT *); + +static const struct clnt_ops tcp_ops = +{ + clnttcp_call, + clnttcp_abort, + clnttcp_geterr, + clnttcp_freeres, + clnttcp_destroy, + clnttcp_control +}; + +/* + * Create a client handle for a tcp/ip connection. + * If *sockp<0, *sockp is set to a newly created TCP socket and it is + * connected to raddr. If *sockp non-negative then + * raddr is ignored. The rpc/tcp package does buffering + * similar to stdio, so the client must pick send and receive buffer sizes,]; + * 0 => use the default. + * If raddr->sin_port is 0, then a binder on the remote machine is + * consulted for the right port number. + * NB: *sockp is copied into a private area. + * NB: It is the clients responsibility to close *sockp. + * NB: The rpch->cl_auth is set null authentication. Caller may wish to set this + * something more useful. + */ +CLIENT * +clnttcp2_create (int af, struct sockaddr *raddr, int raddr_size, + u_long prog, u_long vers, + int *sockp, u_int sendsz, u_int recvsz) +{ + CLIENT *h; + struct ct_data *ct; + struct rpc_msg call_msg; + + h = (CLIENT *) mem_alloc (sizeof (*h)); + ct = (struct ct_data *) mem_alloc (sizeof (*ct)); + if (h == NULL || ct == NULL) + { + struct rpc_createerr *ce = &get_rpc_createerr (); + (void) fprintf (stderr, "clnttcp_create: out of memory\n"); + ce->cf_stat = RPC_SYSTEMERROR; + ce->cf_error.re_errno = ENOMEM; + goto fooy; + } + +#if 0 + // RWMJ: Note this will never be supported for portmap because the + // portmap protocol depends pretty fundamentally on IPv4. Don't + /// use portmapper anyway -- it's silly. + /* + * If no port number given ask the pmap for one + */ + if (port == 0) + { + u_short port; + if ((port = pmap_getport (raddr, prog, vers, IPPROTO_TCP)) == 0) + { + mem_free ((caddr_t) ct, sizeof (struct ct_data)); + mem_free ((caddr_t) h, sizeof (CLIENT)); + return ((CLIENT *) NULL); + } + raddr->sin_port = htons (port); + } +#endif + + /* + * If no socket given, open one + */ + if (*sockp < 0) + { + *sockp = socket (af, SOCK_STREAM, 0); + // RWMJ: Why? + //(void) bindresvport (*sockp, (struct sockaddr_in *) 0); + if ((*sockp < 0) + || (connect (*sockp, raddr, raddr_size) < 0)) + { + struct rpc_createerr *ce = &get_rpc_createerr (); + ce->cf_stat = RPC_SYSTEMERROR; + ce->cf_error.re_errno = errno; + if (*sockp >= 0) + (void) close (*sockp); + goto fooy; + } + ct->ct_closeit = TRUE; + } + else + { + ct->ct_closeit = FALSE; + } + + /* + * Set up private data struct + */ + ct->ct_sock = *sockp; + ct->ct_wait.tv_usec = 0; + ct->ct_waitset = FALSE; + + /* + * Initialize call message + */ + call_msg.rm_xid = _create_xid (); + call_msg.rm_direction = CALL; + call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; + call_msg.rm_call.cb_prog = prog; + call_msg.rm_call.cb_vers = vers; + + /* + * pre-serialize the static part of the call msg and stash it away + */ + xdrmem_create (&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE, + XDR_ENCODE); + if (!xdr_callhdr (&(ct->ct_xdrs), &call_msg)) + { + if (ct->ct_closeit) + { + (void) close (*sockp); + } + goto fooy; + } + ct->ct_mpos = XDR_GETPOS (&(ct->ct_xdrs)); + XDR_DESTROY (&(ct->ct_xdrs)); + + /* + * Create a client handle which uses xdrrec for serialization + * and authnone for authentication. + */ + xdrrec_create (&(ct->ct_xdrs), sendsz, recvsz, + (caddr_t) ct, readtcp, writetcp); + h->cl_ops = (struct clnt_ops *) &tcp_ops; + h->cl_private = (caddr_t) ct; + h->cl_auth = authnone_create (); + return h; + +fooy: + /* + * Something goofed, free stuff and barf + */ + mem_free ((caddr_t) ct, sizeof (struct ct_data)); + mem_free ((caddr_t) h, sizeof (CLIENT)); + return ((CLIENT *) NULL); +} + +static enum clnt_stat +clnttcp_call (h, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout) + CLIENT *h; + u_long proc; + xdrproc_t xdr_args; + caddr_t args_ptr; + xdrproc_t xdr_results; + caddr_t results_ptr; + struct timeval timeout; +{ + struct ct_data *ct = (struct ct_data *) h->cl_private; + XDR *xdrs = &(ct->ct_xdrs); + struct rpc_msg reply_msg; + u_long x_id; + u_int32_t *msg_x_id = (u_int32_t *) (ct->ct_mcall); /* yuk */ + bool_t shipnow; + int refreshes = 2; + + if (!ct->ct_waitset) + { + ct->ct_wait = timeout; + } + + shipnow = + (xdr_results == (xdrproc_t) 0 && ct->ct_wait.tv_sec == 0 + && ct->ct_wait.tv_usec == 0) ? FALSE : TRUE; + +call_again: + xdrs->x_op = XDR_ENCODE; + ct->ct_error.re_status = RPC_SUCCESS; + x_id = ntohl (--(*msg_x_id)); + if ((!XDR_PUTBYTES (xdrs, ct->ct_mcall, ct->ct_mpos)) || + (!XDR_PUTLONG (xdrs, (long *) &proc)) || + (!AUTH_MARSHALL (h->cl_auth, xdrs)) || + (!(*xdr_args) (xdrs, args_ptr))) + { + if (ct->ct_error.re_status == RPC_SUCCESS) + ct->ct_error.re_status = RPC_CANTENCODEARGS; + (void) xdrrec_endofrecord (xdrs, TRUE); + return (ct->ct_error.re_status); + } + if (!xdrrec_endofrecord (xdrs, shipnow)) + return ct->ct_error.re_status = RPC_CANTSEND; + if (!shipnow) + return RPC_SUCCESS; + /* + * Hack to provide rpc-based message passing + */ + if (ct->ct_wait.tv_sec == 0 && ct->ct_wait.tv_usec == 0) + { + return ct->ct_error.re_status = RPC_TIMEDOUT; + } + + + /* + * Keep receiving until we get a valid transaction id + */ + xdrs->x_op = XDR_DECODE; + while (TRUE) + { + reply_msg.acpted_rply.ar_verf = _null_auth; + reply_msg.acpted_rply.ar_results.where = NULL; + reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void; + if (!xdrrec_skiprecord (xdrs)) + return (ct->ct_error.re_status); + /* now decode and validate the response header */ + if (!xdr_replymsg (xdrs, &reply_msg)) + { + if (ct->ct_error.re_status == RPC_SUCCESS) + continue; + return ct->ct_error.re_status; + } + if ((u_int32_t) reply_msg.rm_xid == (u_int32_t) x_id) + break; + } + + /* + * process header + */ + _seterr_reply (&reply_msg, &(ct->ct_error)); + if (ct->ct_error.re_status == RPC_SUCCESS) + { + if (!AUTH_VALIDATE (h->cl_auth, &reply_msg.acpted_rply.ar_verf)) + { + ct->ct_error.re_status = RPC_AUTHERROR; + ct->ct_error.re_why = AUTH_INVALIDRESP; + } + else if (!(*xdr_results) (xdrs, results_ptr)) + { + if (ct->ct_error.re_status == RPC_SUCCESS) + ct->ct_error.re_status = RPC_CANTDECODERES; + } + /* free verifier ... */ + if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) + { + xdrs->x_op = XDR_FREE; + (void) xdr_opaque_auth (xdrs, + &(reply_msg.acpted_rply.ar_verf)); + } + } /* end successful completion */ + else + { + /* maybe our credentials need to be refreshed ... */ + if (refreshes-- && AUTH_REFRESH (h->cl_auth)) + goto call_again; + } /* end of unsuccessful completion */ + return ct->ct_error.re_status; +} + +static void +clnttcp_geterr (h, errp) + CLIENT *h; + struct rpc_err *errp; +{ + struct ct_data *ct = + (struct ct_data *) h->cl_private; + + *errp = ct->ct_error; +} + +static bool_t +clnttcp_freeres (cl, xdr_res, res_ptr) + CLIENT *cl; + xdrproc_t xdr_res; + caddr_t res_ptr; +{ + struct ct_data *ct = (struct ct_data *) cl->cl_private; + XDR *xdrs = &(ct->ct_xdrs); + + xdrs->x_op = XDR_FREE; + return (*xdr_res) (xdrs, res_ptr); +} + +static void +clnttcp_abort () +{ +} + +static bool_t +clnttcp_control (CLIENT *cl, int request, char *info) +{ + struct ct_data *ct = (struct ct_data *) cl->cl_private; + + + switch (request) + { + case CLSET_FD_CLOSE: + ct->ct_closeit = TRUE; + break; + case CLSET_FD_NCLOSE: + ct->ct_closeit = FALSE; + break; + case CLSET_TIMEOUT: + ct->ct_wait = *(struct timeval *) info; + ct->ct_waitset = TRUE; + break; + case CLGET_TIMEOUT: + *(struct timeval *) info = ct->ct_wait; + break; + case CLGET_FD: + *(int *)info = ct->ct_sock; + break; + case CLGET_XID: + /* + * use the knowledge that xid is the + * first element in the call structure *. + * This will get the xid of the PREVIOUS call + */ + *(u_long *)info = ntohl (*(u_long *)ct->ct_mcall); + break; + case CLSET_XID: + /* This will set the xid of the NEXT call */ + *(u_long *)ct->ct_mcall = htonl (*(u_long *)info - 1); + /* decrement by 1 as clnttcp_call() increments once */ + case CLGET_VERS: + /* + * This RELIES on the information that, in the call body, + * the version number field is the fifth field from the + * begining of the RPC header. MUST be changed if the + * call_struct is changed + */ + *(u_long *)info = ntohl (*(u_long *)(ct->ct_mcall + + 4 * BYTES_PER_XDR_UNIT)); + break; + case CLSET_VERS: + *(u_long *)(ct->ct_mcall + 4 * BYTES_PER_XDR_UNIT) + = htonl (*(u_long *)info); + break; + case CLGET_PROG: + /* + * This RELIES on the information that, in the call body, + * the program number field is the field from the + * begining of the RPC header. MUST be changed if the + * call_struct is changed + */ + *(u_long *)info = ntohl(*(u_long *)(ct->ct_mcall + + 3 * BYTES_PER_XDR_UNIT)); + break; + case CLSET_PROG: + *(u_long *)(ct->ct_mcall + 3 * BYTES_PER_XDR_UNIT) + = htonl(*(u_long *)info); + break; + /* The following are only possible with TI-RPC */ + case CLGET_RETRY_TIMEOUT: + case CLSET_RETRY_TIMEOUT: + case CLGET_SVC_ADDR: + case CLSET_SVC_ADDR: + case CLSET_PUSH_TIMOD: + case CLSET_POP_TIMOD: + default: + return FALSE; + } + return TRUE; +} + + +static void +clnttcp_destroy (CLIENT *h) +{ + struct ct_data *ct = + (struct ct_data *) h->cl_private; + + if (ct->ct_closeit) + { + (void) close (ct->ct_sock); + } + XDR_DESTROY (&(ct->ct_xdrs)); + mem_free ((caddr_t) ct, sizeof (struct ct_data)); + mem_free ((caddr_t) h, sizeof (CLIENT)); +} + +/* + * Interface between xdr serializer and tcp connection. + * Behaves like the system calls, read & write, but keeps some error state + * around for the rpc level. + */ +static int +readtcp (char *ctptr, char *buf, int len) +{ + struct ct_data *ct = (struct ct_data *)ctptr; + struct pollfd fd; + int milliseconds = (ct->ct_wait.tv_sec * 1000) + + (ct->ct_wait.tv_usec / 1000); + + if (len == 0) + return 0; + + fd.fd = ct->ct_sock; + fd.events = POLLIN; + while (TRUE) + { + switch (poll(&fd, 1, milliseconds)) + { + case 0: + ct->ct_error.re_status = RPC_TIMEDOUT; + return -1; + + case -1: + if (errno == EINTR) + continue; + ct->ct_error.re_status = RPC_CANTRECV; + ct->ct_error.re_errno = errno; + return -1; + } + break; + } + switch (len = read (ct->ct_sock, buf, len)) + { + + case 0: + /* premature eof */ + ct->ct_error.re_errno = ECONNRESET; + ct->ct_error.re_status = RPC_CANTRECV; + len = -1; /* it's really an error */ + break; + + case -1: + ct->ct_error.re_errno = errno; + ct->ct_error.re_status = RPC_CANTRECV; + break; + } + return len; +} + +static int +writetcp (char *ctptr, char *buf, int len) +{ + int i, cnt; + struct ct_data *ct = (struct ct_data*)ctptr; + + for (cnt = len; cnt > 0; cnt -= i, buf += i) + { + if ((i = write (ct->ct_sock, buf, cnt)) == -1) + { + ct->ct_error.re_errno = errno; + ct->ct_error.re_status = RPC_CANTSEND; + return -1; + } + } + return len; +} diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/clnt_tcp2.h libvirt-oldremote/src/sunrpc/clnt_tcp2.h --- libvirt-cvs/src/sunrpc/clnt_tcp2.h 1970-01-01 01:00:00.000000000 +0100 +++ libvirt-oldremote/src/sunrpc/clnt_tcp2.h 2007-02-23 15:59:36.000000000 +0000 @@ -0,0 +1,18 @@ +/* + * clnt_tcp2.h: Interface to the SunRPC over TCP. + * + * Copyright (C) 2007 Red Hat, Inc. + * + * See COPYING.LIB for the license of this software. + * + * Richard Jones <rjones@xxxxxxxxxx> + */ + +#ifndef __CLNT_TCP2_H__ +#define __CLNT_TCP2_H__ + +extern CLIENT *clnttcp2_create (int af, struct sockaddr *raddr, int raddr_size, + u_long prog, u_long vers, + int *sockp, u_int sendsz, u_int recvsz); + +#endif /* __CLNT_TCP2_H__ */ diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/create_xid.c libvirt-oldremote/src/sunrpc/create_xid.c --- libvirt-cvs/src/sunrpc/create_xid.c 1970-01-01 01:00:00.000000000 +0100 +++ libvirt-oldremote/src/sunrpc/create_xid.c 2007-03-02 11:37:51.000000000 +0000 @@ -0,0 +1,65 @@ +/* + * create_xid.c: Makes unique, unguessable identifiers. This is modified + * from the original source found in glibc-2.5. The original version + * is thread safe (but the rest of SunRPC isn't). This version is + * not thread safe. + * + * Modifications from glibc-2.5 base by Richard Jones <rjones@xxxxxxxxxx>. + */ + +/* Copyright (c) 1998, 2000 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Thorsten Kukuk <kukuk@xxxxxxxxxxxxxxxxxxx>, 1998. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <unistd.h> +#include <stdlib.h> +#include <sys/time.h> +#include <rpc/rpc.h> + +#include "config.h" +#include "create_xid.h" + +#if HAVE_LRAND48_R +static int is_initialized; +static struct drand48_data __rpc_lrand48_data; + +unsigned long +_create_xid (void) +{ + long int res; + + if (!is_initialized) + { + struct timeval now; + + gettimeofday (&now, (struct timezone *) 0); + srand48_r (now.tv_sec ^ now.tv_usec, &__rpc_lrand48_data); + is_initialized = 1; + } + + lrand48_r (&__rpc_lrand48_data, &res); + + return res; +} +#else +unsigned long +_create_xid (void) +{ + return rand (); +} +#endif diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/create_xid.h libvirt-oldremote/src/sunrpc/create_xid.h --- libvirt-cvs/src/sunrpc/create_xid.h 1970-01-01 01:00:00.000000000 +0100 +++ libvirt-oldremote/src/sunrpc/create_xid.h 2007-03-02 11:37:34.000000000 +0000 @@ -0,0 +1,16 @@ +/* + * create_xid.h: Unique, unguessable identifiers. + * + * Copyright (C) 2007 Red Hat, Inc. + * + * See COPYING.LIB for the license of this software. + * + * Richard Jones <rjones@xxxxxxxxxx> + */ + +#ifndef __CREATE_XID_H__ +#define __CREATE_XID_H__ + +unsigned long _create_xid (void); + +#endif /* __CREATE_XID_H__ */ diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/README libvirt-oldremote/src/sunrpc/README --- libvirt-cvs/src/sunrpc/README 1970-01-01 01:00:00.000000000 +0100 +++ libvirt-oldremote/src/sunrpc/README 2007-02-26 14:13:55.000000000 +0000 @@ -0,0 +1,29 @@ +This directory contains modified SunRPC transports. They are based on +the standard transports from glibc 2.5 (in the sunrpc/ directory +there). + +clnt_ext.c + - Fork an external program, eg. ssh. + (Original: clnt_unix.c) + +clnt_gnutls.c + - Modified for IPv6 and GnuTLS support. + (Original: clnt_tcp.c) + +clnt_tcp2.c + - Modified for IPv6 support. + (Original: clnt_tcp.c) + +svc_gnutls.c + - Modified for IPv6, GnuTLS and connection support. + (Original: svc_tcp.c) + +svc_tcp2.c + - Modified for IPv6 and connection support. + (Original: svc_tcp.c) + +svc_unix2.c + - Modified for connection support. + (Original: svc_unix.c) + +$Id$ \ No newline at end of file diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/svc_connections.h libvirt-oldremote/src/sunrpc/svc_connections.h --- libvirt-cvs/src/sunrpc/svc_connections.h 1970-01-01 01:00:00.000000000 +0100 +++ libvirt-oldremote/src/sunrpc/svc_connections.h 2007-02-26 14:20:54.000000000 +0000 @@ -0,0 +1,18 @@ +/* + * svc_connections.h: Callbacks to make SunRPC server transports + * connection-oriented. + * + * Copyright (C) 2007 Red Hat, Inc. + * + * See COPYING.LIB for the license of this software. + * + * Richard Jones <rjones@xxxxxxxxxx> + */ + +#ifndef __SVC_CONNECTIONS_H__ +#define __SVC_CONNECTIONS_H__ + +typedef int (*svc_conn_create) (SVCXPRT *); +typedef void (*svc_conn_destroy) (SVCXPRT *); + +#endif /* __SVC_CONNECTIONS_H__ */ diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/svc_gnutls.c libvirt-oldremote/src/sunrpc/svc_gnutls.c --- libvirt-cvs/src/sunrpc/svc_gnutls.c 1970-01-01 01:00:00.000000000 +0100 +++ libvirt-oldremote/src/sunrpc/svc_gnutls.c 2007-02-28 17:02:18.000000000 +0000 @@ -0,0 +1,674 @@ +/* + * svc_gnutls.c: SunRPC over GnuTLS server. This is a modified version + * of svc_tcp.c from glibc which is written to run over GnuTLS. + * + * Modifications from glibc-2.5 base by Richard Jones <rjones@xxxxxxxxxx>. + */ + +/* @(#)svc_tcp.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)svc_tcp.c 1.21 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * svc_tcp.c, Server side for TCP/IP based RPC. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * Actually implements two flavors of transporter - + * a tcp rendezvouser (a listener and connection establisher) + * and a record/tcp stream. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <libintl.h> +#include <rpc/rpc.h> +#include <sys/socket.h> +#include <sys/poll.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> + +#ifdef USE_IN_LIBIO +# include <wchar.h> +# include <libio/iolibio.h> +#endif + +#include <gnutls/gnutls.h> +#include <gnutls/x509.h> +#include "svc_gnutls.h" + +/* + * Ops vector for TCP/IP based rpc service handle + */ +static bool_t svctcp_recv (SVCXPRT *, struct rpc_msg *); +static enum xprt_stat svctcp_stat (SVCXPRT *); +static bool_t svctcp_getargs (SVCXPRT *, xdrproc_t, caddr_t); +static bool_t svctcp_reply (SVCXPRT *, struct rpc_msg *); +static bool_t svctcp_freeargs (SVCXPRT *, xdrproc_t, caddr_t); +static void svctcp_destroy (SVCXPRT *); + +static const struct xp_ops svctcp_op = +{ + svctcp_recv, + svctcp_stat, + svctcp_getargs, + svctcp_reply, + svctcp_freeargs, + svctcp_destroy +}; + +/* + * Ops vector for TCP/IP rendezvous handler + */ +static bool_t rendezvous_request (SVCXPRT *, struct rpc_msg *); +static enum xprt_stat rendezvous_stat (SVCXPRT *); +static void svctcp_rendezvous_abort (void) __attribute__ ((__noreturn__)); + +/* This function makes sure abort() relocation goes through PLT + and thus can be lazy bound. */ +static void +svctcp_rendezvous_abort (void) +{ + abort (); +}; + +static const struct xp_ops svctcp_rendezvous_op = +{ + rendezvous_request, + rendezvous_stat, + (bool_t (*) (SVCXPRT *, xdrproc_t, caddr_t)) svctcp_rendezvous_abort, + (bool_t (*) (SVCXPRT *, struct rpc_msg *)) svctcp_rendezvous_abort, + (bool_t (*) (SVCXPRT *, xdrproc_t, caddr_t)) svctcp_rendezvous_abort, + svctcp_destroy +}; + +static int readtcp (char*, char *, int); +static int writetcp (char *, char *, int); +static SVCXPRT *makefd_xprt (int, u_int, u_int, svc_conn_destroy, gnutls_session_t); + +struct tcp_rendezvous + { /* kept in xprt->xp_p1 */ + u_int sendsize; + u_int recvsize; + svc_conn_create create; + svc_conn_destroy destroy; + gnutls_certificate_credentials_t x509_cred; + int no_verify_certificate; + int no_verify_address; + void *check_allowed_client_data; + int (*check_allowed_client) (void *, const char *); + }; + +struct tcp_conn + { /* kept in xprt->xp_p1 */ + enum xprt_stat strm_stat; + u_long x_id; + XDR xdrs; + char verf_body[MAX_AUTH_BYTES]; + svc_conn_destroy destroy; + }; + +/* + * Usage: + * xprt = svcgnutls_create(sock, send_buf_size, recv_buf_size, + * gnutls_certificate_credentials_t x509_cred, + * etc.); + * + * Creates, registers, and returns a (rpc) tcp based transporter. + * Once *xprt is initialized, it is registered as a transporter + * see (svc.h, xprt_register). This routine returns + * a NULL if a problem occurred. + * + * If sock<0 then a socket is created, else sock is used. + * If the socket, sock is not bound to a port then svctcp_create + * binds it to an arbitrary port. The routine then starts a tcp + * listener on the socket's associated port. In any (successful) case, + * xprt->xp_sock is the registered socket number and xprt->xp_port is the + * associated port number. + * + * Since tcp streams do buffered io similar to stdio, the caller can specify + * how big the send and receive buffers are via the second and third parms; + * 0 => use the system default. + */ +SVCXPRT * +svcgnutls_create (int sock, u_int sendsize, u_int recvsize, + svc_conn_create create, svc_conn_destroy destroy, + gnutls_certificate_credentials_t x509_cred, + int no_verify_certificate, int no_verify_address, + void *check_allowed_client_data, + int (*check_allowed_client) (void *, const char *)) +{ + bool_t madesock = FALSE; + SVCXPRT *xprt; + struct tcp_rendezvous *r; + struct sockaddr_in addr; + socklen_t len = sizeof (struct sockaddr_in); + + if (sock == RPC_ANYSOCK) + { + if ((sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) + { + perror ("svc_gnutls.c - tcp socket creation problem"); + return (SVCXPRT *) NULL; + } + madesock = TRUE; + + bzero ((char *) &addr, sizeof (addr)); + addr.sin_family = AF_INET; + if (bindresvport (sock, &addr)) + { + addr.sin_port = 0; + (void) bind (sock, (struct sockaddr *) &addr, len); + } + if ((getsockname (sock, (struct sockaddr *) &addr, &len) != 0) || + (listen (sock, SOMAXCONN) != 0)) + { + perror ("svc_gnutls.c - cannot getsockname or listen"); + if (madesock) + (void) close (sock); + return (SVCXPRT *) NULL; + } + } + r = (struct tcp_rendezvous *) mem_alloc (sizeof (*r)); + xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT)); + if (r == NULL || xprt == NULL) + { + (void) fprintf (stderr, "svcgnutls_create: out of memory\n"); + mem_free (r, sizeof (*r)); + mem_free (xprt, sizeof (SVCXPRT)); + return NULL; + } + r->sendsize = sendsize; + r->recvsize = recvsize; + r->create = create; + r->destroy = destroy; + r->x509_cred = x509_cred; + r->no_verify_certificate = no_verify_certificate; + r->no_verify_address = no_verify_address; + r->check_allowed_client_data = check_allowed_client_data; + r->check_allowed_client = check_allowed_client; + xprt->xp_p2 = NULL; + xprt->xp_p1 = (caddr_t) r; + xprt->xp_verf = _null_auth; + xprt->xp_ops = &svctcp_rendezvous_op; + xprt->xp_port = ntohs (addr.sin_port); + xprt->xp_sock = sock; + xprt_register (xprt); + return xprt; +} + +#if 0 +/* + * Like svtcp_create(), except the routine takes any *open* UNIX file + * descriptor as its first input. + */ +SVCXPRT * +svcfd_create (int fd, u_int sendsize, u_int recvsize) +{ + return makefd_xprt (fd, sendsize, recvsize); +} +#endif + +static SVCXPRT * +makefd_xprt (int fd, u_int sendsize, u_int recvsize, svc_conn_destroy destroy, + gnutls_session_t session) +{ + SVCXPRT *xprt; + struct tcp_conn *cd; + + xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT)); + cd = (struct tcp_conn *) mem_alloc (sizeof (struct tcp_conn)); + if (xprt == (SVCXPRT *) NULL || cd == NULL) + { + (void) fprintf (stderr, "svc_gnutls: makefd_xprt: out of memory\n"); + mem_free (xprt, sizeof (SVCXPRT)); + mem_free (cd, sizeof (struct tcp_conn)); + return NULL; + } + cd->strm_stat = XPRT_IDLE; + cd->destroy = destroy; + xdrrec_create (&(cd->xdrs), sendsize, recvsize, + (caddr_t) xprt, readtcp, writetcp); + xprt->xp_p2 = (caddr_t) session; + xprt->xp_p1 = (caddr_t) cd; + xprt->xp_verf.oa_base = cd->verf_body; + xprt->xp_addrlen = 0; + xprt->xp_ops = &svctcp_op; /* truly deals with calls */ + xprt->xp_port = 0; /* this is a connection, not a rendezvouser */ + xprt->xp_sock = fd; + xprt_register (xprt); + return xprt; +} + +// XXX DH_BITS must be defined the same in the server main loop too. +#define DH_BITS 1024 + +static gnutls_session_t +initialize_tls_session (gnutls_certificate_credentials_t x509_cred) +{ + gnutls_session_t session; + int err; + + err = gnutls_init (&session, GNUTLS_SERVER); + if (err) { gnutls_perror (err); return 0; } + + /* avoid calling all the priority functions, since the defaults + * are adequate. + */ + err = gnutls_set_default_priority (session); + if (err) { gnutls_perror (err); return 0; } + + err = gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509_cred); + if (err) { gnutls_perror (err); return 0; } + + /* request client certificate if any. + */ + gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUEST); + + gnutls_dh_set_prime_bits (session, DH_BITS); + + return session; +} + +static int verify_certificate (gnutls_session_t session, const char *addr); + +static bool_t +rendezvous_request (SVCXPRT *xprt, + struct rpc_msg *errmsg __attribute__((unused))) +{ + int sock, err; + struct tcp_rendezvous *r; + struct sockaddr_storage raddr; + socklen_t raddrlen; + + r = (struct tcp_rendezvous *) xprt->xp_p1; + gnutls_session_t session = initialize_tls_session (r->x509_cred); + if (session == 0) return FALSE; + +again: + raddrlen = sizeof raddr; + if ((sock = + accept (xprt->xp_sock, (struct sockaddr *) &raddr, &raddrlen)) < 0) + { + if (errno == EINTR) + goto again; + return FALSE; + } + + gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) (long) sock); + + /* XXX Possible DoS attack here if the client sends the handshake + * very slowly. It may be possible to avoid this using a non-blocking + * socket, see: + * https://www.redhat.com/archives/libvir-list/2007-February/msg00192.html + */ + int ret = gnutls_handshake (session); + if (ret < 0) + { + fprintf (stderr, "svc_gnutls: TLS handshake failed (%s)\n\n", + gnutls_strerror (ret)); + tls_failed: + close (sock); + gnutls_deinit (session); + return FALSE; + } + + /* Convert IP address to printable string (eg. "127.0.0.1") */ + char addr[NI_MAXHOST]; + err = getnameinfo ((struct sockaddr *) &raddr, raddrlen, + addr, sizeof addr, NULL, 0, + NI_NUMERICHOST); + if (err != 0) + { + fprintf (stderr, "svc_gnutls: getnameinfo: %s\n", gai_strerror (err)); + goto tls_failed; + } + + /* Verify client certificate. */ + if (verify_certificate (session, addr) == -1) + { + fprintf (stderr, "svc_gnutls: failed to verify client's certificate\n"); + if (!r->no_verify_certificate) goto tls_failed; + } + + if (!r->check_allowed_client (r->check_allowed_client_data, addr)) + { + fprintf (stderr, "svc_gnutls: client is not on the list of allowed clients\n"); + if (!r->no_verify_address) goto tls_failed; + } + + /* Checks have succeeded. Write a '\1' byte back to the client to + * indicate this (otherwise the socket is abruptly closed in + * tls_failed above). This is necessary to avoid the client + * segfaulting if the checks fail. + */ + char buf[1] = { '\1' }; + again_2: + if ((err = gnutls_record_send (session, buf, 1)) < 0) + { + if (err == GNUTLS_E_INTERRUPTED || err == GNUTLS_E_AGAIN) goto again_2; + fprintf (stderr, "svc_gnutls: client closed connection early\n"); + goto tls_failed; + } + + /* + * make a new transporter (re-uses xprt) + */ + xprt = makefd_xprt (sock, r->sendsize, r->recvsize, r->destroy, session); +#if 0 /* This assumed IPv4 - commented out. */ + memcpy (&xprt->xp_raddr, &addr, sizeof (addr)); + xprt->xp_addrlen = len; +#endif + if (r->create) (void) r->create (xprt); + return FALSE; /* there is never an rpc msg to be processed */ +} + +static int my_gnutls_x509_crt_check_address (gnutls_x509_crt_t cert, const char *addr); + +static int +verify_certificate (gnutls_session_t session, const char *addr) +{ + int ret; + unsigned int status; + const gnutls_datum_t *certs; + unsigned int nCerts, i; + time_t now; + + if ((ret = gnutls_certificate_verify_peers2 (session, &status)) < 0){ + fprintf (stderr, "svc_gnutls: verify failed: %s\n", gnutls_strerror (ret)); + return -1; + } + + if ((now = time(NULL)) == ((time_t)-1)) { + return -1; + } + + if (status != 0) { + if (status & GNUTLS_CERT_INVALID) + fprintf (stderr, "svc_gnutls: the client certificate is not trusted.\n"); + + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) + fprintf (stderr, "svc_gnutls: the client certificate hasn't got a known issuer.\n"); + + if (status & GNUTLS_CERT_REVOKED) + fprintf (stderr, "svc_gnutls: the client certificate has been revoked.\n"); + + if (status & GNUTLS_CERT_INSECURE_ALGORITHM) + fprintf (stderr, "svc_gnutls: the client certificate uses an insecure algorithm\n"); + + return -1; + } + + if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509) + return -1; + + if (!(certs = gnutls_certificate_get_peers(session, &nCerts))) + return -1; + + for (i = 0 ; i < nCerts ; i++) { + gnutls_x509_crt_t cert; + + if (gnutls_x509_crt_init (&cert) < 0) + return -1; + + if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) { + gnutls_x509_crt_deinit (cert); + return -1; + } + + if (gnutls_x509_crt_get_expiration_time (cert) < now) { + fprintf(stderr, "svc_gnutls: the client certificate has expired\n"); + gnutls_x509_crt_deinit (cert); + return -1; + } + + if (gnutls_x509_crt_get_activation_time (cert) > now) { + fprintf(stderr, "svc_gnutls: the client certificate is not yet activated\n"); + gnutls_x509_crt_deinit (cert); + return -1; + } + + if (i == 0) { + if (!my_gnutls_x509_crt_check_address (cert, addr)) { + fprintf (stderr, "svc_gnutls: the client certificate's subjectAltName.iPAddress or commonName does not match the client's actual address (%s)\n", + addr); + gnutls_x509_crt_deinit (cert); + return -1; + } + } + } + + return 0; +} + +/* Modified version of gnutls_x509_crt_check_hostname which checks + * a numeric IP address against iPAddress in the certificate. If + * and only if there is no subjectAltName iPAddress extension, then + * we check against the CN. It currently only allows exact matches + * (ie. not wildcards). See RFC 2818 sections 3.1 & 3.2. Also + * updated in RFC 3280. Also see lib/x509/rfc2818_hostname.c in + * the GnuTLS source. + */ +static int +my_gnutls_x509_crt_check_address (gnutls_x509_crt_t cert, const char *addr) +{ + char name[256]; // This is hard-coded in original too. + size_t namesize; + int i, ret = 0, found_iPAddress = 0; + for (i = 0; ret >= 0; ++i) + { + namesize = sizeof name; + ret = + gnutls_x509_crt_get_subject_alt_name (cert, i, name, &namesize, NULL); + if (ret == GNUTLS_SAN_IPADDRESS) + { + found_iPAddress = 1; + if (strcmp (name, addr) == 0) return 1; + } + } + + if (!found_iPAddress) + { + namesize = sizeof name; + if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME, 0, + 0, name, &namesize) < 0) + return 0; + + if (strcmp (name, addr) == 0) return 1; + } + + return 0; // Not found. +} + +static enum xprt_stat +rendezvous_stat (SVCXPRT *xprt __attribute__((unused))) +{ + return XPRT_IDLE; +} + +static void +svctcp_destroy (SVCXPRT *xprt) +{ + struct tcp_conn *cd = (struct tcp_conn *) xprt->xp_p1; + gnutls_session_t session = (gnutls_session_t) xprt->xp_p2; + + gnutls_bye (session, GNUTLS_SHUT_WR); + + if (cd->destroy) cd->destroy (xprt); + + xprt_unregister (xprt); + (void) close (xprt->xp_sock); + if (xprt->xp_port != 0) + { + /* a rendezvouser socket */ + xprt->xp_port = 0; + } + else + { + /* an actual connection socket */ + XDR_DESTROY (&(cd->xdrs)); + } + + gnutls_deinit (session); + + mem_free ((caddr_t) cd, sizeof (struct tcp_conn)); + mem_free ((caddr_t) xprt, sizeof (SVCXPRT)); +} + + +/* + * reads data from the tcp connection. + * any error is fatal and the connection is closed. + * (And a read of zero bytes is a half closed stream => error.) + */ +static int +readtcp (char *xprtptr, char *buf, int len) +{ + SVCXPRT *xprt = (SVCXPRT *)xprtptr; + gnutls_session_t session = (gnutls_session_t) xprt->xp_p2; + int sock = xprt->xp_sock; + int milliseconds = 35 * 1000; + struct pollfd pollfd; + + do + { + pollfd.fd = sock; + pollfd.events = POLLIN; + switch (poll (&pollfd, 1, milliseconds)) + { + case -1: + if (errno == EINTR) + continue; + /*FALLTHROUGH*/ + case 0: + goto fatal_err; + default: + if ((pollfd.revents & POLLERR) || (pollfd.revents & POLLHUP) + || (pollfd.revents & POLLNVAL)) + goto fatal_err; + break; + } + } + while ((pollfd.revents & POLLIN) == 0); + + if ((len = gnutls_record_recv (session, buf, len)) > 0) + return len; + + fatal_err: + ((struct tcp_conn *) (xprt->xp_p1))->strm_stat = XPRT_DIED; + return -1; +} + +/* + * writes data to the tcp connection. + * Any error is fatal and the connection is closed. + */ +static int +writetcp (char *xprtptr, char * buf, int len) +{ + SVCXPRT *xprt = (SVCXPRT *)xprtptr; + gnutls_session_t session = (gnutls_session_t) xprt->xp_p2; + int err; + + again: + if ((err = gnutls_record_send (session, buf, len)) < 0) { + if (err == GNUTLS_E_INTERRUPTED || err == GNUTLS_E_AGAIN) goto again; + ((struct tcp_conn *) (xprt->xp_p1))->strm_stat = XPRT_DIED; + return -1; + } + + return len; +} + +static enum xprt_stat +svctcp_stat (SVCXPRT *xprt) +{ + struct tcp_conn *cd = + (struct tcp_conn *) (xprt->xp_p1); + + if (cd->strm_stat == XPRT_DIED) + return XPRT_DIED; + if (!xdrrec_eof (&(cd->xdrs))) + return XPRT_MOREREQS; + return XPRT_IDLE; +} + +static bool_t +svctcp_recv (SVCXPRT *xprt, struct rpc_msg *msg) +{ + struct tcp_conn *cd = (struct tcp_conn *) (xprt->xp_p1); + XDR *xdrs = &(cd->xdrs); + + xdrs->x_op = XDR_DECODE; + (void) xdrrec_skiprecord (xdrs); + if (xdr_callmsg (xdrs, msg)) + { + cd->x_id = msg->rm_xid; + return TRUE; + } + cd->strm_stat = XPRT_DIED; /* XXXX */ + return FALSE; +} + +static bool_t +svctcp_getargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr) +{ + return ((*xdr_args) (&(((struct tcp_conn *) + (xprt->xp_p1))->xdrs), args_ptr)); +} + +static bool_t +svctcp_freeargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr) +{ + XDR *xdrs = &(((struct tcp_conn *) (xprt->xp_p1))->xdrs); + + xdrs->x_op = XDR_FREE; + return ((*xdr_args) (xdrs, args_ptr)); +} + +static bool_t +svctcp_reply (SVCXPRT *xprt, struct rpc_msg *msg) +{ + struct tcp_conn *cd = (struct tcp_conn *) (xprt->xp_p1); + XDR *xdrs = &(cd->xdrs); + bool_t stat; + + xdrs->x_op = XDR_ENCODE; + msg->rm_xid = cd->x_id; + stat = xdr_replymsg (xdrs, msg); + (void) xdrrec_endofrecord (xdrs, TRUE); + return stat; +} diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/svc_gnutls.h libvirt-oldremote/src/sunrpc/svc_gnutls.h --- libvirt-cvs/src/sunrpc/svc_gnutls.h 1970-01-01 01:00:00.000000000 +0100 +++ libvirt-oldremote/src/sunrpc/svc_gnutls.h 2007-02-26 14:19:25.000000000 +0000 @@ -0,0 +1,25 @@ +/* + * svc_gnutls.h: Interface to the SunRPC over GnuTLS server. + * + * Copyright (C) 2007 Red Hat, Inc. + * + * See COPYING.LIB for the license of this software. + * + * Richard Jones <rjones@xxxxxxxxxx> + */ + +#ifndef __SVC_GNUTLS_H__ +#define __SVC_GNUTLS_H__ + +#include "svc_connections.h" + +extern SVCXPRT *svcgnutls_create (int sock, u_int sendsize, u_int recvsize, + svc_conn_create create, + svc_conn_destroy destroy, + gnutls_certificate_credentials_t x509_cred, + int no_verify_certificate, + int no_verify_address, + void *check_allowed_client_data, + int (*check_allowed_client) (void *, const char *)); + +#endif /* __SVC_GNUTLS_H__ */ diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/svc_tcp2.c libvirt-oldremote/src/sunrpc/svc_tcp2.c --- libvirt-cvs/src/sunrpc/svc_tcp2.c 1970-01-01 01:00:00.000000000 +0100 +++ libvirt-oldremote/src/sunrpc/svc_tcp2.c 2007-02-26 14:41:18.000000000 +0000 @@ -0,0 +1,440 @@ +/* + * svc_tcp2.c: SunRPC TCP server. This is a very slightly modified + * version of svc_tcp.c from glibc which removes some of the IPv4 + * assumptions, so this version can be used over IPv4 or IPv6 sockets. + * + * Modifications from glibc-2.5 base by Richard Jones <rjones@xxxxxxxxxx>. + */ + +/* @(#)svc_tcp.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)svc_tcp.c 1.21 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * svc_tcp.c, Server side for TCP/IP based RPC. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * Actually implements two flavors of transporter - + * a tcp rendezvouser (a listener and connection establisher) + * and a record/tcp stream. + */ + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <libintl.h> +#include <rpc/rpc.h> +#include <sys/socket.h> +#include <sys/poll.h> +#include <errno.h> +#include <stdlib.h> + +#ifdef USE_IN_LIBIO +# include <wchar.h> +# include <libio/iolibio.h> +#endif + +#include "svc_tcp2.h" + +/* + * Ops vector for TCP/IP based rpc service handle + */ +static bool_t svctcp_recv (SVCXPRT *, struct rpc_msg *); +static enum xprt_stat svctcp_stat (SVCXPRT *); +static bool_t svctcp_getargs (SVCXPRT *, xdrproc_t, caddr_t); +static bool_t svctcp_reply (SVCXPRT *, struct rpc_msg *); +static bool_t svctcp_freeargs (SVCXPRT *, xdrproc_t, caddr_t); +static void svctcp_destroy (SVCXPRT *); + +static const struct xp_ops svctcp_op = +{ + svctcp_recv, + svctcp_stat, + svctcp_getargs, + svctcp_reply, + svctcp_freeargs, + svctcp_destroy +}; + +/* + * Ops vector for TCP/IP rendezvous handler + */ +static bool_t rendezvous_request (SVCXPRT *, struct rpc_msg *); +static enum xprt_stat rendezvous_stat (SVCXPRT *); +static void svctcp_rendezvous_abort (void) __attribute__ ((__noreturn__)); + +/* This function makes sure abort() relocation goes through PLT + and thus can be lazy bound. */ +static void +svctcp_rendezvous_abort (void) +{ + abort (); +}; + +static const struct xp_ops svctcp_rendezvous_op = +{ + rendezvous_request, + rendezvous_stat, + (bool_t (*) (SVCXPRT *, xdrproc_t, caddr_t)) svctcp_rendezvous_abort, + (bool_t (*) (SVCXPRT *, struct rpc_msg *)) svctcp_rendezvous_abort, + (bool_t (*) (SVCXPRT *, xdrproc_t, caddr_t)) svctcp_rendezvous_abort, + svctcp_destroy +}; + +static int readtcp (char*, char *, int); +static int writetcp (char *, char *, int); +static SVCXPRT *makefd_xprt (int, u_int, u_int, svc_conn_destroy); + +struct tcp_rendezvous + { /* kept in xprt->xp_p1 */ + u_int sendsize; + u_int recvsize; + svc_conn_create create; + svc_conn_destroy destroy; + }; + +struct tcp_conn + { /* kept in xprt->xp_p1 */ + enum xprt_stat strm_stat; + u_long x_id; + XDR xdrs; + char verf_body[MAX_AUTH_BYTES]; + svc_conn_destroy destroy; + }; + +/* + * Usage: + * xprt = svctcp_create(sock, send_buf_size, recv_buf_size); + * + * Creates, registers, and returns a (rpc) tcp based transporter. + * Once *xprt is initialized, it is registered as a transporter + * see (svc.h, xprt_register). This routine returns + * a NULL if a problem occurred. + * + * If sock<0 then a socket is created, else sock is used. + * If the socket, sock is not bound to a port then svctcp_create + * binds it to an arbitrary port. The routine then starts a tcp + * listener on the socket's associated port. In any (successful) case, + * xprt->xp_sock is the registered socket number and xprt->xp_port is the + * associated port number. + * + * Since tcp streams do buffered io similar to stdio, the caller can specify + * how big the send and receive buffers are via the second and third parms; + * 0 => use the system default. + */ +SVCXPRT * +svctcp2_create (int sock, u_int sendsize, u_int recvsize, + svc_conn_create create, svc_conn_destroy destroy) +{ + bool_t madesock = FALSE; + SVCXPRT *xprt; + struct tcp_rendezvous *r; + struct sockaddr_in addr; + socklen_t len = sizeof (struct sockaddr_in); + + if (sock == RPC_ANYSOCK) + { + if ((sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) + { + perror ("svc_tcp.c - tcp socket creation problem"); + return (SVCXPRT *) NULL; + } + madesock = TRUE; + + bzero ((char *) &addr, sizeof (addr)); + addr.sin_family = AF_INET; + if (bindresvport (sock, &addr)) + { + addr.sin_port = 0; + (void) bind (sock, (struct sockaddr *) &addr, len); + } + if ((getsockname (sock, (struct sockaddr *) &addr, &len) != 0) || + (listen (sock, SOMAXCONN) != 0)) + { + perror ("svc_tcp.c - cannot getsockname or listen"); + if (madesock) + (void) close (sock); + return (SVCXPRT *) NULL; + } + } + r = (struct tcp_rendezvous *) mem_alloc (sizeof (*r)); + xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT)); + if (r == NULL || xprt == NULL) + { + (void) fprintf (stderr, "svctcp_create: out of memory\n"); + mem_free (r, sizeof (*r)); + mem_free (xprt, sizeof (SVCXPRT)); + return NULL; + } + r->sendsize = sendsize; + r->recvsize = recvsize; + r->create = create; + r->destroy = destroy; + xprt->xp_p2 = NULL; + xprt->xp_p1 = (caddr_t) r; + xprt->xp_verf = _null_auth; + xprt->xp_ops = &svctcp_rendezvous_op; + xprt->xp_port = ntohs (addr.sin_port); + xprt->xp_sock = sock; + xprt_register (xprt); + return xprt; +} + +#if 0 +/* + * Like svtcp_create(), except the routine takes any *open* UNIX file + * descriptor as its first input. + */ +SVCXPRT * +svcfd_create (int fd, u_int sendsize, u_int recvsize) +{ + return makefd_xprt (fd, sendsize, recvsize); +} +#endif + +static SVCXPRT * +makefd_xprt (int fd, u_int sendsize, u_int recvsize, svc_conn_destroy destroy) +{ + SVCXPRT *xprt; + struct tcp_conn *cd; + + xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT)); + cd = (struct tcp_conn *) mem_alloc (sizeof (struct tcp_conn)); + if (xprt == (SVCXPRT *) NULL || cd == NULL) + { + (void) fprintf (stderr, "svc_tcp: makefd_xprt: out of memory\n"); + mem_free (xprt, sizeof (SVCXPRT)); + mem_free (cd, sizeof (struct tcp_conn)); + return NULL; + } + cd->strm_stat = XPRT_IDLE; + cd->destroy = destroy; + xdrrec_create (&(cd->xdrs), sendsize, recvsize, + (caddr_t) xprt, readtcp, writetcp); + xprt->xp_p2 = NULL; + xprt->xp_p1 = (caddr_t) cd; + xprt->xp_verf.oa_base = cd->verf_body; + xprt->xp_addrlen = 0; + xprt->xp_ops = &svctcp_op; /* truly deals with calls */ + xprt->xp_port = 0; /* this is a connection, not a rendezvouser */ + xprt->xp_sock = fd; + xprt_register (xprt); + return xprt; +} + +static bool_t +rendezvous_request (SVCXPRT *xprt, + struct rpc_msg *errmsg __attribute__((unused))) +{ + int sock; + struct tcp_rendezvous *r; + // Actually we never use or store the returned address. The old code + // assumes an IPv4 address, provides a way to store it, and provides + // a way for the server to query it. I have commented out that code. + struct sockaddr_storage raddr; + socklen_t raddrlen; + + r = (struct tcp_rendezvous *) xprt->xp_p1; +again: + raddrlen = sizeof raddr; + if ((sock = + accept (xprt->xp_sock, (struct sockaddr *) &raddr, &raddrlen)) < 0) + { + if (errno == EINTR) + goto again; + return FALSE; + } + /* + * make a new transporter (re-uses xprt) + */ + xprt = makefd_xprt (sock, r->sendsize, r->recvsize, r->destroy); +#if 0 + memcpy (&xprt->xp_raddr, &addr, sizeof (addr)); + xprt->xp_addrlen = len; +#endif + if (r->create) (void) r->create (xprt); + return FALSE; /* there is never an rpc msg to be processed */ +} + +static enum xprt_stat +rendezvous_stat (SVCXPRT *xprt __attribute__((unused))) +{ + return XPRT_IDLE; +} + +static void +svctcp_destroy (SVCXPRT *xprt) +{ + struct tcp_conn *cd = (struct tcp_conn *) xprt->xp_p1; + + if (cd->destroy) cd->destroy (xprt); + + xprt_unregister (xprt); + (void) close (xprt->xp_sock); + if (xprt->xp_port != 0) + { + /* a rendezvouser socket */ + xprt->xp_port = 0; + } + else + { + /* an actual connection socket */ + XDR_DESTROY (&(cd->xdrs)); + } + mem_free ((caddr_t) cd, sizeof (struct tcp_conn)); + mem_free ((caddr_t) xprt, sizeof (SVCXPRT)); +} + + +/* + * reads data from the tcp connection. + * any error is fatal and the connection is closed. + * (And a read of zero bytes is a half closed stream => error.) + */ +static int +readtcp (char *xprtptr, char *buf, int len) +{ + SVCXPRT *xprt = (SVCXPRT *)xprtptr; + int sock = xprt->xp_sock; + int milliseconds = 35 * 1000; + struct pollfd pollfd; + + do + { + pollfd.fd = sock; + pollfd.events = POLLIN; + switch (poll (&pollfd, 1, milliseconds)) + { + case -1: + if (errno == EINTR) + continue; + /*FALLTHROUGH*/ + case 0: + goto fatal_err; + default: + if ((pollfd.revents & POLLERR) || (pollfd.revents & POLLHUP) + || (pollfd.revents & POLLNVAL)) + goto fatal_err; + break; + } + } + while ((pollfd.revents & POLLIN) == 0); + + if ((len = read (sock, buf, len)) > 0) + return len; + + fatal_err: + ((struct tcp_conn *) (xprt->xp_p1))->strm_stat = XPRT_DIED; + return -1; +} + +/* + * writes data to the tcp connection. + * Any error is fatal and the connection is closed. + */ +static int +writetcp (char *xprtptr, char * buf, int len) +{ + SVCXPRT *xprt = (SVCXPRT *)xprtptr; + int i, cnt; + + for (cnt = len; cnt > 0; cnt -= i, buf += i) + { + if ((i = write (xprt->xp_sock, buf, cnt)) < 0) + { + ((struct tcp_conn *) (xprt->xp_p1))->strm_stat = XPRT_DIED; + return -1; + } + } + return len; +} + +static enum xprt_stat +svctcp_stat (SVCXPRT *xprt) +{ + struct tcp_conn *cd = + (struct tcp_conn *) (xprt->xp_p1); + + if (cd->strm_stat == XPRT_DIED) + return XPRT_DIED; + if (!xdrrec_eof (&(cd->xdrs))) + return XPRT_MOREREQS; + return XPRT_IDLE; +} + +static bool_t +svctcp_recv (SVCXPRT *xprt, struct rpc_msg *msg) +{ + struct tcp_conn *cd = (struct tcp_conn *) (xprt->xp_p1); + XDR *xdrs = &(cd->xdrs); + + xdrs->x_op = XDR_DECODE; + (void) xdrrec_skiprecord (xdrs); + if (xdr_callmsg (xdrs, msg)) + { + cd->x_id = msg->rm_xid; + return TRUE; + } + cd->strm_stat = XPRT_DIED; /* XXXX */ + return FALSE; +} + +static bool_t +svctcp_getargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr) +{ + return ((*xdr_args) (&(((struct tcp_conn *) + (xprt->xp_p1))->xdrs), args_ptr)); +} + +static bool_t +svctcp_freeargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr) +{ + XDR *xdrs = &(((struct tcp_conn *) (xprt->xp_p1))->xdrs); + + xdrs->x_op = XDR_FREE; + return ((*xdr_args) (xdrs, args_ptr)); +} + +static bool_t +svctcp_reply (SVCXPRT *xprt, struct rpc_msg *msg) +{ + struct tcp_conn *cd = (struct tcp_conn *) (xprt->xp_p1); + XDR *xdrs = &(cd->xdrs); + bool_t stat; + + xdrs->x_op = XDR_ENCODE; + msg->rm_xid = cd->x_id; + stat = xdr_replymsg (xdrs, msg); + (void) xdrrec_endofrecord (xdrs, TRUE); + return stat; +} diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/svc_tcp2.h libvirt-oldremote/src/sunrpc/svc_tcp2.h --- libvirt-cvs/src/sunrpc/svc_tcp2.h 1970-01-01 01:00:00.000000000 +0100 +++ libvirt-oldremote/src/sunrpc/svc_tcp2.h 2007-02-26 14:19:05.000000000 +0000 @@ -0,0 +1,20 @@ +/* + * svc_tcp2.h: Interface to the SunRPC over TCP server. + * + * Copyright (C) 2007 Red Hat, Inc. + * + * See COPYING.LIB for the license of this software. + * + * Richard Jones <rjones@xxxxxxxxxx> + */ + +#ifndef __SVC_TCP2_H__ +#define __SVC_TCP2_H__ + +#include "svc_connections.h" + +extern SVCXPRT *svctcp2_create (int sock, u_int sendsize, u_int recvsize, + svc_conn_create create, + svc_conn_destroy destroy); + +#endif /* __SVC_TCP2_H__ */ diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/svc_unix2.c libvirt-oldremote/src/sunrpc/svc_unix2.c --- libvirt-cvs/src/sunrpc/svc_unix2.c 1970-01-01 01:00:00.000000000 +0100 +++ libvirt-oldremote/src/sunrpc/svc_unix2.c 2007-02-26 14:43:23.000000000 +0000 @@ -0,0 +1,542 @@ +/* + * svc_unix2.c: SunRPC Unix domain socket server. This is a very + * slightly modified version of svc_unix.c from glibc which adds + * support for connections. + * + * Modifications from glibc-2.5 base by Richard Jones <rjones@xxxxxxxxxx>. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * svc_unix.c, Server side for TCP/IP based RPC. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * Actually implements two flavors of transporter - + * a unix rendezvouser (a listener and connection establisher) + * and a record/unix stream. + */ + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <rpc/rpc.h> +#include <rpc/svc.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <sys/poll.h> +#include <errno.h> +#include <stdlib.h> +#include <libintl.h> + +#ifdef USE_IN_LIBIO +# include <wchar.h> +#endif + +#include "svc_unix2.h" + +/* + * Ops vector for AF_UNIX based rpc service handle + */ +static bool_t svcunix_recv (SVCXPRT *, struct rpc_msg *); +static enum xprt_stat svcunix_stat (SVCXPRT *); +static bool_t svcunix_getargs (SVCXPRT *, xdrproc_t, caddr_t); +static bool_t svcunix_reply (SVCXPRT *, struct rpc_msg *); +static bool_t svcunix_freeargs (SVCXPRT *, xdrproc_t, caddr_t); +static void svcunix_destroy (SVCXPRT *); + +static const struct xp_ops svcunix_op = +{ + svcunix_recv, + svcunix_stat, + svcunix_getargs, + svcunix_reply, + svcunix_freeargs, + svcunix_destroy +}; + +/* + * Ops vector for AF_UNIX rendezvous handler + */ +static bool_t rendezvous_request (SVCXPRT *, struct rpc_msg *); +static enum xprt_stat rendezvous_stat (SVCXPRT *); +static void svcunix_rendezvous_abort (void) __attribute__ ((__noreturn__)); + +/* This function makes sure abort() relocation goes through PLT + and thus can be lazy bound. */ +static void +svcunix_rendezvous_abort (void) +{ + abort (); +}; + +static const struct xp_ops svcunix_rendezvous_op = +{ + rendezvous_request, + rendezvous_stat, + (bool_t (*) (SVCXPRT *, xdrproc_t, caddr_t)) svcunix_rendezvous_abort, + (bool_t (*) (SVCXPRT *, struct rpc_msg *)) svcunix_rendezvous_abort, + (bool_t (*) (SVCXPRT *, xdrproc_t, caddr_t)) svcunix_rendezvous_abort, + svcunix_destroy +}; + +static int readunix (char*, char *, int); +static int writeunix (char *, char *, int); +static SVCXPRT *makefd_xprt (int, u_int, u_int, svc_conn_destroy); + +struct unix_rendezvous { /* kept in xprt->xp_p1 */ + u_int sendsize; + u_int recvsize; + svc_conn_create create; + svc_conn_destroy destroy; +}; + +struct unix_conn { /* kept in xprt->xp_p1 */ + enum xprt_stat strm_stat; + u_long x_id; + XDR xdrs; + char verf_body[MAX_AUTH_BYTES]; + svc_conn_destroy destroy; +}; + +/* + * Usage: + * xprt = svcunix_create(sock, send_buf_size, recv_buf_size); + * + * Creates, registers, and returns a (rpc) unix based transporter. + * Once *xprt is initialized, it is registered as a transporter + * see (svc.h, xprt_register). This routine returns + * a NULL if a problem occurred. + * + * If sock<0 then a socket is created, else sock is used. + * If the socket, sock is not bound to a port then svcunix_create + * binds it to an arbitrary port. The routine then starts a unix + * listener on the socket's associated port. In any (successful) case, + * xprt->xp_sock is the registered socket number and xprt->xp_port is the + * associated port number. + * + * Since unix streams do buffered io similar to stdio, the caller can specify + * how big the send and receive buffers are via the second and third parms; + * 0 => use the system default. + */ +SVCXPRT * +svcunix2_create (int sock, u_int sendsize, u_int recvsize, + svc_conn_create create, svc_conn_destroy destroy, + char *path) +{ + bool_t madesock = FALSE; + SVCXPRT *xprt; + struct unix_rendezvous *r; + struct sockaddr_un addr; + socklen_t len = sizeof (struct sockaddr_in); + + if (sock == RPC_ANYSOCK) + { + if ((sock = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) + { + perror ("svc_unix.c - AF_UNIX socket creation problem"); + return (SVCXPRT *) NULL; + } + madesock = TRUE; + } + memset (&addr, '\0', sizeof (addr)); + addr.sun_family = AF_UNIX; + len = strlen (path) + 1; + memcpy (addr.sun_path, path, len); + len += sizeof (addr.sun_family); + + bind (sock, (struct sockaddr *) &addr, len); + + if (getsockname (sock, (struct sockaddr *) &addr, &len) != 0 + || listen (sock, SOMAXCONN) != 0) + { + perror ("svc_unix.c - cannot getsockname or listen"); + if (madesock) + close (sock); + return (SVCXPRT *) NULL; + } + + r = (struct unix_rendezvous *) mem_alloc (sizeof (*r)); + xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT)); + if (r == NULL || xprt == NULL) + { + fprintf (stderr, "svcunix_create: out of memory\n"); + mem_free (r, sizeof (*r)); + mem_free (xprt, sizeof (SVCXPRT)); + return NULL; + } + r->sendsize = sendsize; + r->recvsize = recvsize; + r->create = create; + r->destroy = destroy; + xprt->xp_p2 = NULL; + xprt->xp_p1 = (caddr_t) r; + xprt->xp_verf = _null_auth; + xprt->xp_ops = &svcunix_rendezvous_op; + xprt->xp_port = -1; + xprt->xp_sock = sock; + xprt_register (xprt); + return xprt; +} + +#if 0 +/* + * Like svunix_create(), except the routine takes any *open* UNIX file + * descriptor as its first input. + */ +SVCXPRT * +svcunixfd_create (int fd, u_int sendsize, u_int recvsize) +{ + return makefd_xprt (fd, sendsize, recvsize); +} +#endif + +static SVCXPRT * +makefd_xprt (int fd, u_int sendsize, u_int recvsize, svc_conn_destroy destroy) +{ + SVCXPRT *xprt; + struct unix_conn *cd; + + xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT)); + cd = (struct unix_conn *) mem_alloc (sizeof (struct unix_conn)); + if (xprt == (SVCXPRT *) NULL || cd == (struct unix_conn *) NULL) + { + fprintf (stderr, "svc_unix: makefd_xprt: out of memory\n"); + mem_free (xprt, sizeof (SVCXPRT)); + mem_free (cd, sizeof (struct unix_conn)); + return NULL; + } + cd->strm_stat = XPRT_IDLE; + cd->destroy = destroy; + xdrrec_create (&(cd->xdrs), sendsize, recvsize, + (caddr_t) xprt, readunix, writeunix); + xprt->xp_p2 = NULL; + xprt->xp_p1 = (caddr_t) cd; + xprt->xp_verf.oa_base = cd->verf_body; + xprt->xp_addrlen = 0; + xprt->xp_ops = &svcunix_op; /* truly deals with calls */ + xprt->xp_port = 0; /* this is a connection, not a rendezvouser */ + xprt->xp_sock = fd; + xprt_register (xprt); + return xprt; +} + +static bool_t +rendezvous_request (SVCXPRT *xprt __attribute__((unused)), + struct rpc_msg *errmsg __attribute__((unused))) +{ + int sock; + struct unix_rendezvous *r; + struct sockaddr_un addr; + struct sockaddr_in in_addr; + socklen_t len; + + r = (struct unix_rendezvous *) xprt->xp_p1; +again: + len = sizeof (struct sockaddr_un); + if ((sock = accept (xprt->xp_sock, (struct sockaddr *) &addr, &len)) < 0) + { + if (errno == EINTR) + goto again; + return FALSE; + } + /* + * make a new transporter (re-uses xprt) + */ + memset (&in_addr, '\0', sizeof (in_addr)); + in_addr.sin_family = AF_UNIX; + xprt = makefd_xprt (sock, r->sendsize, r->recvsize, r->destroy); + memcpy (&xprt->xp_raddr, &in_addr, sizeof (in_addr)); + xprt->xp_addrlen = len; + if (r->create) (void) r->create (xprt); + return FALSE; /* there is never an rpc msg to be processed */ +} + +static enum xprt_stat +rendezvous_stat (SVCXPRT *xprt __attribute__((unused))) +{ + return XPRT_IDLE; +} + +static void +svcunix_destroy (SVCXPRT *xprt) +{ + struct unix_conn *cd = (struct unix_conn *) xprt->xp_p1; + + if (cd->destroy) cd->destroy (xprt); + + xprt_unregister (xprt); + close (xprt->xp_sock); + if (xprt->xp_port != 0) + { + /* a rendezvouser socket */ + xprt->xp_port = 0; + } + else + { + /* an actual connection socket */ + XDR_DESTROY (&(cd->xdrs)); + } + mem_free ((caddr_t) cd, sizeof (struct unix_conn)); + mem_free ((caddr_t) xprt, sizeof (SVCXPRT)); +} + +#ifdef SCM_CREDENTIALS +struct cmessage { + struct cmsghdr cmsg; + struct ucred cmcred; + /* hack to make sure we have enough memory */ + char dummy[(CMSG_ALIGN (sizeof (struct ucred)) - sizeof (struct ucred) + sizeof (long))]; +}; + +/* XXX This is not thread safe, but since the main functions in svc.c + and the rpcgen generated *_svc functions for the daemon are also not + thread safe and uses static global variables, it doesn't matter. */ +static struct cmessage cm; +#endif + +static int +__msgread (int sock, void *data, size_t cnt) +{ + struct iovec iov; + struct msghdr msg; + int len; + + iov.iov_base = data; + iov.iov_len = cnt; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_name = NULL; + msg.msg_namelen = 0; +#ifdef SCM_CREDENTIALS + msg.msg_control = (caddr_t) &cm; + msg.msg_controllen = sizeof (struct cmessage); +#endif + msg.msg_flags = 0; + +#ifdef SO_PASSCRED + { + int on = 1; + if (setsockopt (sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof (on))) + return -1; + } +#endif + + restart: + len = recvmsg (sock, &msg, 0); + if (len >= 0) + { + if (msg.msg_flags & MSG_CTRUNC || len == 0) + return 0; + else + return len; + } + if (errno == EINTR) + goto restart; + return -1; +} + +static int +__msgwrite (int sock, void *data, size_t cnt) +{ +#ifndef SCM_CREDENTIALS + /* We cannot implement this reliably. */ + __set_errno (ENOSYS); + return -1; +#else + struct iovec iov; + struct msghdr msg; + struct cmsghdr *cmsg = &cm.cmsg; + struct ucred cred; + int len; + + /* XXX I'm not sure, if gete?id() is always correct, or if we should use + get?id(). But since keyserv needs geteuid(), we have no other chance. + It would be much better, if the kernel could pass both to the server. */ + cred.pid = getpid (); + cred.uid = geteuid (); + cred.gid = getegid (); + + memcpy (CMSG_DATA(cmsg), &cred, sizeof (struct ucred)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDENTIALS; + cmsg->cmsg_len = sizeof(*cmsg) + sizeof(struct ucred); + + iov.iov_base = data; + iov.iov_len = cnt; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_control = cmsg; + msg.msg_controllen = CMSG_ALIGN(cmsg->cmsg_len); + msg.msg_flags = 0; + + restart: + len = sendmsg (sock, &msg, 0); + if (len >= 0) + return len; + if (errno == EINTR) + goto restart; + return -1; + +#endif +} + +/* + * reads data from the unix connection. + * any error is fatal and the connection is closed. + * (And a read of zero bytes is a half closed stream => error.) + */ +static int +readunix (char *xprtptr, char *buf, int len) +{ + SVCXPRT *xprt = (SVCXPRT *) xprtptr; + int sock = xprt->xp_sock; + int milliseconds = 35 * 1000; + struct pollfd pollfd; + + do + { + pollfd.fd = sock; + pollfd.events = POLLIN; + switch (poll (&pollfd, 1, milliseconds)) + { + case -1: + if (errno == EINTR) + continue; + /*FALLTHROUGH*/ + case 0: + goto fatal_err; + default: + if ((pollfd.revents & POLLERR) || (pollfd.revents & POLLHUP) + || (pollfd.revents & POLLNVAL)) + goto fatal_err; + break; + } + } + while ((pollfd.revents & POLLIN) == 0); + + if ((len = __msgread (sock, buf, len)) > 0) + return len; + + fatal_err: + ((struct unix_conn *) (xprt->xp_p1))->strm_stat = XPRT_DIED; + return -1; +} + +/* + * writes data to the unix connection. + * Any error is fatal and the connection is closed. + */ +static int +writeunix (char *xprtptr, char * buf, int len) +{ + SVCXPRT *xprt = (SVCXPRT *) xprtptr; + int i, cnt; + + for (cnt = len; cnt > 0; cnt -= i, buf += i) + { + if ((i = __msgwrite (xprt->xp_sock, buf, cnt)) < 0) + { + ((struct unix_conn *) (xprt->xp_p1))->strm_stat = XPRT_DIED; + return -1; + } + } + return len; +} + +static enum xprt_stat +svcunix_stat (SVCXPRT *xprt) +{ + struct unix_conn *cd = + (struct unix_conn *) (xprt->xp_p1); + + if (cd->strm_stat == XPRT_DIED) + return XPRT_DIED; + if (!xdrrec_eof (&(cd->xdrs))) + return XPRT_MOREREQS; + return XPRT_IDLE; +} + +static bool_t +svcunix_recv (SVCXPRT *xprt, struct rpc_msg *msg) +{ + struct unix_conn *cd = (struct unix_conn *) (xprt->xp_p1); + XDR *xdrs = &(cd->xdrs); + + xdrs->x_op = XDR_DECODE; + xdrrec_skiprecord (xdrs); + if (xdr_callmsg (xdrs, msg)) + { + cd->x_id = msg->rm_xid; + /* set up verifiers */ +#ifdef SCM_CREDENTIALS + msg->rm_call.cb_verf.oa_flavor = AUTH_UNIX; + msg->rm_call.cb_verf.oa_base = (caddr_t) &cm; + msg->rm_call.cb_verf.oa_length = sizeof (cm); +#endif + return TRUE; + } + cd->strm_stat = XPRT_DIED; /* XXXX */ + return FALSE; +} + +static bool_t +svcunix_getargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr) +{ + return (*xdr_args) (&(((struct unix_conn *) (xprt->xp_p1))->xdrs), + args_ptr); +} + +static bool_t +svcunix_freeargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr) +{ + XDR *xdrs = &(((struct unix_conn *) (xprt->xp_p1))->xdrs); + + xdrs->x_op = XDR_FREE; + return (*xdr_args) (xdrs, args_ptr); +} + +static bool_t +svcunix_reply (SVCXPRT *xprt, struct rpc_msg *msg) +{ + struct unix_conn *cd = (struct unix_conn *) (xprt->xp_p1); + XDR *xdrs = &(cd->xdrs); + bool_t stat; + + xdrs->x_op = XDR_ENCODE; + msg->rm_xid = cd->x_id; + stat = xdr_replymsg (xdrs, msg); + (void) xdrrec_endofrecord (xdrs, TRUE); + return stat; +} diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/svc_unix2.h libvirt-oldremote/src/sunrpc/svc_unix2.h --- libvirt-cvs/src/sunrpc/svc_unix2.h 1970-01-01 01:00:00.000000000 +0100 +++ libvirt-oldremote/src/sunrpc/svc_unix2.h 2007-02-26 14:27:23.000000000 +0000 @@ -0,0 +1,21 @@ +/* + * svc_unix2.h: Interface to the SunRPC over Unix domain sockets server. + * + * Copyright (C) 2007 Red Hat, Inc. + * + * See COPYING.LIB for the license of this software. + * + * Richard Jones <rjones@xxxxxxxxxx> + */ + +#ifndef __SVC_UNIX2_H__ +#define __SVC_UNIX2_H__ + +#include "svc_connections.h" + +extern SVCXPRT *svcunix2_create (int sock, u_int sendsize, u_int recvsize, + svc_conn_create create, + svc_conn_destroy destroy, + char *path); + +#endif /* __SVC_UNIX2_H__ */ diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/virterror.c libvirt-oldremote/src/virterror.c --- libvirt-cvs/src/virterror.c 2007-03-16 11:04:46.000000000 +0000 +++ libvirt-oldremote/src/virterror.c 2007-04-04 12:54:06.000000000 +0100 @@ -274,6 +274,9 @@ case VIR_FROM_NET: dom = "Network "; break; + case VIR_FROM_REMOTE: + dom = "Remote "; + break; } if ((err->dom != NULL) && (err->code != VIR_ERR_INVALID_DOMAIN)) { domain = err->dom->name; @@ -610,6 +613,18 @@ else errmsg = "%s"; break; + case VIR_ERR_RPC: + if (info == NULL) + errmsg = _("RPC error"); + else + errmsg = "%s"; + break; + case VIR_ERR_RPC_BAD_CONNECTION: + if (info == NULL) + errmsg = _("RPC bad connection"); + else + errmsg = "%s"; + break; } return (errmsg); }
Attachment:
smime.p7s
Description: S/MIME Cryptographic Signature