diff --git a/docs/apibuild.py b/docs/apibuild.py
index 6fec049..b2c918e 100755
--- a/docs/apibuild.py
+++ b/docs/apibuild.py
@@ -39,6 +39,7 @@ ignored_functions = {
   "virDomainMigratePrepare": "private function for migration",
   "virDomainMigratePrepare2": "private function for migration",
   "virDrvSupportsFeature": "private function for remote access",
+  "virConnectSecureMigrationData": "private function for secure migration",
   "DllMain": "specific function fo Win32",
 }
 
diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h
index 30f559d..b9362d5 100644
--- a/include/libvirt/libvirt.h
+++ b/include/libvirt/libvirt.h
@@ -319,6 +319,7 @@ typedef virDomainInterfaceStatsStruct *virDomainInterfaceStatsPtr;
 /* Domain migration flags. */
 typedef enum {
   VIR_MIGRATE_LIVE              = 1, /* live migration */
+  VIR_MIGRATE_SECURE            = 2, /* secure migration */
 } virDomainMigrateFlags;
 
 /* Domain migration. */
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index 2f7076f..94e3995 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -319,6 +319,7 @@ typedef virDomainInterfaceStatsStruct *virDomainInterfaceStatsPtr;
 /* Domain migration flags. */
 typedef enum {
   VIR_MIGRATE_LIVE              = 1, /* live migration */
+  VIR_MIGRATE_SECURE            = 2, /* secure migration */
 } virDomainMigrateFlags;
 
 /* Domain migration. */
diff --git a/qemud/remote.c b/qemud/remote.c
index 8d24a3a..5228550 100644
--- a/qemud/remote.c
+++ b/qemud/remote.c
@@ -710,6 +710,40 @@ remoteDispatchNodeGetFreeMemory (struct qemud_server *server ATTRIBUTE_UNUSED,
     return 0;
 }
 
