Merge "Handle the failure case due to the invalid cert file" into tizen
[platform/upstream/connman.git] / vpn / plugins / ipsec.c
index e4bbf2b..6862fbc 100644 (file)
@@ -76,9 +76,13 @@ struct openssl_private_data {
 struct ipsec_private_data {
        struct vpn_provider *provider;
        struct openssl_private_data openssl_data;
-       vpn_provider_connect_cb_t cb;
+       vpn_provider_connect_cb_t connect_cb;
+       void *connect_user_data;
+};
 
-       void *user_data;
+struct ipsec_event_data {
+       vpn_event_callback event_cb;
+       void *event_user_data;
 };
 
 struct {
@@ -158,10 +162,12 @@ static const char *ikev1_proposals [] ={
                NULL,
 };
 
-static const char *ikev2_esp_proposals = "aes256-aes128-sha512-sha384-sha256-sha1-modp2048-modp1536-modp1024";
+static const char *ikev2_esp_proposals = "aes256-aes128-sha256-sha1";
 
 static const char *ikev2_proposals = "aes256-aes128-sha512-sha384-sha256-sha1-modp2048-modp1536-modp1024";
 
+static struct ipsec_event_data event_data;
+
 static void init_openssl(void)
 {
        /* Load the human readable error strings for libcrypto */
@@ -242,51 +248,97 @@ static int get_cert_type(const char *path)
        return cert_type;
 }
 
-static void read_der_file(const char *path, X509 **cert)
+static int read_der_file(const char *path, X509 **cert)
 {
        FILE *fp = NULL;
+       int err = 0;
+
+       if(!path || !cert) {
+               /* TODO :remove this after debug */
+               DBG("there's no cert data");
+               return 0;
+       }
 
        DBG("der path %s\n", path);
        fp = fopen(path, "r");
+       if (!fp) {
+               connman_error("Failed to open file");
+               return -EINVAL;
+       }
+
        *cert = d2i_X509_fp(fp, NULL);
+       if (!fp) {
+               connman_error("Failed to read der file");
+               err = -EINVAL;
+       }
+
        fclose(fp);
-       return;
+       return err;
 }
 
-static void read_pem_file(const char *path, X509 **cert)
+static int read_pem_file(const char *path, X509 **cert)
 {
        FILE *fp = NULL;
+       int err = 0;
+
+       if(!path || !cert) {
+               /* TODO :remove this after debug */
+               DBG("there's no cert data");
+               return 0;
+       }
 
        DBG("pem path %s\n", path);
        fp = fopen(path, "r");
+       if (!fp) {
+               connman_error("Failed to open file");
+               return -EINVAL;
+       }
+
        *cert = PEM_read_X509(fp, cert, NULL, NULL);
+       if (!fp) {
+               connman_error("Failed to read pem file");
+               err = -EINVAL;
+       }
+
        fclose(fp);
-       return;
+       return err;
 }
 
-static void read_pkcs12_file(const char *path, const char *pass, EVP_PKEY **pkey, X509 **cert, STACK_OF(X509) **ca)
+static int read_pkcs12_file(const char *path, const char *pass, EVP_PKEY **pkey, X509 **cert, STACK_OF(X509) **ca)
 {
        FILE *fp = NULL;
        PKCS12 *p12;
+       int err = 0;
+
+       if(!path || !pass || !pkey || !cert || !ca) {
+               /* TODO :remove this after debug */
+               DBG("there's no cert data");
+               return 0;
+       }
 
        DBG("pkcs12 path %s\n", path);
        fp = fopen(path, "r");
+       if (!fp) {
+               connman_error("Failed to open file");
+               return -EINVAL;
+       }
 
        p12 = d2i_PKCS12_fp(fp, NULL);
        if (!p12) {
-               print_openssl_error();
+               connman_error("Failed to open pkcs12");
                fclose(fp);
-               return;
+               return -EINVAL;
        }
 
        if (!PKCS12_parse(p12, pass, pkey, cert, ca)) {
-               print_openssl_error();
-               fclose(fp);
-               return;
+               connman_error("Failed to parse pkcs12");
+               err = -EINVAL;
        }
 
        PKCS12_free(p12);
-       return;
+       fclose(fp);
+
+       return err;
 }
 
 static char *get_private_key_str(struct openssl_private_data *data)
@@ -410,29 +462,30 @@ static char * get_nth_ca_cert_str(struct openssl_private_data *data, int num)
        return get_cert_str(cert);
 }
 
-static void extract_cert_info(const char *path, const char *pass, struct openssl_private_data *data)
+static int extract_cert_info(const char *path, const char *pass, struct openssl_private_data *data)
 {
+       int err = 0;
        if(!path || !data) {
                /* TODO :remove this after debug */
                DBG("there's no cert data");
-               return;
+               return 0;
        }
 
        switch (get_cert_type(path)) {
        case CERT_TYPE_DER:
-               read_der_file(path, &(data->local_cert));
+               err = read_der_file(path, &(data->local_cert));
                break;
        case CERT_TYPE_PEM:
-               read_pem_file(path, &(data->local_cert));
+               err = read_pem_file(path, &(data->local_cert));
                break;
        case CERT_TYPE_PKCS12:
-               read_pkcs12_file(path, pass, &(data->private_key), &(data->local_cert), &(data->ca_certs));
+               err = read_pkcs12_file(path, pass, &(data->private_key), &(data->local_cert), &(data->ca_certs));
                break;
        default:
                break;
        }
 
-       return;
+       return err;
 }
 
 static void free_openssl_private_data(struct openssl_private_data *data)
@@ -468,6 +521,14 @@ static int ipsec_notify(DBusMessage *msg, struct vpn_provider *provider)
        return 0;
 }
 
