Imported Upstream version 1.26
[platform/upstream/connman.git] / client / agent.c
index 16d1928..d020889 100644 (file)
 #include "dbus_helpers.h"
 #include "agent.h"
 
-static bool agent_registered = false;
-static DBusMessage *agent_message = NULL;
-static struct {
+#define AGENT_INTERFACE      "net.connman.Agent"
+#define VPN_AGENT_INTERFACE  "net.connman.vpn.Agent"
+
+static DBusConnection *agent_connection;
+
+struct agent_input_data {
+       const char *attribute;
+       bool requested;
+       char *prompt;
+       connmanctl_input_func_t func;
+};
+
+struct agent_data {
+       struct agent_input_data *input;
+       char *interface;
+       bool registered;
+       DBusMessage *message;
        DBusMessage *reply;
        DBusMessageIter iter;
        DBusMessageIter dict;
-} agent_reply = { };
+       GDBusMethodFunction pending_function;
+};
 
-#define AGENT_INTERFACE      "net.connman.Agent"
+static void request_input_ssid_return(char *input, void *user_data);
+static void request_input_passphrase_return(char *input, void *user_data);
+static void request_input_string_return(char *input, void *user_data);
 
-static void request_input_ssid_return(char *input);
-static void request_input_passphrase_return(char *input);
-static void request_input_string_return(char *input);
+enum requestinput {
+       SSID                    = 0,
+       IDENTITY                = 1,
+       PASSPHRASE              = 2,
+       WPS                     = 3,
+       WISPR_USERNAME          = 4,
+       WISPR_PASSPHRASE        = 5,
+       REQUEST_INPUT_MAX       = 6,
+};
+
+static struct agent_input_data agent_input_handler[] = {
+       { "Name", false, "Hidden SSID name? ", request_input_ssid_return },
+       { "Identity", false, "EAP username? ", request_input_string_return },
+       { "Passphrase", false, "Passphrase? ",
+         request_input_passphrase_return },
+       { "WPS", false, "WPS PIN (empty line for pushbutton)? " ,
+         request_input_string_return },
+       { "Username", false, "WISPr username? ", request_input_string_return },
+       { "Password", false, "WISPr password? ", request_input_string_return },
+       { },
+};
+
+static struct agent_data agent_request = {
+       agent_input_handler,
+       AGENT_INTERFACE,
+};
+
+static struct agent_input_data vpnagent_input_handler[] = {
+       { "OpenConnect.Cookie", false, "OpenConnect Cookie? ",
+         request_input_string_return },
+       { "OpenConnect.ServerCert", false,
+         "OpenConnect server certificate hash? ",
+         request_input_string_return },
+       { "OpenConnect.VPNHost", false, "OpenConnect VPN server? ",
+         request_input_string_return },
+       { "Username", false, "VPN username? ", request_input_string_return },
+       { "Password", false, "VPN password? ", request_input_string_return },
+       { },
+};
+
+static struct agent_data vpn_agent_request = {
+       vpnagent_input_handler,
+       VPN_AGENT_INTERFACE,
+};
 
 static int confirm_input(char *input)
 {
        int i;
 
-       if (input == NULL)
+       if (!input)
                return -1;
 
        for (i = 0; input[i] != '\0'; i++)
@@ -77,7 +135,7 @@ static int confirm_input(char *input)
 static char *strip_path(char *path)
 {
        char *name = strrchr(path, '/');
-       if (name != NULL)
+       if (name)
                name++;
        else
                name = path;
@@ -89,27 +147,31 @@ static char *agent_path(void)
 {
        static char *path = NULL;
 
-       if (path == NULL)
+       if (!path)
                path = g_strdup_printf("/net/connman/connmanctl%d", getpid());
 
        return path;
 }
 
-static void pending_message_remove()
+static void pending_message_remove(struct agent_data *request)
 {
-       if (agent_message != NULL) {
-               dbus_message_unref(agent_message);
-               agent_message = NULL;
+       if (request->message) {
+               dbus_message_unref(request->message);
+               request->message = NULL;
        }
 
-       if (agent_reply.reply != NULL) {
-               dbus_message_unref(agent_reply.reply);
-               agent_reply.reply = NULL;
+       if (request->reply) {
+               dbus_message_unref(request->reply);
+               request->reply = NULL;
        }
 }
 
 static void pending_command_complete(char *message)
 {
+       struct agent_data *next_request = NULL;
+       DBusMessage *pending_message;
+       GDBusMethodFunction pending_function;
+
        __connmanctl_save_rl();
 
        fprintf(stdout, "%s", message);
@@ -119,17 +181,58 @@ static void pending_command_complete(char *message)
        if (__connmanctl_is_interactive() == true)
                __connmanctl_command_mode();
        else
-               __connmanctl_agent_mode("", NULL);
+               __connmanctl_agent_mode("", NULL, NULL);
+
+       if (agent_request.message)
+               next_request = &agent_request;
+       else if (vpn_agent_request.message)
+               next_request = &vpn_agent_request;
+
+       if (!next_request)
+               return;
+
+       pending_message = next_request->message;
+       pending_function = next_request->pending_function;
+       next_request->pending_function = NULL;
+
+       pending_function(agent_connection, next_request->message,
+                       next_request);
+
+       dbus_message_unref(pending_message);
+}
+
+static bool handle_message(DBusMessage *message, struct agent_data *request,
+               GDBusMethodFunction function)
+{
+       if (!agent_request.pending_function &&
+                       !vpn_agent_request.pending_function)
+               return true;
+
+       request->message = dbus_message_ref(message);
+       request->pending_function = function;
+
+       return false;
 }
 
 static DBusMessage *agent_release(DBusConnection *connection,
                DBusMessage *message, void *user_data)
 {
-       g_dbus_unregister_interface(connection, agent_path(), AGENT_INTERFACE);
-       agent_registered = false;
+       struct agent_data *request = user_data;
+
+       if (handle_message(message, request, agent_release) == false)
+               return NULL;
+
+       g_dbus_unregister_interface(connection, agent_path(),
+                       request->interface);
+       request->registered = false;
 
-       pending_message_remove();
-       pending_command_complete("Agent unregistered by ConnMan\n");
+       pending_message_remove(request);
+
+       if (strcmp(request->interface, AGENT_INTERFACE) == 0)
+               pending_command_complete("Agent unregistered by ConnMan\n");
+       else
+               pending_command_complete("VPN Agent unregistered by ConnMan "
+                               "VPNd\n");
 
        if (__connmanctl_is_interactive() == false)
                __connmanctl_quit();
@@ -140,39 +243,54 @@ static DBusMessage *agent_release(DBusConnection *connection,
 static DBusMessage *agent_cancel(DBusConnection *connection,
                DBusMessage *message, void *user_data)
 {
-       pending_message_remove();
-       pending_command_complete("Agent request cancelled by ConnMan\n");
+       struct agent_data *request = user_data;
+
+       if (handle_message(message, request, agent_cancel) == false)
+               return NULL;
+
+       pending_message_remove(request);
+
+       if (strcmp(request->interface, AGENT_INTERFACE) == 0)
+               pending_command_complete("Agent request cancelled by "
+                               "ConnMan\n");
+       else
+               pending_command_complete("VPN Agent request cancelled by "
+                               "ConnMan VPNd\n");
 
        return dbus_message_new_method_return(message);
 }
 
-static DBusConnection *agent_connection = NULL;
-
-static void request_browser_return(char *input)
+static void request_browser_return(char *input, void *user_data)
 {
+       struct agent_data *request = user_data;
+
        switch (confirm_input(input)) {
        case 1:
-               g_dbus_send_reply(agent_connection, agent_message,
+               g_dbus_send_reply(agent_connection, request->message,
                                DBUS_TYPE_INVALID);
                break;
        case 0:
-               g_dbus_send_error(agent_connection, agent_message,
+               g_dbus_send_error(agent_connection, request->message,
                                "net.connman.Agent.Error.Canceled", NULL);
                break;
        default:
                return;
        }
 
-       pending_message_remove();
+       pending_message_remove(request);
        pending_command_complete("");
 }
 
 static DBusMessage *agent_request_browser(DBusConnection *connection,
                DBusMessage *message, void *user_data)
 {
+       struct agent_data *request = user_data;
        DBusMessageIter iter;
        char *service, *url;
 
+       if (handle_message(message, request, agent_request_browser) == false)
+               return NULL;
+
        dbus_message_iter_init(message, &iter);
 
        dbus_message_iter_get_basic(&iter, &service);
@@ -184,39 +302,49 @@ static DBusMessage *agent_request_browser(DBusConnection *connection,
        fprintf(stdout, "  %s\n", url);
        __connmanctl_redraw_rl();
 
-       agent_connection = connection;
-       agent_message = dbus_message_ref(message);
+       request->message = dbus_message_ref(message);
        __connmanctl_agent_mode("Connected (yes/no)? ",
-                       request_browser_return);
+                       request_browser_return, request);
 
        return NULL;
 }
 
-static void report_error_return(char *input)
+static void report_error_return(char *input, void *user_data)
 {
+       struct agent_data *request = user_data;
+
        switch (confirm_input(input)) {
        case 1:
-               g_dbus_send_error(agent_connection, agent_message,
-                               "net.connman.Agent.Error.Retry", NULL);
+               if (strcmp(request->interface, AGENT_INTERFACE) == 0)
+                       g_dbus_send_error(agent_connection, request->message,
+                                       "net.connman.Agent.Error.Retry", NULL);
+               else
+                       g_dbus_send_error(agent_connection, request->message,
+                                       "net.connman.vpn.Agent.Error.Retry",
+                                       NULL);
                break;
        case 0:
-               g_dbus_send_reply(agent_connection, agent_message,
+               g_dbus_send_reply(agent_connection, request->message,
                                DBUS_TYPE_INVALID);
                break;
        default:
                return;
        }
 
-       pending_message_remove();
+       pending_message_remove(request);
        pending_command_complete("");
 }
 
 static DBusMessage *agent_report_error(DBusConnection *connection,
                DBusMessage *message, void *user_data)
 {
+       struct agent_data *request = user_data;
        DBusMessageIter iter;
        char *path, *service, *error;
 
+       if (handle_message(message, request, agent_report_error) == false)
+               return NULL;
+
        dbus_message_iter_init(message, &iter);
 
        dbus_message_iter_get_basic(&iter, &path);
@@ -226,144 +354,154 @@ static DBusMessage *agent_report_error(DBusConnection *connection,
        dbus_message_iter_get_basic(&iter, &error);
 
        __connmanctl_save_rl();
-       fprintf(stdout, "Agent ReportError %s\n", service);
+       if (strcmp(request->interface, AGENT_INTERFACE) == 0)
+               fprintf(stdout, "Agent ReportError %s\n", service);
+       else
+               fprintf(stdout, "VPN Agent ReportError %s\n", service);
        fprintf(stdout, "  %s\n", error);
        __connmanctl_redraw_rl();
 
-       agent_connection = connection;
-       agent_message = dbus_message_ref(message);
-       __connmanctl_agent_mode("Retry (yes/no)? ", report_error_return);
+       request->message = dbus_message_ref(message);
+       __connmanctl_agent_mode("Retry (yes/no)? ", report_error_return,
+                       request);
 
        return NULL;
 }
 
-enum requestinput {
-       SSID                    = 0,
-       IDENTITY                = 1,
-       PASSPHRASE              = 2,
-       WPS                     = 3,
-       WISPR_USERNAME          = 4,
-       WISPR_PASSPHRASE        = 5,
-       REQUEST_INPUT_MAX       = 6,
-};
+static DBusMessage *agent_report_peer_error(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct agent_data *request = user_data;
+       char *path, *peer, *error;
+       DBusMessageIter iter;
 
-static struct {
-       const char *attribute;
-       bool requested;
-       char *prompt;
-       connmanctl_input_func_t *func;
-} agent_input[] = {
-       { "Name", false, "Hidden SSID name? ", request_input_ssid_return },
-       { "Identity", false, "EAP username? ", request_input_string_return },
-       { "Passphrase", false, "Passphrase? ",
-         request_input_passphrase_return },
-       { "WPS", false, "WPS PIN (empty line for pushbutton)? " ,
-         request_input_string_return },
-       { "Username", false, "WISPr username? ", request_input_string_return },
-       { "Password", false, "WISPr password? ", request_input_string_return },
-       { },
-};
+       if (handle_message(message, request,
+                               agent_report_peer_error) == false)
+               return NULL;
+
+       dbus_message_iter_init(message, &iter);
+
+       dbus_message_iter_get_basic(&iter, &path);
+       peer = strip_path(path);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_get_basic(&iter, &error);
+
+       __connmanctl_save_rl();
+       fprintf(stdout, "Agent ReportPeerError %s\n", peer);
+       fprintf(stdout, "  %s\n", error);
+       __connmanctl_redraw_rl();
 
-static void request_input_next(void)
+       request->message = dbus_message_ref(message);
+       __connmanctl_agent_mode("Retry (yes/no)? ",
+                               report_error_return, request);
+       return NULL;
+}
+
+static void request_input_next(struct agent_data *request)
 {
        int i;
 
-       for (i = 0; agent_input[i].attribute != NULL; i++) {
-               if (agent_input[i].requested == true) {
-                       if(agent_input[i].func != NULL)
-                               __connmanctl_agent_mode(agent_input[i].prompt,
-                                               agent_input[i].func);
+       for (i = 0; request->input[i].attribute; i++) {
+               if (request->input[i].requested == true) {
+                       if (request->input[i].func)
+                               __connmanctl_agent_mode(request->input[i].prompt,
+                                               request->input[i].func,
+                                               request);
                        else
-                               agent_input[i].requested = false;
+                               request->input[i].requested = false;
                        return;
                }
        }
 
-       dbus_message_iter_close_container(&agent_reply.iter,
-                       &agent_reply.dict);
+       dbus_message_iter_close_container(&request->iter, &request->dict);
 
-       g_dbus_send_message(agent_connection, agent_reply.reply);
-       agent_reply.reply = NULL;
+       g_dbus_send_message(agent_connection, request->reply);
+       request->reply = NULL;
 
-       pending_message_remove();
+       pending_message_remove(request);
        pending_command_complete("");
+
+       __connmanctl_redraw_rl();
 }
 
-static void request_input_append(const char *attribute, char *value)
+static void request_input_append(struct agent_data *request,
+               const char *attribute, char *value)
 {
-       __connmanctl_dbus_append_dict_entry(&agent_reply.dict, attribute,
+       __connmanctl_dbus_append_dict_entry(&request->dict, attribute,
                        DBUS_TYPE_STRING, &value);
 }
 
-static void request_input_ssid_return(char *input)
+static void request_input_ssid_return(char *input,
+               void *user_data)
 {
+       struct agent_data *request = user_data;
        int len = 0;
 
-       if (input != NULL)
+       if (input)
                len = strlen(input);
 
        if (len > 0 && len <= 32) {
-               agent_input[SSID].requested = false;
-               request_input_append(agent_input[SSID].attribute, input);
+               request->input[SSID].requested = false;
+               request_input_append(request, request->input[SSID].attribute,
+                               input);
 
-               request_input_next();
+               request_input_next(request);
        }
 }
 
-static void request_input_passphrase_return(char *input)
+static void request_input_passphrase_return(char *input, void *user_data)
 {
+       struct agent_data *request = user_data;
+       int len = 0;
+
        /* TBD passphrase length checking */
 
-       if (input != NULL && strlen(input) > 0) {
-               agent_input[PASSPHRASE].requested = false;
-               request_input_append(agent_input[PASSPHRASE].attribute, input);
+       if (input)
+               len = strlen(input);
+
+       if (len == 0 && request->input[WPS].requested == false)
+               return;
+
+       request->input[PASSPHRASE].requested = false;
 
-               agent_input[WPS].requested = false;
+       if (len > 0) {
+               request_input_append(request,
+                               request->input[PASSPHRASE].attribute, input);
 
-               request_input_next();
+               request->input[WPS].requested = false;
        }
+
+       request_input_next(request);
 }
 
-static void request_input_string_return(char *input)
+static void request_input_string_return(char *input, void *user_data)
 {
+       struct agent_data *request = user_data;
        int i;
 
-       for (i = 0; agent_input[i].attribute != NULL; i++) {
-               if (agent_input[i].requested == true) {
-                       request_input_append(agent_input[i].attribute, input);
-                       agent_input[i].requested = false;
+       for (i = 0; request->input[i].attribute; i++) {
+               if (request->input[i].requested == true) {
+                       request_input_append(request,
+                                       request->input[i].attribute, input);
+                       request->input[i].requested = false;
                        break;
                }
        }
 
-       request_input_next();
+       request_input_next(request);
 }
 
-static DBusMessage *agent_request_input(DBusConnection *connection,
-               DBusMessage *message, void *user_data)
+static void parse_agent_request(struct agent_data *request,
+                                               DBusMessageIter *iter)
 {
-       DBusMessageIter iter, dict, entry, variant;
-       char *service, *str, *field;
-       DBusMessageIter dict_entry, field_entry, field_value;
-       char *argument, *value, *attr_type;
-
+       DBusMessageIter dict, entry, variant, dict_entry;
+       DBusMessageIter field_entry, field_value;
+       char *field, *argument, *value;
+       char *attr_type = NULL;
        int i;
 
-       dbus_message_iter_init(message, &iter);
-
-       dbus_message_iter_get_basic(&iter, &str);
-       service = strip_path(str);
-
-       dbus_message_iter_next(&iter);
-       dbus_message_iter_recurse(&iter, &dict);
-
-       __connmanctl_save_rl();
-       fprintf(stdout, "Agent RequestInput %s\n", service);
-       __connmanctl_dbus_print(&dict, "  ", " = ", "\n");
-       fprintf(stdout, "\n");
-       __connmanctl_redraw_rl();
-
-       dbus_message_iter_recurse(&iter, &dict);
+       dbus_message_iter_recurse(iter, &dict);
 
        while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
 
@@ -395,9 +533,9 @@ static DBusMessage *agent_request_input(DBusConnection *connection,
                        dbus_message_iter_next(&dict_entry);
                }
 
-               for (i = 0; agent_input[i].attribute != NULL; i++) {
-                       if (strcmp(field, agent_input[i].attribute) == 0) {
-                               agent_input[i].requested = true;
+               for (i = 0; request->input[i].attribute; i++) {
+                       if (strcmp(field, request->input[i].attribute) == 0) {
+                               request->input[i].requested = true;
                                break;
                        }
                }
@@ -407,24 +545,146 @@ static DBusMessage *agent_request_input(DBusConnection *connection,
 
                dbus_message_iter_next(&dict);
        }
+}
 
-       agent_connection = connection;
-       agent_reply.reply = dbus_message_new_method_return(message);
-       dbus_message_iter_init_append(agent_reply.reply, &agent_reply.iter);
+static DBusMessage *agent_request_input(DBusConnection *connection,
+               DBusMessage *message, void *user_data)
+{
+       struct agent_data *request = user_data;
+       DBusMessageIter iter, dict;
+       char *service, *str;
+
+       if (handle_message(message, request, agent_request_input) == false)
+               return NULL;
+
+       dbus_message_iter_init(message, &iter);
+
+       dbus_message_iter_get_basic(&iter, &str);
+       service = strip_path(str);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &dict);
+
+       __connmanctl_save_rl();
+       if (strcmp(request->interface, AGENT_INTERFACE) == 0)
+               fprintf(stdout, "Agent RequestInput %s\n", service);
+       else
+               fprintf(stdout, "VPN Agent RequestInput %s\n", service);
+       __connmanctl_dbus_print(&dict, "  ", " = ", "\n");
+       fprintf(stdout, "\n");
+
+       parse_agent_request(request, &iter);
+
+       request->reply = dbus_message_new_method_return(message);
+       dbus_message_iter_init_append(request->reply, &request->iter);
+
+       dbus_message_iter_open_container(&request->iter, DBUS_TYPE_ARRAY,
+                        DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                        DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                        DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+                       &request->dict);
+
+       request_input_next(request);
+
+       return NULL;
+}
+
+static void request_authorization_return(char *input, void *user_data)
+{
+       struct agent_data *request = user_data;
+
+       switch (confirm_input(input)) {
+       case 1:
+               request->reply = dbus_message_new_method_return(
+                                                       request->message);
+               dbus_message_iter_init_append(request->reply, &request->iter);
+
+               dbus_message_iter_open_container(&request->iter,
+                               DBUS_TYPE_ARRAY,
+                               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                               DBUS_TYPE_STRING_AS_STRING
+                               DBUS_TYPE_VARIANT_AS_STRING
+                               DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+                               &request->dict);
+               dbus_message_iter_close_container(&request->iter,
+                                                       &request->dict);
+               g_dbus_send_message(agent_connection, request->reply);
+               request->reply = NULL;
+               break;
+       case 0:
+                g_dbus_send_error(agent_connection, request->message,
+                                "net.connman.Agent.Error.Rejected", NULL);
+                break;
+       default:
+                g_dbus_send_error(agent_connection, request->message,
+                                "net.connman.Agent.Error.Canceled", NULL);
+                break;
+       }
 
-       dbus_message_iter_open_container(&agent_reply.iter, DBUS_TYPE_ARRAY,
+       pending_message_remove(request);
+       pending_command_complete("");
+}
+
+static DBusMessage *
+agent_request_peer_authorization(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct agent_data *request = user_data;
+       DBusMessageIter iter, dict;
+       char *peer, *str;
+       bool input;
+       int i;
+
+       if (handle_message(message, request, agent_request_peer_authorization)
+                                                               == false)
+               return NULL;
+
+       dbus_message_iter_init(message, &iter);
+
+       dbus_message_iter_get_basic(&iter, &str);
+       peer = strip_path(str);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &dict);
+
+       __connmanctl_save_rl();
+       fprintf(stdout, "Agent RequestPeerAuthorization %s\n", peer);
+       __connmanctl_dbus_print(&dict, "  ", " = ", "\n");
+       fprintf(stdout, "\n");
+
+       parse_agent_request(request, &iter);
+
+       for (input = false, i = 0; request->input[i].attribute; i++) {
+               if (request->input[i].requested == true) {
+                       input = true;
+                       break;
+               }
+       }
+
+       if (!input) {
+               request->message = dbus_message_ref(message);
+               __connmanctl_agent_mode("Accept connection (yes/no)? ",
+                                       request_authorization_return, request);
+               return NULL;
+       }
+
+       request->reply = dbus_message_new_method_return(message);
+       dbus_message_iter_init_append(request->reply, &request->iter);
+
+       dbus_message_iter_open_container(&request->iter, DBUS_TYPE_ARRAY,
                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
-                        DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &agent_reply.dict);
+                        DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+                       &request->dict);
 
-       request_input_next();
+       request_input_next(request);
 
        return NULL;
 }
 
 static const GDBusMethodTable agent_methods[] = {
-       { GDBUS_METHOD("Release", NULL, NULL, agent_release) },
-       { GDBUS_METHOD("Cancel", NULL, NULL, agent_cancel) },
+       { GDBUS_ASYNC_METHOD("Release", NULL, NULL, agent_release) },
+       { GDBUS_ASYNC_METHOD("Cancel", NULL, NULL, agent_cancel) },
        { GDBUS_ASYNC_METHOD("RequestBrowser",
                                GDBUS_ARGS({ "service", "o" },
                                        { "url", "s" }),
@@ -433,11 +693,20 @@ static const GDBusMethodTable agent_methods[] = {
                                GDBUS_ARGS({ "service", "o" },
                                        { "error", "s" }),
                                NULL, agent_report_error) },
+       { GDBUS_ASYNC_METHOD("ReportPeerError",
+                               GDBUS_ARGS({ "peer", "o" },
+                                       { "error", "s" }),
+                               NULL, agent_report_peer_error) },
        { GDBUS_ASYNC_METHOD("RequestInput",
                                GDBUS_ARGS({ "service", "o" },
                                        { "fields", "a{sv}" }),
                                GDBUS_ARGS({ "fields", "a{sv}" }),
                                agent_request_input) },
+       { GDBUS_ASYNC_METHOD("RequestPeerAuthorization",
+                               GDBUS_ARGS({ "peer", "o" },
+                                       { "fields", "a{sv}" }),
+                               GDBUS_ARGS({ "fields", "a{sv}" }),
+                               agent_request_peer_authorization) },
        { },
 };
 
@@ -446,41 +715,48 @@ static int agent_register_return(DBusMessageIter *iter, const char *error,
 {
        DBusConnection *connection = user_data;
 
-       if (error != NULL) {
+       if (error) {
                g_dbus_unregister_interface(connection, agent_path(),
                                AGENT_INTERFACE);
                fprintf(stderr, "Error registering Agent: %s\n", error);
                return 0;
        }
 
-       agent_registered = true;
+       agent_request.registered = true;
        fprintf(stdout, "Agent registered\n");
 
        return -EINPROGRESS;
 }
 
+static void append_path(DBusMessageIter *iter, void *user_data)
+{
+       const char *path = user_data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+}
+
 int __connmanctl_agent_register(DBusConnection *connection)
 {
        char *path = agent_path();
        int result;
 
-       if (agent_registered == true) {
+       if (agent_request.registered == true) {
                fprintf(stderr, "Agent already registered\n");
                return -EALREADY;
        }
 
-       if (g_dbus_register_interface(connection, path,
+       agent_connection = connection;
+
+       if (!g_dbus_register_interface(connection, path,
                                        AGENT_INTERFACE, agent_methods,
-                                       NULL, NULL, connection,
-                                       NULL) == FALSE) {
+                                       NULL, NULL, &agent_request, NULL)) {
                fprintf(stderr, "Error: Failed to register Agent callbacks\n");
                return 0;
        }
 
        result = __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
                        CONNMAN_PATH, "net.connman.Manager", "RegisterAgent",
-                       agent_register_return, connection,
-                       DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
+                       agent_register_return, connection, append_path, path);
 
        if (result != -EINPROGRESS) {
                g_dbus_unregister_interface(connection, agent_path(),
@@ -495,12 +771,12 @@ int __connmanctl_agent_register(DBusConnection *connection)
 static int agent_unregister_return(DBusMessageIter *iter, const char *error,
                void *user_data)
 {
-       if (error != NULL) {
+       if (error) {
                fprintf(stderr, "Error unregistering Agent: %s\n", error);
                return 0;
        }
 
-       agent_registered = false;
+       agent_request.registered = false;
        fprintf(stdout, "Agent unregistered\n");
 
        return 0;
@@ -511,7 +787,7 @@ int __connmanctl_agent_unregister(DBusConnection *connection)
        char *path = agent_path();
        int result;
 
-       if (agent_registered == false) {
+       if (agent_request.registered == false) {
                fprintf(stderr, "Agent not registered\n");
                return -EALREADY;
        }
@@ -520,11 +796,115 @@ int __connmanctl_agent_unregister(DBusConnection *connection)
 
        result = __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
                        CONNMAN_PATH, "net.connman.Manager", "UnregisterAgent",
-                       agent_unregister_return, NULL,
-                       DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
+                       agent_unregister_return, NULL, append_path, path);
 
        if (result != -EINPROGRESS)
                fprintf(stderr, "Error: Failed to unregister Agent\n");
 
        return result;
 }
+
+static const GDBusMethodTable vpn_agent_methods[] = {
+       { GDBUS_ASYNC_METHOD("Release", NULL, NULL, agent_release) },
+       { GDBUS_ASYNC_METHOD("Cancel", NULL, NULL, agent_cancel) },
+       { GDBUS_ASYNC_METHOD("ReportError",
+                               GDBUS_ARGS({ "service", "o" },
+                                       { "error", "s" }),
+                               NULL, agent_report_error) },
+       { GDBUS_ASYNC_METHOD("RequestInput",
+                               GDBUS_ARGS({ "service", "o" },
+                                       { "fields", "a{sv}" }),
+                               GDBUS_ARGS({ "fields", "a{sv}" }),
+                               agent_request_input) },
+       { },
+};
+
+static int vpn_agent_register_return(DBusMessageIter *iter, const char *error,
+               void *user_data)
+{
+       DBusConnection *connection = user_data;
+
+       if (error) {
+               g_dbus_unregister_interface(connection, agent_path(),
+                               VPN_AGENT_INTERFACE);
+               fprintf(stderr, "Error registering VPN Agent: %s\n", error);
+               return 0;
+       }
+
+       vpn_agent_request.registered = true;
+       fprintf(stdout, "VPN Agent registered\n");
+
+       return -EINPROGRESS;
+}
+
+int __connmanctl_vpn_agent_register(DBusConnection *connection)
+{
+       char *path = agent_path();
+       int result;
+
+       if (vpn_agent_request.registered == true) {
+               fprintf(stderr, "VPN Agent already registered\n");
+               return -EALREADY;
+       }
+
+       agent_connection = connection;
+
+       if (!g_dbus_register_interface(connection, path,
+                       VPN_AGENT_INTERFACE, vpn_agent_methods,
+                       NULL, NULL, &vpn_agent_request, NULL)) {
+               fprintf(stderr, "Error: Failed to register VPN Agent "
+                               "callbacks\n");
+               return 0;
+       }
+
+       result = __connmanctl_dbus_method_call(connection, VPN_SERVICE,
+                       VPN_PATH, "net.connman.vpn.Manager", "RegisterAgent",
+                       vpn_agent_register_return, connection, append_path,
+                       path);
+
+       if (result != -EINPROGRESS) {
+               g_dbus_unregister_interface(connection, agent_path(),
+                               VPN_AGENT_INTERFACE);
+
+               fprintf(stderr, "Error: Failed to register VPN Agent\n");
+       }
+
+       return result;
+}
+
+static int vpn_agent_unregister_return(DBusMessageIter *iter,
+               const char *error, void *user_data)
+{
+       if (error) {
+               fprintf(stderr, "Error unregistering VPN Agent: %s\n", error);
+               return 0;
+       }
+
+       vpn_agent_request.registered = false;
+       fprintf(stdout, "VPN Agent unregistered\n");
+
+       return 0;
+}
+
+int __connmanctl_vpn_agent_unregister(DBusConnection *connection)
+{
+       char *path = agent_path();
+       int result;
+
+       if (vpn_agent_request.registered == false) {
+               fprintf(stderr, "VPN Agent not registered\n");
+               return -EALREADY;
+       }
+
+       g_dbus_unregister_interface(connection, agent_path(),
+                       VPN_AGENT_INTERFACE);
+
+       result = __connmanctl_dbus_method_call(connection, VPN_SERVICE,
+                       VPN_PATH, "net.connman.vpn.Manager", "UnregisterAgent",
+                       vpn_agent_unregister_return, NULL, append_path, path);
+
+       if (result != -EINPROGRESS)
+               fprintf(stderr, "Error: Failed to unregister VPN Agent\n");
+
+       return result;
+}