+static int
+remoteDispatchSecureMigrationData (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                   struct qemud_client *client ATTRIBUTE_UNUSED,
+                                   virConnectPtr conn,
+                                   remote_error *rerr,
+                                   remote_secure_migration_data_args *args,
+                                   void *ret ATTRIBUTE_UNUSED)
+{
+    int r;
+
+    if (args->cookie.cookie_len > REMOTE_CONNECT_SECURE_MIGRATION_COOKIE_MAX) {
+        remoteDispatchFormatError (rerr, "%s",
+                                   _("cookie_len > REMOTE_CONNECT_SECURE_MIGRATION_COOKIE_MAX"));
+        return -1;
+    }
+
+    if (args->buffer.buffer_len > REMOTE_CONNECT_SECURE_MIGRATION_DATA_MAX) {
+        remoteDispatchFormatError (rerr, "%s",
+                                   _("buffer_len > REMOTE_CONNECT_SECURE_MIGRATION_DATA_MAX"));
+        return -1;
+    }
+
+    r = virConnectSecureMigrationData(conn, args->cookie.cookie_val,
+                                      args->cookie.cookie_len,
+                                      args->buffer.buffer_val,
+                                      args->buffer.buffer_len);
+
+    if (r < 0) {
+        remoteDispatchConnError(rerr, conn);
+        return -1;
+    }
+
+    return 0;
+}
 
 static int
 remoteDispatchDomainGetSchedulerType (struct qemud_server *server ATTRIBUTE_UNUSED,
diff --git a/qemud/remote_dispatch_args.h b/qemud/remote_dispatch_args.h
index 58eccf5..8ba3a3a 100644
--- a/qemud/remote_dispatch_args.h
+++ b/qemud/remote_dispatch_args.h
@@ -105,3 +105,4 @@
     remote_domain_get_security_label_args val_remote_domain_get_security_label_args;
     remote_node_device_create_xml_args val_remote_node_device_create_xml_args;
     remote_node_device_destroy_args val_remote_node_device_destroy_args;
+    remote_secure_migration_data_args val_remote_secure_migration_data_args;
diff --git a/qemud/remote_dispatch_prototypes.h b/qemud/remote_dispatch_prototypes.h
index 96dcb2a..f54ec21 100644
--- a/qemud/remote_dispatch_prototypes.h
+++ b/qemud/remote_dispatch_prototypes.h
@@ -688,6 +688,13 @@ static int remoteDispatchOpen(
     remote_error *err,
     remote_open_args *args,
     void *ret);
+static int remoteDispatchSecureMigrationData(
+    struct qemud_server *server,
+    struct qemud_client *client,
+    virConnectPtr conn,
+    remote_error *err,
+    remote_secure_migration_data_args *args,
+    void *ret);
 static int remoteDispatchStoragePoolBuild(
     struct qemud_server *server,
     struct qemud_client *client,
diff --git a/qemud/remote_dispatch_table.h b/qemud/remote_dispatch_table.h
index ac7f9b9..cd62290 100644
--- a/qemud/remote_dispatch_table.h
+++ b/qemud/remote_dispatch_table.h
@@ -627,3 +627,8 @@
     .args_filter = (xdrproc_t) xdr_remote_node_device_destroy_args,
     .ret_filter = (xdrproc_t) xdr_void,
 },
+{   /* SecureMigrationData => 125 */
+    .fn = (dispatch_fn) remoteDispatchSecureMigrationData,
+    .args_filter = (xdrproc_t) xdr_remote_secure_migration_data_args,
+    .ret_filter = (xdrproc_t) xdr_void,
+},
diff --git a/qemud/remote_protocol.c b/qemud/remote_protocol.c
index af3c792..87d9dcf 100644
--- a/qemud/remote_protocol.c
+++ b/qemud/remote_protocol.c
@@ -467,6 +467,19 @@ xdr_remote_domain_get_scheduler_type_ret (XDR *xdrs, remote_domain_get_scheduler
 }
 
 bool_t
+xdr_remote_secure_migration_data_args (XDR *xdrs, remote_secure_migration_data_args *objp)
+{
+        char **objp_cpp0 = (char **) (void *) &objp->cookie.cookie_val;
+        char **objp_cpp1 = (char **) (void *) &objp->buffer.buffer_val;
+
+         if (!xdr_bytes (xdrs, objp_cpp0, (u_int *) &objp->cookie.cookie_len, REMOTE_CONNECT_SECURE_MIGRATION_COOKIE_MAX))
+                 return FALSE;
+         if (!xdr_bytes (xdrs, objp_cpp1, (u_int *) &objp->buffer.buffer_len, REMOTE_CONNECT_SECURE_MIGRATION_DATA_MAX))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
 xdr_remote_domain_get_scheduler_parameters_args (XDR *xdrs, remote_domain_get_scheduler_parameters_args *objp)
 {
 
diff --git a/qemud/remote_protocol.h b/qemud/remote_protocol.h
index 9d67e58..e5e3d8b 100644
--- a/qemud/remote_protocol.h
+++ b/qemud/remote_protocol.h
@@ -41,6 +41,8 @@ typedef remote_nonnull_string *remote_string;
 #define REMOTE_SECURITY_MODEL_MAX VIR_SECURITY_MODEL_BUFLEN
 #define REMOTE_SECURITY_LABEL_MAX VIR_SECURITY_LABEL_BUFLEN
 #define REMOTE_SECURITY_DOI_MAX VIR_SECURITY_DOI_BUFLEN
+#define REMOTE_CONNECT_SECURE_MIGRATION_DATA_MAX 65536
+#define REMOTE_CONNECT_SECURE_MIGRATION_COOKIE_MAX 65536
 
 typedef char remote_uuid[VIR_UUID_BUFLEN];
 
@@ -227,6 +229,18 @@ struct remote_domain_get_scheduler_type_ret {
 };
 typedef struct remote_domain_get_scheduler_type_ret remote_domain_get_scheduler_type_ret;
 
+struct remote_secure_migration_data_args {
+        struct {
+                u_int cookie_len;
+                char *cookie_val;
+        } cookie;
+        struct {
+                u_int buffer_len;
+                char *buffer_val;
+        } buffer;
+};
+typedef struct remote_secure_migration_data_args remote_secure_migration_data_args;
+
 struct remote_domain_get_scheduler_parameters_args {
         remote_nonnull_domain dom;
         int nparams;
@@ -1415,6 +1429,7 @@ enum remote_procedure {
         REMOTE_PROC_NODE_GET_SECURITY_MODEL = 122,
         REMOTE_PROC_NODE_DEVICE_CREATE_XML = 123,
         REMOTE_PROC_NODE_DEVICE_DESTROY = 124,
+        REMOTE_PROC_SECURE_MIGRATION_DATA = 125,
 };
 typedef enum remote_procedure remote_procedure;
 
@@ -1479,6 +1494,7 @@ extern  bool_t xdr_remote_node_get_cells_free_memory_ret (XDR *, remote_node_get
 extern  bool_t xdr_remote_node_get_free_memory_ret (XDR *, remote_node_get_free_memory_ret*);
 extern  bool_t xdr_remote_domain_get_scheduler_type_args (XDR *, remote_domain_get_scheduler_type_args*);
 extern  bool_t xdr_remote_domain_get_scheduler_type_ret (XDR *, remote_domain_get_scheduler_type_ret*);
+extern  bool_t xdr_remote_secure_migration_data_args (XDR *, remote_secure_migration_data_args*);
 extern  bool_t xdr_remote_domain_get_scheduler_parameters_args (XDR *, remote_domain_get_scheduler_parameters_args*);
 extern  bool_t xdr_remote_domain_get_scheduler_parameters_ret (XDR *, remote_domain_get_scheduler_parameters_ret*);
 extern  bool_t xdr_remote_domain_set_scheduler_parameters_args (XDR *, remote_domain_set_scheduler_parameters_args*);
@@ -1693,6 +1709,7 @@ extern bool_t xdr_remote_node_get_cells_free_memory_ret ();
 extern bool_t xdr_remote_node_get_free_memory_ret ();
 extern bool_t xdr_remote_domain_get_scheduler_type_args ();
 extern bool_t xdr_remote_domain_get_scheduler_type_ret ();
+extern bool_t xdr_remote_secure_migration_data_args ();
 extern bool_t xdr_remote_domain_get_scheduler_parameters_args ();
 extern bool_t xdr_remote_domain_get_scheduler_parameters_ret ();
 extern bool_t xdr_remote_domain_set_scheduler_parameters_args ();
diff --git a/qemud/remote_protocol.x b/qemud/remote_protocol.x
index 2c79949..6ba03da 100644
--- a/qemud/remote_protocol.x
+++ b/qemud/remote_protocol.x
@@ -130,6 +130,9 @@ const REMOTE_SECURITY_LABEL_MAX = VIR_SECURITY_LABEL_BUFLEN;
  */
 const REMOTE_SECURITY_DOI_MAX = VIR_SECURITY_DOI_BUFLEN;
 
+const REMOTE_CONNECT_SECURE_MIGRATION_DATA_MAX = 65536;
+const REMOTE_CONNECT_SECURE_MIGRATION_COOKIE_MAX = 65536;
+
 /* UUID.  VIR_UUID_BUFLEN definition comes from libvirt.h */
 typedef opaque remote_uuid[VIR_UUID_BUFLEN];
 
@@ -323,6 +326,11 @@ struct remote_domain_get_scheduler_type_ret {
     int nparams;
 };
 
+struct remote_secure_migration_data_args {
+    opaque cookie<REMOTE_CONNECT_SECURE_MIGRATION_COOKIE_MAX>;
+    opaque buffer<REMOTE_CONNECT_SECURE_MIGRATION_DATA_MAX>;
+};
+
 struct remote_domain_get_scheduler_parameters_args {
     remote_nonnull_domain dom;
     int nparams;
@@ -1286,7 +1294,9 @@ enum remote_procedure {
     REMOTE_PROC_NODE_GET_SECURITY_MODEL = 122,
 
     REMOTE_PROC_NODE_DEVICE_CREATE_XML = 123,
-    REMOTE_PROC_NODE_DEVICE_DESTROY = 124
+    REMOTE_PROC_NODE_DEVICE_DESTROY = 124,
+
+    REMOTE_PROC_SECURE_MIGRATION_DATA = 125
 };
 
 /* Custom RPC structure. */
diff --git a/src/driver.h b/src/driver.h
index c357b76..7a90201 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -331,6 +331,15 @@ typedef int
     (*virDrvNodeDeviceReset)
                     (virNodeDevicePtr dev);
 
+
+typedef int
+    (*virDrvSecureMigrationData)
+                    (virConnectPtr domain,
+                     const char *cookie,
+                     int cookielen,
+                     char *data,
+                     int datalen);
+
 /**
  * _virDriver:
  *
@@ -409,6 +418,7 @@ struct _virDriver {
     virDrvNodeDeviceDettach     nodeDeviceDettach;
     virDrvNodeDeviceReAttach    nodeDeviceReAttach;
     virDrvNodeDeviceReset       nodeDeviceReset;
+    virDrvSecureMigrationData secureMigrationData;
 };
 
 typedef int
diff --git a/src/libvirt.c b/src/libvirt.c
index ded18a7..79b4fc7 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -2661,6 +2661,7 @@ error:
  *
  * Flags may be one of more of the following:
  *   VIR_MIGRATE_LIVE   Attempt a live migration.
+ *   VIR_MIGRATE_SECURE Attempt to do a secure migration
  *
  * If a hypervisor supports renaming domains during migration,
  * then you may set the dname parameter to the new name (otherwise
@@ -2734,6 +2735,13 @@ virDomainMigrate (virDomainPtr domain,
         goto error;
     }
 
+    if ((flags & VIR_MIGRATE_SECURE) && uri == NULL) {
+        /* if you are doing a secure migration, you *must* also pass a uri */
+        virLibConnError(conn, VIR_ERR_INVALID_ARG,
+                        _("requested SECURE migration, but no URI passed"));
+        goto error;
+    }
+
     /* Check that migration is supported by both drivers. */
     if (VIR_DRV_SUPPORTS_FEATURE (conn->driver, conn,
                                   VIR_DRV_FEATURE_MIGRATION_V1) &&
@@ -2851,13 +2859,13 @@ error:
  */
 int
 virDomainMigratePrepare (virConnectPtr dconn,
-                           char **cookie,
-                           int *cookielen,
-                           const char *uri_in,
-                           char **uri_out,
-                           unsigned long flags,
-                           const char *dname,
-                           unsigned long bandwidth)
+                         char **cookie,
+                         int *cookielen,
+                         const char *uri_in,
+                         char **uri_out,
+                         unsigned long flags,
+                         const char *dname,
+                         unsigned long bandwidth)
 {
     VIR_DEBUG("dconn=%p, cookie=%p, cookielen=%p, uri_in=%s, uri_out=%p, "
               "flags=%lu, dname=%s, bandwidth=%lu", dconn, cookie, cookielen,
@@ -2895,6 +2903,39 @@ error:
 
 /*
  * Not for public use.  This function is part of the internal
+ * implementation of secure migration in the remote case.
+ */
+int virConnectSecureMigrationData(virConnectPtr conn,
+                                  const char *cookie,
+                                  int cookielen,
+                                  char *data,
+                                  int datalen)
+{
+    virResetLastError();
+
+    if (conn->flags & VIR_CONNECT_RO) {
+        virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__);
+        goto error;
+    }
+
+    if (conn->driver->secureMigrationData) {
+        int ret;
+        ret = conn->driver->secureMigrationData (conn, cookie, cookielen, data, datalen);
+        if (ret < 0)
+            goto error;
+        return ret;
+    }
+
+    virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+
+error:
+    /* Copy to connection error object for back compatability */
+    virSetConnError(conn);
+    return -1;
+}
+
+/*
+ * Not for public use.  This function is part of the internal
  * implementation of migration in the remote case.
  */
 int
diff --git a/src/libvirt_internal.h b/src/libvirt_internal.h
index 44145d4..61ef508 100644
--- a/src/libvirt_internal.h
+++ b/src/libvirt_internal.h
@@ -72,5 +72,10 @@ virDomainPtr virDomainMigrateFinish2 (virConnectPtr dconn,
                                       unsigned long flags,
                                       int retcode);
 
+int virConnectSecureMigrationData(virConnectPtr conn,
+                                  const char *cookie,
+                                  int cookielen,
+                                  char *data,
+                                  int datalen);
 
 #endif
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index ce63869..6c01566 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -182,6 +182,7 @@ virDomainMigratePerform;
 virDomainMigrateFinish;
 virDomainMigratePrepare2;
 virDomainMigrateFinish2;
+virConnectSecureMigrationData;
 virRegisterDriver;
 virRegisterNetworkDriver;
 virRegisterStateDriver;
diff --git a/src/lxc_driver.c b/src/lxc_driver.c
index 5392af1..5aa9cea 100644
--- a/src/lxc_driver.c
+++ b/src/lxc_driver.c
@@ -1485,6 +1485,7 @@ static virDriver lxcDriver = {
     NULL, /* nodeDeviceDettach */
     NULL, /* nodeDeviceReAttach */
     NULL, /* nodeDeviceReset */
+    NULL, /* secureMigrationData */
 };
 
 static virStateDriver lxcStateDriver = {
diff --git a/src/openvz_driver.c b/src/openvz_driver.c
index 755e449..db46143 100644
--- a/src/openvz_driver.c
+++ b/src/openvz_driver.c
@@ -1372,6 +1372,7 @@ static virDriver openvzDriver = {
     NULL, /* nodeDeviceDettach */
     NULL, /* nodeDeviceReAttach */
     NULL, /* nodeDeviceReset */
+    NULL, /* secureMigrationData */
 };
 
 int openvzRegister(void) {
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
index bd60b29..f608e36 100644
--- a/src/qemu_driver.c
+++ b/src/qemu_driver.c
@@ -4723,6 +4723,9 @@ static void qemuDomainEventQueue(struct qemud_driver *driver,
 }
 
 /* Migration support. */
+struct secure_mig {
+    int socket;
+};
 
 /* Prepare is the first step, and it runs on the destination host.
  *
@@ -4730,11 +4733,11 @@ static void qemuDomainEventQueue(struct qemud_driver *driver,
  */
 static int
 qemudDomainMigratePrepare2 (virConnectPtr dconn,
-                            char **cookie ATTRIBUTE_UNUSED,
-                            int *cookielen ATTRIBUTE_UNUSED,
+                            char **cookie,
+                            int *cookielen,
                             const char *uri_in,
                             char **uri_out,
-                            unsigned long flags ATTRIBUTE_UNUSED,
+                            unsigned long flags,
                             const char *dname,
                             unsigned long resource ATTRIBUTE_UNUSED,
                             const char *dom_xml)
@@ -4748,7 +4751,9 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn,
     char migrateFrom [64];
     const char *p;
     virDomainEventPtr event = NULL;
-    int ret = -1;;
+    int ret = -1;
+    struct secure_mig *secureMigData = NULL;
+    struct sockaddr_in a;
 
     *uri_out = NULL;
 
@@ -4768,6 +4773,8 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn,
      * If the URI passed in is not NULL then we try to parse out the
      * port number and use that (note that the hostname is assumed
      * to be a correct hostname which refers to the target machine).
+     *
+     * Note that for secure migration, uri_in is guaranteed to be non-NULL
      */
     if (uri_in == NULL) {
         this_port = QEMUD_MIGRATION_FIRST_PORT + port++;
@@ -4786,24 +4793,37 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn,
             goto cleanup;
         }
     } else {
-        /* Check the URI starts with "tcp:".  We will escape the
-         * URI when passing it to the qemu monitor, so bad
-         * characters in hostname part don't matter.
-         */
-        if (!STRPREFIX (uri_in, "tcp:")) {
-            qemudReportError (dconn, NULL, NULL, VIR_ERR_INVALID_ARG,
-                  "%s", _("only tcp URIs are supported for KVM migrations"));
-            goto cleanup;
+        if (!(flags & VIR_MIGRATE_SECURE)) {
+            /* Check the URI starts with "tcp:".  We will escape the
+             * URI when passing it to the qemu monitor, so bad
+             * characters in hostname part don't matter.
+             */
+            if (!STRPREFIX (uri_in, "tcp:")) {
+                qemudReportError (dconn, NULL, NULL, VIR_ERR_INVALID_ARG,
+                                  "%s", _("only tcp URIs are supported for KVM migrations"));
+                goto cleanup;
+            }
+
+            /* Get the port number. */
+            p = strrchr (uri_in, ':');
+            p++; /* definitely has a ':' in it, see above */
+            this_port = virParseNumber (&p);
+            if (this_port == -1 || p-uri_in != strlen (uri_in)) {
+                qemudReportError (dconn, NULL, NULL, VIR_ERR_INVALID_ARG,
+                                  "%s", _("URI did not have ':port' at the end"));
+                goto cleanup;
+            }
         }
+        else {
+            /* Secure migration requested; find a free port */
+            this_port = QEMUD_MIGRATION_FIRST_PORT + port++;
+            if (port == QEMUD_MIGRATION_NUM_PORTS) port = 0;
 
-        /* Get the port number. */
-        p = strrchr (uri_in, ':');
-        p++; /* definitely has a ':' in it, see above */
-        this_port = virParseNumber (&p);
-        if (this_port == -1 || p-uri_in != strlen (uri_in)) {
-            qemudReportError (dconn, NULL, NULL, VIR_ERR_INVALID_ARG,
-                              "%s", _("URI did not have ':port' at the end"));
-            goto cleanup;
+            /* for secure migration, uri_out is more or less meaningless (since
+             * we are migrating to localhost anyway).  Dup uri_in to maintain
+             * our contract
+             */
+            *uri_out = strdup(uri_in);
         }
     }
 
@@ -4858,8 +4878,14 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn,
     /* Start the QEMU daemon, with the same command-line arguments plus
      * -incoming tcp:0.0.0.0:port
      */
-    snprintf (migrateFrom, sizeof (migrateFrom), "tcp:0.0.0.0:%d", this_port);
+    if (!(flags & VIR_MIGRATE_SECURE))
+        snprintf (migrateFrom, sizeof (migrateFrom), "tcp:0.0.0.0:%d", this_port);
+    else
+        snprintf (migrateFrom, sizeof (migrateFrom), "tcp:127.0.0.1:%d", this_port);
+
     if (qemudStartVMDaemon (dconn, driver, vm, migrateFrom, -1) < 0) {
+        qemudReportError (dconn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
+                          "%s", _("failed to start listening VM"));
         if (!vm->persistent) {
             virDomainRemoveInactive(&driver->domains, vm);
             vm = NULL;
@@ -4867,6 +4893,38 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn,
         goto cleanup;
     }
 
+    if (flags & VIR_MIGRATE_SECURE) {
+        /* this memory (and the socket) will be cleaned up during the
+         * finish step
+         */
+        if (VIR_ALLOC(secureMigData) < 0) {
+            virReportOOMError (dconn);
+            goto cleanup;
+        }
+
+        secureMigData->socket = socket(AF_INET, SOCK_STREAM, 0);
+        if (secureMigData->socket < 0) {
+            virReportSystemError(dconn, errno,
+                                 "%s", _("cannot open socket"));
+            goto cleanup;
+        }
+
+        memset(&a, 0, sizeof(a));
+        a.sin_port = htons(this_port);
+        a.sin_family = AF_INET;
+        a.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+        if ((connect(secureMigData->socket, (struct sockaddr *)&a, sizeof(a))) < 0) {
+            virReportSystemError(dconn, errno,
+                                 "%s", _("cannot connect to qemu"));
+            close(secureMigData->socket);
+            goto cleanup;
+        }
+
+        *cookie = (char *)secureMigData;
+        *cookielen = sizeof(*secureMigData);
+    }
+
     event = virDomainEventNewFromObj(vm,
                                      VIR_DOMAIN_EVENT_STARTED,
                                      VIR_DOMAIN_EVENT_STARTED_MIGRATED);
@@ -4876,6 +4934,13 @@ cleanup:
     virDomainDefFree(def);
     if (ret != 0) {
         VIR_FREE(*uri_out);
+        VIR_FREE(secureMigData);
+
+        /* there might have been an error after we started the incoming qemu
+         * process, so be sure to kill it before we leave
+         */
+        if (vm)
+            qemudShutdownVMDaemon(NULL, driver, vm);
     }
     if (vm)
         virDomainObjUnlock(vm);
@@ -4885,11 +4950,174 @@ cleanup:
     return ret;
 }
 
+static int qemu_listen(int port)
+{
+    int qemu_sock, optval;
+    struct sockaddr_in sa_qemu;
+
+    /* NOTE: on error, we just cleanup after ourselves and return -1; the
+     * higher layer will report the error for us
+     */
+
+    qemu_sock = socket(AF_INET, SOCK_STREAM, 0);
+    if (qemu_sock < 0)
+        return -1;
+
+    optval = 1;
+    if (setsockopt(qemu_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&optval,
+                   sizeof(optval)) < 0)
+        goto close_qemu_sock;
+
+    memset(&sa_qemu, 0, sizeof(sa_qemu));
+    sa_qemu.sin_port = htons(port);
+    sa_qemu.sin_family = AF_INET;
+    sa_qemu.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+    if (bind(qemu_sock, (struct sockaddr *)&sa_qemu, sizeof(sa_qemu)) < 0)
+        goto close_qemu_sock;
+    if (listen(qemu_sock, 1) < 0)
+        goto close_qemu_sock;
+
+    return qemu_sock;
+
+close_qemu_sock:
+    close(qemu_sock);
+
+    return -1;
+}
+
+int virConnectSecureMigrationData(virConnectPtr conn,
+                                  const char *cookie,
+                                  int cookielen,
+                                  char *data,
+                                  int datalen);
+
+static int doSecureMigrate(virDomainPtr dom,
+                           virDomainObjPtr vm,
+                           const char *cookie,
+                           int cookielen,
+                           const char *uri)
+{
+/* FIXME: we know 65536 is enough because that is the limit from RPC.  Is
+ * there a nicer way to reflect that here?
+ */
+#define MAX_BUFFER 65536
+    int port;
+    int client_sock, qemu_sock;
+    struct sockaddr_in sa_client;
+    socklen_t addrlen;
+    virConnectPtr dconn;
+    char cmd[HOST_NAME_MAX+50];
+    char *info = NULL;
+    int retval = -1;
+    ssize_t bytes;
+    char buffer[MAX_BUFFER];
+    int ret;
+    char *safe_uri;
+    char ebuf[1024];
+
+    /* cycle through the localhost ports 9000 to 10000, looking for a socket
+     * we can listen on.  Once we've found one, break out of here
+     */
+    qemu_sock = -1;
+    for (port = 9000; port < 10000; port++) {
+        qemu_sock = qemu_listen(port);
+        if (qemu_sock >= 0)
+            break;
+    }
+
+    if (qemu_sock < 0) {
+        qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                          _("Could not open secure listening socket: %s"),
+                          virStrerror(errno, ebuf, sizeof ebuf));
+        return -1;
+    }
+
+    /* Do the migrate command, but let it return immediately.  Then we
+     * will accept data below
+     */
+    snprintf (cmd, sizeof cmd, "migrate -d tcp:127.0.0.1:%d", port);
+
+    if (qemudMonitorCommand(vm, cmd, &info) < 0) {
+        qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                          "%s", _("migrate operation failed"));
+        goto close_qemu_sock;
+    }
+
+    if (strstr(info, "fail") != NULL) {
+        qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                          _("secure migrate failed: %s"), info);
+        goto close_qemu_sock;
+    }
+
+    addrlen = sizeof(sa_client);
+    while ((client_sock = accept(qemu_sock, (struct sockaddr *)&sa_client, &addrlen)) < 0) {
+        if (errno == EAGAIN || errno == EINTR)
+            continue;
+        qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                          _("Failed accepting from the qemu socket: %s"),
+                          virStrerror(errno, ebuf, sizeof ebuf));
+        goto qemu_cancel_migration;
+    }
+
+    safe_uri = qemudEscapeMonitorArg (uri);
+    if (!safe_uri) {
+        virReportOOMError (dom->conn);
+        goto close_client_sock;
+    }
+
+    dconn = virConnectOpen(uri);
+    VIR_FREE (safe_uri);
+    if (dconn == NULL) {
+        qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                          _("Failed to connect to remote libvirtd"));
+        goto close_client_sock;
+    }
+
+    for (;;) {
+        bytes = saferead(client_sock, buffer, MAX_BUFFER);
+        if (bytes < 0) {
+            qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                              _("Failed to read from qemu: %s"),
+                              virStrerror(errno, ebuf, sizeof ebuf));
+            goto close_dconn;
+        }
+        else if (bytes == 0)
+            /* EOF; get out of here */
+            break;
+
+        ret = virConnectSecureMigrationData(dconn, cookie, cookielen,
+                                            buffer, bytes);
+        if (ret < 0) {
+            qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                              _("Failed to write migration data to remote libvirtd"));
+            goto close_dconn;
+        }
+    }
+
+    retval = 0;
+
+close_dconn:
+    virConnectClose(dconn);
+
+close_client_sock:
+    close(client_sock);
+
+qemu_cancel_migration:
+    if (retval != 0)
+        qemudMonitorCommand(vm, "migrate_cancel", &info);
+    VIR_FREE(info);
+
+close_qemu_sock:
+    close(qemu_sock);
+
+    return retval;
+}
+
 /* Perform is the second step, and it runs on the source host. */
 static int
 qemudDomainMigratePerform (virDomainPtr dom,
-                           const char *cookie ATTRIBUTE_UNUSED,
-                           int cookielen ATTRIBUTE_UNUSED,
+                           const char *cookie,
+                           int cookielen,
                            const char *uri,
                            unsigned long flags ATTRIBUTE_UNUSED,
                            const char *dname ATTRIBUTE_UNUSED,
@@ -4948,28 +5176,36 @@ qemudDomainMigratePerform (virDomainPtr dom,
         VIR_FREE (info);
     }
 
-    /* Issue the migrate command. */
-    safe_uri = qemudEscapeMonitorArg (uri);
-    if (!safe_uri) {
-        virReportOOMError (dom->conn);
-        goto cleanup;
-    }
-    snprintf (cmd, sizeof cmd, "migrate \"%s\"", safe_uri);
-    VIR_FREE (safe_uri);
+    if (!(flags & VIR_MIGRATE_SECURE)) {
+        /* Issue the migrate command. */
+        safe_uri = qemudEscapeMonitorArg (uri);
+        if (!safe_uri) {
+            virReportOOMError (dom->conn);
+            goto cleanup;
+        }
+        snprintf (cmd, sizeof cmd, "migrate \"%s\"", safe_uri);
+        VIR_FREE (safe_uri);
 
-    if (qemudMonitorCommand (vm, cmd, &info) < 0) {
-        qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
-                          "%s", _("migrate operation failed"));
-        goto cleanup;
-    }
+        if (qemudMonitorCommand (vm, cmd, &info) < 0) {
+            qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                              "%s", _("migrate operation failed"));
+            goto cleanup;
+        }
 
-    DEBUG ("%s: migrate reply: %s", vm->def->name, info);
+        DEBUG ("%s: migrate reply: %s", vm->def->name, info);
 
-    /* Now check for "fail" in the output string */
-    if (strstr(info, "fail") != NULL) {
-        qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
-                          _("migrate failed: %s"), info);
-        goto cleanup;
+        /* Now check for "fail" in the output string */
+        if (strstr(info, "fail") != NULL) {
+            qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                              _("migrate failed: %s"), info);
+            goto cleanup;
+        }
+    }
+    else {
+        if (doSecureMigrate(dom, vm, cookie, cookielen, uri) < 0) {
+            /* doSecureMigrate already set the error, so just get out */
+            goto cleanup;
+        }
     }
 
     /* Clean up the source domain. */