+static void ipsec_set_event_cb(vpn_event_callback event_cb, struct vpn_provider *provider)
+{
+       DBG("set event cb!");
+       event_data.event_cb = event_cb;
+       event_data.event_user_data = provider;
+       return;
+}
+
 static int ipsec_is_same_auth(const char* req, const char* target)
 {
        if (req == NULL || target == NULL)
@@ -524,6 +585,7 @@ static void ipsec_add_default_child_sa_data(struct vpn_provider *provider, VICIS
                for (list = NULL; ikev1_esp_proposals[i] != NULL; i++)
                        list = g_slist_append(list, g_strdup(ikev1_esp_proposals[i]));
                vici_add_list(child, "esp_proposals", list, "net");
+               g_slist_free_full(list, g_free);
                list = NULL;
        } else {
                vici_add_kvl(child, "esp_proposals", ikev2_esp_proposals, "net");
@@ -541,11 +603,11 @@ static void ipsec_add_default_conn_data(struct vpn_provider *provider, VICISecti
                for (list = NULL; ikev1_proposals[i] != NULL; i++)
                        list = g_slist_append(list, g_strdup(ikev1_proposals[i]));
                vici_add_list(conn, "proposals", list, NULL);
+               g_slist_free_full(list, g_free);
                list = NULL;
 
                if (g_strcmp0(vpn_provider_get_string(provider, "IPsec.LocalAuth"), "psk") == 0)
                        vici_add_kv(conn, "aggressive", "yes", NULL);
-
        } else {
                vici_add_kvl(conn, "proposals", ikev2_proposals, NULL);
        }
@@ -722,7 +784,7 @@ static char *load_file_from_path(const char *path)
        size_t  file_size = 0;
        char *file_buff = NULL;
 
-       if (path) {
+       if (!path) {
                connman_error("File path is NULL\n");
                return NULL;
        }
@@ -841,6 +903,7 @@ static int ipsec_load_cert(struct vpn_provider *provider)
        DBG("CertType: %s, CertFalg: %s,CertData: %s", type, flag, data);
        if (!type || ! flag || !data) {
                connman_error("invalid certification information");
+               g_free(data);
                return -EINVAL;
        }
 
@@ -853,16 +916,71 @@ static int ipsec_load_cert(struct vpn_provider *provider)
        return ret;
 }
 
-void connect_reply_cb(int err, void *user_data)
+static int ipsec_terminate(struct vpn_provider *provider)
+{
+       VICISection *sect;
+       int ret = 0;
+
+       sect = vici_create_section(NULL);
+       if (!sect)
+               return -ENOMEM;
+
+       vici_add_kv(sect, "child", "net", NULL);
+       vici_add_kv(sect, "ike", vpn_provider_get_string(provider, "Name"), NULL);
+       vici_add_kv(sect, "timeout", "-1", NULL);
+       ret = vici_send_request(vici_client, VICI_CMD_TERMINATE, sect);
+       if (ret < 0)
+               connman_error("vici_send_request failed");
+
+       vici_destroy_section(sect);
+
+       return ret;
+}
+
+static void request_reply_cb(int err, void *user_data)
 {
        struct ipsec_private_data *data;
 
        data = (struct ipsec_private_data *)user_data;
-       data->cb(data->provider, data->user_data, err);
+       DBG("request reply cb");
+
+       if(err != 0) {
+               if (event_data.event_cb)
+                       event_data.event_cb(event_data.event_user_data, VPN_STATE_FAILURE);
+               /* TODO: Does close socket needed? */
+       } else {
+               DBG("Series of requests are succeeded");
+               /* TODO: Not sure about below */
+               if (event_data.event_cb)
+                       event_data.event_cb(event_data.event_user_data, VPN_STATE_CONNECT);
+       }
 
        free_private_data(data);
 }
 
+static void ipsec_vici_event_cb(VICIClientEvent event, void *user_data)
+{
+       struct vpn_provider *provider;
+
+       provider = (struct vpn_provider *)user_data;
+       if (!provider) {
+               DBG("Invalid user data");
+               return;
+       }
+
+       if(event == VICI_EVENT_CHILD_UP) {
+               if (event_data.event_cb)
+                       event_data.event_cb(event_data.event_user_data, VPN_STATE_READY);
+       } else if (event == VICI_EVENT_CHILD_DOWN) {
+               if (event_data.event_cb)
+                       event_data.event_cb(event_data.event_user_data, VPN_STATE_DISCONNECT);
+       } else {
+               DBG("Unknown event");
+       }
+
+       return;
+}
+
 static struct ipsec_private_data* create_ipsec_private_data(struct vpn_provider *provider,
                vpn_provider_connect_cb_t cb, void* user_data)
 {
@@ -876,8 +994,8 @@ static struct ipsec_private_data* create_ipsec_private_data(struct vpn_provider
        init_openssl();
 
        data->provider = provider;
-       data->cb = cb;
-       data->user_data = user_data;
+       data->connect_cb = cb;
+       data->connect_user_data = user_data;
        return data;
 }
 
@@ -891,7 +1009,7 @@ static void vici_connect(struct ipsec_private_data *data)
                IPSEC_ERROR_CHECK_GOTO(-1, done, "Invalid data parameter");
 
        provider = data->provider;
-       cb = data->cb;
+       cb = data->connect_cb;
        if (!provider || !cb)
                IPSEC_ERROR_CHECK_GOTO(-1, done, "Invalid provider or callback");
 
@@ -906,8 +1024,15 @@ static void vici_connect(struct ipsec_private_data *data)
        /* TODO :remove this after debug */
        DBG("success to initialize vici socket");
 
-       vici_set_connect_reply_cb(vici_client, (vici_connect_reply_cb)connect_reply_cb, data);
+       vici_set_request_reply_cb(vici_client, (vici_request_reply_cb)request_reply_cb, data);
+       /*
+        * Sets child-updown event
+        */
+       err = vici_set_event_cb(vici_client, (vici_event_cb)ipsec_vici_event_cb, provider);
+       IPSEC_ERROR_CHECK_GOTO(err, done, "register event failed");
 
+       /* TODO :remove this after debug */
+       DBG("success to vici_set_event_cb");
        /*
         * Send the load-conn command
         */
@@ -968,8 +1093,9 @@ static void vici_connect(struct ipsec_private_data *data)
 
 done:
        /* refer to connect_cb on vpn-provider.c for cb */
-       if(err != 0 && cb)
-               cb(provider, data->user_data, -err);
+       if(cb)
+               cb(provider, data->connect_user_data, -err);
+       /* TODO: Does close socket needed? when err is not zero */
 
        return;
 }
@@ -998,6 +1124,8 @@ static void monitor_vici_socket(struct ipsec_private_data *data)
        if (error) {
                connman_error("g_file_monitor_directory failed: %s / %d", error->message, error->code);
                g_error_free(error);
+               if(event_data.event_cb)
+                       event_data.event_cb(event_data.event_user_data, VPN_STATE_FAILURE);
                return;
        }
        /* TODO :remove this after debug */
@@ -1017,6 +1145,13 @@ static void check_vici_socket(struct ipsec_private_data *data)
        }
 }
 
