Add ipsec plugin
[platform/upstream/connman.git] / vpn / plugins / ipsec.c
1 /*
2  *
3  *  ConnMan VPN daemon
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License version 2 as
7  *  published by the Free Software Foundation.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  *
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #include <stdio.h>
29 #include <net/if.h>
30
31 #include <glib.h>
32
33 #define CONNMAN_API_SUBJECT_TO_CHANGE
34 #include <connman/plugin.h>
35 #include <connman/log.h>
36 #include <connman/task.h>
37 #include <connman/dbus.h>
38 #include <connman/ipconfig.h>
39
40 #include "../vpn-provider.h"
41
42 #include "vpn.h"
43 #include "ipsec.h"
44 #include "vici-client.h"
45
46 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
47
48 static DBusConnection *connection;
49
50 struct {
51         const char *cm_opt;
52         const char *vici_key;
53         const char *section;
54 } ipsec_conn_options[] = {
55         {"IPsec.Version", "version", NULL},
56         {"IPsec.LocalAddrs", "local_addrs", NULL},
57         {"IPsec.RemoteAddrs", "remote_addrs", NULL},
58         {"IPsec.LocalAuth", "auth", "local"},
59         {"IPsec.RemoteAuth", "auth", "remote"},
60 };
61
62 /*
63  * IPsec.LocalID
64  * IPsec.RemoteTS
65  */
66 struct {
67         const char *cm_opt;
68         const char *vici_type;
69 } ipsec_shared_options[] = {
70         {"IPsec.LocalXauthID", NULL},
71         {"IPsec.XauthSecret", "XAUTH"},
72         {"IPsec.IKESecret", "IKE"},
73 };
74
75 struct {
76         const char *cm_opt;
77         const char *vici_type;
78         const char *vici_flag;
79 } ipsec_cert_options[] = {
80         {"IPsec.LocalCert", "X509", NULL},
81         {"IPsec.RemoteCert", "X509", NULL},
82         {"IPsec.CACert", "X509", "CA"},
83 };
84
85
86 static int ipsec_notify(DBusMessage *msg, struct vpn_provider *provider)
87 {
88         return 0;
89 }
90
91 static void vici_destroy_section(struct section* sect)
92 {
93         g_hash_table_destroy(sect->elm);
94         g_hash_table_destroy(sect->subsection);
95         g_free(sect);
96 }
97
98 static void free_section(gpointer data)
99 {
100         struct section* sect = (struct section*)data;
101         vici_destroy_section(sect);
102 }
103
104 static struct section* vici_create_section(const char* name)
105 {
106         struct section* sect;
107
108         sect = g_try_new0(struct section, 1);
109         if (!sect) {
110                 connman_error("Failed to create section");
111                 return NULL;
112         }
113
114         sect->name = g_strdup(name);
115         sect->elm = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
116         sect->subsection = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_section);
117         return sect;
118 }
119
120 static int vici_section_add_kv(struct section* sect, const char* key, const char* value)
121 {
122         if (sect == NULL || key == NULL || value == NULL) {
123                 connman_error("invalid parameter");
124                 return -1;
125         }
126
127         g_hash_table_insert(sect->elm, g_strdup(key), g_strdup(value));
128         return 0;
129 }
130
131 static int vici_section_add_subsection(struct section* sect, const char* name, struct section* child)
132 {
133         if (sect == NULL || name == NULL || child == NULL) {
134                 connman_error("invalid parameter");
135                 return -1;
136         }
137
138         g_hash_table_insert(sect->subsection, g_strdup(name), child);
139         return 0;
140 }
141
142
143 static struct section* vici_section_get_subsection(struct section* sect, const char* name)
144 {
145         struct section* sub = g_hash_table_lookup(sect->subsection, name);
146         if (sub == NULL) {
147                 sub = vici_create_section(name);
148                 vici_section_add_subsection(sect, name, sub);
149         }
150         return sub;
151 }
152
153 static int vici_section_add_element(struct section* sect, const char* key,
154                 const char* value, const char* subsection)
155 {
156         struct section* target = sect;
157
158         if (sect == NULL || key == NULL) {
159                 connman_error("invalid parameter");
160                 return -1;
161         }
162
163         if (subsection)
164                 target = vici_section_get_subsection(sect, subsection);
165
166         vici_section_add_kv(target, key, value);
167         return 0;
168 }
169
170 static int ipsec_is_same_auth(const char* req, const char* target)
171 {
172         if (req == NULL || target == NULL)
173                 return 0;
174         return (g_strcmp0(req, target) == 0);
175 }
176
177 static int vici_load_cert(const char* type, const char* flag, const char* data)
178 {
179         struct section *sect;
180         sect = vici_create_section("");
181         vici_section_add_element(sect, "type", type, NULL);
182         vici_section_add_element(sect, "flag", flag, NULL);
183         vici_section_add_element(sect, "data", data, NULL);
184
185         vici_client_send_request(VICI_REQUEST_LOAD_CERT, sect);
186
187         vici_destroy_section(sect);
188
189         return 0;
190 }
191
192 static int ipsec_load_conn(struct vpn_provider *provider)
193 {
194         const char *key;
195         const char *value;
196         const char *subsection;
197         struct section *sect;
198         int i;
199
200         value = vpn_provider_get_string(provider, "Name");
201         sect = vici_create_section(value);
202
203         for (i = 0; i < (int)ARRAY_SIZE(ipsec_conn_options); i++) {
204                 key = ipsec_conn_options[i].vici_key;
205                 value = vpn_provider_get_string(provider, ipsec_conn_options[i].cm_opt);
206                 subsection = ipsec_conn_options[i].section;
207                 vici_section_add_element(sect, key, value, subsection);
208         }
209
210         vici_client_send_request(VICI_REQUEST_LOAD_CONN, sect);
211
212         vici_destroy_section(sect);
213
214         return 0;
215 }
216
217 static int ipsec_load_shared(struct vpn_provider *provider)
218 {
219         const char *type;
220         const char *data;
221         const char *owner;
222         const char *auth_type;
223         struct section *sect;
224
225         sect = vici_create_section("");
226
227         auth_type = vpn_provider_get_string(provider, "IPsec.LocalAuth");
228         if (ipsec_is_same_auth(auth_type, IPSEC_AUTH_PSK)) {
229                 type = VICI_SHARED_TYPE_PSK;
230                 data = vpn_provider_get_string(provider, "IPsec.IKESecret");
231         } else if (ipsec_is_same_auth(auth_type, IPSEC_AUTH_XAUTH)) {
232                 type = VICI_SHARED_TYPE_XAUTH;
233                 data = vpn_provider_get_string(provider, "IPsec.XauthSecret");
234         } else {
235                 connman_error("invalid auth type: %s", auth_type);
236                 return -1;
237         }
238
239         owner = vpn_provider_get_string(provider, "IPsec.LocalXauthID");
240
241         vici_section_add_element(sect, "type", type, NULL);
242         vici_section_add_element(sect, "data", data, NULL);
243         vici_section_add_element(sect, "owner", owner, NULL);
244
245         vici_client_send_request(VICI_REQUEST_LOAD_SHARED, sect);
246
247         vici_destroy_section(sect);
248
249         return 0;
250 }
251
252 static int ipsec_load_cert(struct vpn_provider *provider)
253 {
254         const char *type;
255         const char *flag;
256         const char *data;
257         const char *auth_type;
258         int i;
259
260         auth_type = vpn_provider_get_string(provider, "IPsec.LocalAuth");
261         if (!ipsec_is_same_auth(auth_type, IPSEC_AUTH_RSA)) {
262                 connman_error("invalid auth type: %s", auth_type);
263                 return -1;
264         }
265
266         for (i = 0; i < (int)ARRAY_SIZE(ipsec_cert_options); i++) {
267                 type = ipsec_cert_options[i].vici_type;;
268                 flag = ipsec_cert_options[i].vici_flag;
269                 data = vpn_provider_get_string(provider, ipsec_cert_options[i].cm_opt);
270                 vici_load_cert(type, flag, data);
271         }
272
273         return 0;
274 }
275
276 static int ipsec_connect(struct vpn_provider *provider,
277                         struct connman_task *task, const char *if_name,
278                         vpn_provider_connect_cb_t cb, const char *dbus_sender,
279                         void *user_data)
280 {
281         int err = 0;
282
283         /*
284          * Start charon daemon using ipsec script of strongSwan.
285          */
286         connman_task_add_argument(task, "start", NULL);
287         err = connman_task_run(task, vpn_died, provider, NULL, NULL, NULL);
288         IPSEC_ERROR_CHECK_GOTO(err, done, "ipsec start failed");
289
290         /*
291          * Initialize vici client
292          */
293         err = vici_client_initialize();
294         IPSEC_ERROR_CHECK_GOTO(err, done, "failed to initialize vici_client");
295
296         /*
297          * Send the load-conn command
298          */
299         err = ipsec_load_conn(provider);
300         IPSEC_ERROR_CHECK_GOTO(err, done, "load-conn failed");
301
302         /*
303          * Send the load-shared command for PSK or XAUTH
304          */
305         err = ipsec_load_shared(provider);
306         IPSEC_ERROR_CHECK_GOTO(err, done, "load-shared failed");
307
308         /*
309          * Send the load-cert command
310          */
311         err = ipsec_load_cert(provider);
312         IPSEC_ERROR_CHECK_GOTO(err, done, "load-cert failed");
313
314 done:
315         if (cb)
316                 cb(provider, user_data, err);
317
318         return err;
319 }
320
321 static int ipsec_error_code(struct vpn_provider *provider, int exit_code)
322 {
323         return 0;
324 }
325
326 static int ipsec_save(struct vpn_provider *provider, GKeyFile *keyfile)
327 {
328         return 0;
329 }
330
331 static void ipsec_disconnect(struct vpn_provider *provider)
332 {
333         int err = 0;
334
335         err = vici_client_deinitialize();
336         IPSEC_ERROR_CHECK_RETURN(err, "failed to deinitialize vici_client");
337 }
338
339 static struct vpn_driver vpn_driver = {
340         .flags = VPN_FLAG_NO_TUN,
341         .notify = ipsec_notify,
342         .connect = ipsec_connect,
343         .error_code = ipsec_error_code,
344         .save = ipsec_save,
345         .disconnect = ipsec_disconnect,
346 };
347
348 static int ipsec_init(void)
349 {
350         connection = connman_dbus_get_connection();
351
352         return vpn_register("ipsec", &vpn_driver, IPSEC);
353 }
354
355 static void ipsec_exit(void)
356 {
357         vpn_unregister("ipsec");
358
359         dbus_connection_unref(connection);
360 }
361
362 CONNMAN_PLUGIN_DEFINE(ipsec, "IPSec plugin", VERSION,
363         CONNMAN_PLUGIN_PRIORITY_DEFAULT, ipsec_init, ipsec_exit)