@@ -5023,10 +5259,10 @@ cleanup:
 static virDomainPtr
 qemudDomainMigrateFinish2 (virConnectPtr dconn,
                            const char *dname,
-                           const char *cookie ATTRIBUTE_UNUSED,
-                           int cookielen ATTRIBUTE_UNUSED,
+                           const char *cookie,
+                           int cookielen,
                            const char *uri ATTRIBUTE_UNUSED,
-                           unsigned long flags ATTRIBUTE_UNUSED,
+                           unsigned long flags,
                            int retcode)
 {
     struct qemud_driver *driver = dconn->privateData;
@@ -5034,6 +5270,7 @@ qemudDomainMigrateFinish2 (virConnectPtr dconn,
     virDomainPtr dom = NULL;
     virDomainEventPtr event = NULL;
     char *info = NULL;
+    struct secure_mig *secureMigData;
 
     qemuDriverLock(driver);
     vm = virDomainFindByName(&driver->domains, dname);
@@ -5043,6 +5280,20 @@ qemudDomainMigrateFinish2 (virConnectPtr dconn,
         goto cleanup;
     }
 
+    if (flags & VIR_MIGRATE_SECURE) {
+        /* in this case, we need to close out the cookie resources */
+        if (cookie == NULL || cookielen != sizeof(struct secure_mig)) {
+            qemudReportError (dconn, NULL, NULL, VIR_ERR_INVALID_ARG,
+                              _("Bad size for secure migration cookie"));
+            goto cleanup;
+        }
+
+        secureMigData = (struct secure_mig *)cookie;
+
+        close(secureMigData->socket);
+        VIR_FREE(secureMigData);
+    }
+
     /* Did the migration go as planned?  If yes, return the domain
      * object, but if no, clean up the empty qemu process.
      */
@@ -5188,6 +5439,31 @@ out:
     return ret;
 }
 
+static int
+qemudSecureMigrationData (virConnectPtr conn, const char *cookie, int cookielen,
+                          char *data, int datalen)
+{
+    struct secure_mig *migdata;
+    char ebuf[1024];
+
+    if (cookie == NULL || cookielen != sizeof(struct secure_mig)) {
+        qemudReportError (conn, NULL, NULL, VIR_ERR_INVALID_ARG,
+                          _("Bad size for secure migration cookie"));
+        return -1;
+    }
+
+    migdata = (struct secure_mig *)cookie;
+
+    if (safewrite(migdata->socket, data, datalen) != datalen) {
+        qemudReportError (conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
+                          _("Could not write data to migration socket: %s"),
+                          virStrerror(errno, ebuf, sizeof ebuf));
+        return -1;
+    }
+
+    return 0;
+}
+
 static virDriver qemuDriver = {
     VIR_DRV_QEMU,
     "QEMU",
@@ -5264,6 +5540,7 @@ static virDriver qemuDriver = {
     qemudNodeDeviceDettach, /* nodeDeviceDettach */
     qemudNodeDeviceReAttach, /* nodeDeviceReAttach */
     qemudNodeDeviceReset, /* nodeDeviceReset */
+    qemudSecureMigrationData, /* secureMigrationData */
 };
 
 
diff --git a/src/remote_internal.c b/src/remote_internal.c
index 64c24f4..0c9b7ff 100644
--- a/src/remote_internal.c
+++ b/src/remote_internal.c
@@ -944,7 +944,7 @@ remoteOpen (virConnectPtr conn,
     struct private_data *priv;
     int ret, rflags = 0;
 
-    if (inside_daemon)
+    if (inside_daemon && (!conn->uri || (conn->uri && !conn->uri->server)))
         return VIR_DRV_OPEN_DECLINED;
 
     if (!(priv = remoteAllocPrivateData(conn)))
@@ -2510,6 +2510,36 @@ done:
     return ddom;
 }
 
+static int remoteSecureMigrationData(virConnectPtr conn,
+                                     const char *cookie,
+                                     int cookielen,
+                                     char *data,
+                                     int datalen)
+{
+    int rv = -1;
+    remote_secure_migration_data_args args;
+    struct private_data *priv = conn->privateData;
+
+    remoteDriverLock(priv);
+
+    args.cookie.cookie_len = cookielen;
+    args.cookie.cookie_val = (char *)cookie;
+    args.buffer.buffer_len = datalen;
+    args.buffer.buffer_val = data;
+
+    if (call (conn, priv, 0, REMOTE_PROC_SECURE_MIGRATION_DATA,
+              (xdrproc_t) xdr_remote_secure_migration_data_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        goto done;
+
+    rv = 0;
+
+done:
+    remoteDriverUnlock(priv);
+
+    return rv;
+}
+
 static int
 remoteDomainMigratePrepare2 (virConnectPtr dconn,
                              char **cookie, int *cookielen,
@@ -6964,6 +6994,7 @@ static virDriver driver = {
     remoteNodeDeviceDettach, /* nodeDeviceDettach */
     remoteNodeDeviceReAttach, /* nodeDeviceReAttach */
     remoteNodeDeviceReset, /* nodeDeviceReset */
+    remoteSecureMigrationData, /* secureMigrationData */
 };
 
 static virNetworkDriver network_driver = {
diff --git a/src/test.c b/src/test.c
index 1915b57..b93d8e7 100644
--- a/src/test.c
+++ b/src/test.c
@@ -3528,6 +3528,7 @@ static virDriver testDriver = {
     NULL, /* nodeDeviceDettach */
     NULL, /* nodeDeviceReAttach */
     NULL, /* nodeDeviceReset */
+    NULL, /* secureMigrationData */
 };
 
 static virNetworkDriver testNetworkDriver = {
diff --git a/src/uml_driver.c b/src/uml_driver.c
index 0e2c4dd..88140bb 100644
--- a/src/uml_driver.c
+++ b/src/uml_driver.c
@@ -1887,6 +1887,7 @@ static virDriver umlDriver = {
     NULL, /* nodeDeviceDettach */
     NULL, /* nodeDeviceReAttach */
     NULL, /* nodeDeviceReset */
+    NULL, /* secureMigrationData */
 };
 
 
diff --git a/src/virsh.c b/src/virsh.c
index cb32ede..08cbe4d 100644
--- a/src/virsh.c
+++ b/src/virsh.c
@@ -2320,6 +2320,7 @@ static const vshCmdInfo info_migrate[] = {
 
 static const vshCmdOptDef opts_migrate[] = {
     {"live", VSH_OT_BOOL, 0, gettext_noop("live migration")},
+    {"secure", VSH_OT_BOOL, 0, gettext_noop("secure migration")},
     {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")},
     {"desturi", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("connection URI of the destination host")},
     {"migrateuri", VSH_OT_DATA, 0, gettext_noop("migration URI, usually can be omitted")},
@@ -2357,6 +2358,9 @@ cmdMigrate (vshControl *ctl, const vshCmd *cmd)
     if (vshCommandOptBool (cmd, "live"))
         flags |= VIR_MIGRATE_LIVE;
 
+    if (vshCommandOptBool (cmd, "secure"))
+        flags |= VIR_MIGRATE_SECURE;
+
     /* Temporarily connect to the destination host. */
     dconn = virConnectOpenAuth (desturi, virConnectAuthPtrDefault, 0);
     if (!dconn) goto done;
diff --git a/src/xen_unified.c b/src/xen_unified.c
index e708980..2f6fcfe 100644
--- a/src/xen_unified.c
+++ b/src/xen_unified.c
@@ -1608,6 +1608,7 @@ static virDriver xenUnifiedDriver = {
     xenUnifiedNodeDeviceDettach, /* nodeDeviceDettach */
     xenUnifiedNodeDeviceReAttach, /* nodeDeviceReAttach */
     xenUnifiedNodeDeviceReset, /* nodeDeviceReset */
+    NULL, /* secureMigrationData */
 };
 
 /**