+static void ipsec_died(struct connman_task *task, int exit_code, void *user_data)
+{
+       DBG("task %p exit_code %d", task, exit_code);
+       unlink(VICI_DEFAULT_URI);
+       vpn_died(task, exit_code, user_data);
+}
+
 static int ipsec_connect(struct vpn_provider *provider,
                        struct connman_task *task, const char *if_name,
                        vpn_provider_connect_cb_t cb, const char *dbus_sender,
@@ -1035,17 +1170,27 @@ static int ipsec_connect(struct vpn_provider *provider,
        /*
         * Start charon daemon using ipsec script of strongSwan.
         */
-       err = connman_task_run(task, vpn_died, provider, NULL, NULL, NULL);
+       err = connman_task_run(task, ipsec_died, provider, NULL, NULL, NULL);
        if (err < 0) {
                connman_error("charon start failed");
                if (cb)
                        cb(provider, user_data, err);
+
+               g_free(data);
                return err;
        }
 
        path = vpn_provider_get_string(provider, "IPsec.LocalCerts");
        pass = vpn_provider_get_string(provider, "IPsec.LocalCertPass");
-       extract_cert_info(path, pass, &(data->openssl_data));
+       err = extract_cert_info(path, pass, &(data->openssl_data));
+       if (err < 0) {
+               connman_error("extract cert info failed");
+               if (cb)
+                       cb(provider, user_data, err);
+
+               g_free(data);
+               return err;
+       }
 
        check_vici_socket(data);
 //     g_usleep(G_USEC_PER_SEC);
@@ -1143,14 +1288,22 @@ static int ipsec_save(struct vpn_provider *provider, GKeyFile *keyfile)
 static void ipsec_disconnect(struct vpn_provider *provider)
 {
        int err = 0;
+       /*
+        * Send the terminate command
+        */
+       err = ipsec_terminate(provider);
+       IPSEC_ERROR_CHECK_RETURN(err, "terminate failed");
 
        err = vici_deinitialize(vici_client);
        IPSEC_ERROR_CHECK_RETURN(err, "failed to deinitialize vici_client");
+
+       return;
 }
 
 static struct vpn_driver vpn_driver = {
        .flags = VPN_FLAG_NO_TUN,
        .notify = ipsec_notify,
+       .set_event_cb = ipsec_set_event_cb,
        .connect = ipsec_connect,
        .error_code = ipsec_error_code,
        .save = ipsec_save,
@@ -1161,6 +1314,9 @@ static int ipsec_init(void)
 {
        connection = connman_dbus_get_connection();
 
+       event_data.event_cb = NULL;
+       event_data.event_user_data = NULL;
+
        return vpn_register("ipsec", &vpn_driver, IPSEC);
 }