nm-openconnect-service.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
  2. /* NetworkManager -- Network link manager
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  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 along
  15. * with this program; if not, write to the Free Software Foundation, Inc.,
  16. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  17. *
  18. * Copyright © 2008 - 2009 Intel Corporation.
  19. *
  20. * Based on nm-vpnc-service.c:
  21. * Copyright © 2005 - 2008 Red Hat, Inc.
  22. * Copyright © 2007 - 2008 Novell, Inc.
  23. */
  24. #ifdef HAVE_CONFIG_H
  25. #include <config.h>
  26. #endif
  27. #include <stdio.h>
  28. #include <string.h>
  29. #include <stdlib.h>
  30. #include <unistd.h>
  31. #include <fcntl.h>
  32. #include <signal.h>
  33. #include <sys/stat.h>
  34. #include <sys/wait.h>
  35. #include <errno.h>
  36. #include <sys/ioctl.h>
  37. #include <linux/if_tun.h>
  38. #include <net/if.h>
  39. #include <pwd.h>
  40. #include <grp.h>
  41. #include <locale.h>
  42. #include <glib/gi18n.h>
  43. #include <nm-setting-vpn.h>
  44. #include "nm-openconnect-service.h"
  45. #include "nm-utils.h"
  46. #if !defined(DIST_VERSION)
  47. # define DIST_VERSION VERSION
  48. #endif
  49. G_DEFINE_TYPE (NMOpenconnectPlugin, nm_openconnect_plugin, NM_TYPE_VPN_PLUGIN)
  50. typedef struct {
  51. GPid pid;
  52. char *tun_name;
  53. } NMOpenconnectPluginPrivate;
  54. #define NM_OPENCONNECT_PLUGIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_OPENCONNECT_PLUGIN, NMOpenconnectPluginPrivate))
  55. static const char *openconnect_binary_paths[] =
  56. {
  57. "/usr/bin/openconnect",
  58. "/usr/sbin/openconnect",
  59. "/usr/local/bin/openconnect",
  60. "/usr/local/sbin/openconnect",
  61. "/opt/bin/openconnect",
  62. "/opt/sbin/openconnect",
  63. NULL
  64. };
  65. #define NM_OPENCONNECT_HELPER_PATH LIBEXECDIR"/nm-openconnect-service-openconnect-helper"
  66. typedef struct {
  67. const char *name;
  68. GType type;
  69. gint int_min;
  70. gint int_max;
  71. } ValidProperty;
  72. static ValidProperty valid_properties[] = {
  73. { NM_OPENCONNECT_KEY_GATEWAY, G_TYPE_STRING, 0, 0 },
  74. { NM_OPENCONNECT_KEY_CACERT, G_TYPE_STRING, 0, 0 },
  75. { NM_OPENCONNECT_KEY_AUTHTYPE, G_TYPE_STRING, 0, 0 },
  76. { NM_OPENCONNECT_KEY_USERCERT, G_TYPE_STRING, 0, 0 },
  77. { NM_OPENCONNECT_KEY_PRIVKEY, G_TYPE_STRING, 0, 0 },
  78. { NM_OPENCONNECT_KEY_MTU, G_TYPE_STRING, 0, 0 },
  79. { NM_OPENCONNECT_KEY_PEM_PASSPHRASE_FSID, G_TYPE_BOOLEAN, 0, 0 },
  80. { NM_OPENCONNECT_KEY_PROXY, G_TYPE_STRING, 0, 0 },
  81. { NM_OPENCONNECT_KEY_CSD_ENABLE, G_TYPE_BOOLEAN, 0, 0 },
  82. { NM_OPENCONNECT_KEY_CSD_WRAPPER, G_TYPE_STRING, 0, 0 },
  83. { NM_OPENCONNECT_KEY_TOKEN_MODE, G_TYPE_STRING, 0, 0 },
  84. { NM_OPENCONNECT_KEY_TOKEN_SECRET, G_TYPE_STRING, 0, 0 },
  85. { NULL, G_TYPE_NONE, 0, 0 }
  86. };
  87. static ValidProperty valid_secrets[] = {
  88. { NM_OPENCONNECT_KEY_COOKIE, G_TYPE_STRING, 0, 0 },
  89. { NM_OPENCONNECT_KEY_GATEWAY, G_TYPE_STRING, 0, 0 },
  90. { NM_OPENCONNECT_KEY_GWCERT, G_TYPE_STRING, 0, 0 },
  91. { NULL, G_TYPE_NONE, 0, 0 }
  92. };
  93. static uid_t tun_owner;
  94. static gid_t tun_group;
  95. static gboolean debug = FALSE;
  96. static GMainLoop *loop = NULL;
  97. typedef struct ValidateInfo {
  98. ValidProperty *table;
  99. GError **error;
  100. gboolean have_items;
  101. } ValidateInfo;
  102. static void
  103. validate_one_property (const char *key, const char *value, gpointer user_data)
  104. {
  105. ValidateInfo *info = (ValidateInfo *) user_data;
  106. int i;
  107. if (*(info->error))
  108. return;
  109. info->have_items = TRUE;
  110. /* 'name' is the setting name; always allowed but unused */
  111. if (!strcmp (key, NM_SETTING_NAME))
  112. return;
  113. for (i = 0; info->table[i].name; i++) {
  114. ValidProperty prop = info->table[i];
  115. long int tmp;
  116. if (strcmp (prop.name, key))
  117. continue;
  118. switch (prop.type) {
  119. case G_TYPE_STRING:
  120. return; /* valid */
  121. case G_TYPE_INT:
  122. errno = 0;
  123. tmp = strtol (value, NULL, 10);
  124. if (errno == 0 && tmp >= prop.int_min && tmp <= prop.int_max)
  125. return; /* valid */
  126. g_set_error (info->error,
  127. NM_VPN_PLUGIN_ERROR,
  128. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  129. _("invalid integer property '%s' or out of range [%d -> %d]"),
  130. key, prop.int_min, prop.int_max);
  131. break;
  132. case G_TYPE_BOOLEAN:
  133. if (!strcmp (value, "yes") || !strcmp (value, "no"))
  134. return; /* valid */
  135. g_set_error (info->error,
  136. NM_VPN_PLUGIN_ERROR,
  137. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  138. _("invalid boolean property '%s' (not yes or no)"),
  139. key);
  140. break;
  141. default:
  142. g_set_error (info->error,
  143. NM_VPN_PLUGIN_ERROR,
  144. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  145. _("unhandled property '%s' type %s"),
  146. key, g_type_name (prop.type));
  147. break;
  148. }
  149. }
  150. /* Did not find the property from valid_properties or the type did not match */
  151. if (!info->table[i].name && strncmp(key, "form:", 5)) {
  152. g_warning ("property '%s' unknown", key);
  153. if (0)
  154. g_set_error (info->error,
  155. NM_VPN_PLUGIN_ERROR,
  156. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  157. _("property '%s' invalid or not supported"),
  158. key);
  159. }
  160. }
  161. static gboolean
  162. nm_openconnect_properties_validate (NMSettingVPN *s_vpn, GError **error)
  163. {
  164. ValidateInfo info = { &valid_properties[0], error, FALSE };
  165. nm_setting_vpn_foreach_data_item (s_vpn, validate_one_property, &info);
  166. if (!info.have_items) {
  167. g_set_error (error,
  168. NM_VPN_PLUGIN_ERROR,
  169. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  170. "%s",
  171. _("No VPN configuration options."));
  172. return FALSE;
  173. }
  174. return *error ? FALSE : TRUE;
  175. }
  176. static gboolean
  177. nm_openconnect_secrets_validate (NMSettingVPN *s_vpn, GError **error)
  178. {
  179. ValidateInfo info = { &valid_secrets[0], error, FALSE };
  180. nm_setting_vpn_foreach_secret (s_vpn, validate_one_property, &info);
  181. if (!info.have_items) {
  182. g_set_error (error,
  183. NM_VPN_PLUGIN_ERROR,
  184. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  185. "%s",
  186. _("No VPN secrets!"));
  187. return FALSE;
  188. }
  189. return *error ? FALSE : TRUE;
  190. }
  191. static char *
  192. create_persistent_tundev(void)
  193. {
  194. struct passwd *pw;
  195. struct ifreq ifr;
  196. int fd;
  197. int i;
  198. pw = getpwnam(NM_OPENCONNECT_USER);
  199. if (!pw)
  200. return NULL;
  201. tun_owner = pw->pw_uid;
  202. tun_group = pw->pw_gid;
  203. fd = open("/dev/net/tun", O_RDWR);
  204. if (fd < 0) {
  205. perror("open /dev/net/tun");
  206. exit(EXIT_FAILURE);
  207. }
  208. memset(&ifr, 0, sizeof(ifr));
  209. ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
  210. for (i = 0; i < 256; i++) {
  211. sprintf(ifr.ifr_name, "vpn%d", i);
  212. if (!ioctl(fd, TUNSETIFF, (void *)&ifr))
  213. break;
  214. }
  215. if (i == 256)
  216. exit(EXIT_FAILURE);
  217. if (ioctl(fd, TUNSETOWNER, tun_owner) < 0) {
  218. perror("TUNSETOWNER");
  219. exit(EXIT_FAILURE);
  220. }
  221. if (ioctl(fd, TUNSETPERSIST, 1)) {
  222. perror("TUNSETPERSIST");
  223. exit(EXIT_FAILURE);
  224. }
  225. close(fd);
  226. g_warning("Created tundev %s\n", ifr.ifr_name);
  227. return g_strdup(ifr.ifr_name);
  228. }
  229. static void
  230. destroy_persistent_tundev(char *tun_name)
  231. {
  232. struct ifreq ifr;
  233. int fd;
  234. fd = open("/dev/net/tun", O_RDWR);
  235. if (fd < 0) {
  236. perror("open /dev/net/tun");
  237. exit(EXIT_FAILURE);
  238. }
  239. memset(&ifr, 0, sizeof(ifr));
  240. ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
  241. strcpy(ifr.ifr_name, tun_name);
  242. if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) {
  243. perror("TUNSETIFF");
  244. exit(EXIT_FAILURE);
  245. }
  246. if (ioctl(fd, TUNSETPERSIST, 0)) {
  247. perror("TUNSETPERSIST");
  248. exit(EXIT_FAILURE);
  249. }
  250. g_warning("Destroyed tundev %s\n", tun_name);
  251. close(fd);
  252. }
  253. static void openconnect_drop_child_privs(gpointer user_data)
  254. {
  255. char *tun_name = user_data;
  256. if (tun_name) {
  257. if (initgroups(NM_OPENCONNECT_USER, tun_group) ||
  258. setgid(tun_group) || setuid(tun_owner)) {
  259. g_warning ("Failed to drop privileges when spawning openconnect");
  260. exit (1);
  261. }
  262. }
  263. }
  264. static void
  265. openconnect_watch_cb (GPid pid, gint status, gpointer user_data)
  266. {
  267. NMOpenconnectPlugin *plugin = NM_OPENCONNECT_PLUGIN (user_data);
  268. NMOpenconnectPluginPrivate *priv = NM_OPENCONNECT_PLUGIN_GET_PRIVATE (plugin);
  269. guint error = 0;
  270. if (WIFEXITED (status)) {
  271. error = WEXITSTATUS (status);
  272. if (error != 0)
  273. g_warning ("openconnect exited with error code %d", error);
  274. }
  275. else if (WIFSTOPPED (status))
  276. g_warning ("openconnect stopped unexpectedly with signal %d", WSTOPSIG (status));
  277. else if (WIFSIGNALED (status))
  278. g_warning ("openconnect died with signal %d", WTERMSIG (status));
  279. else
  280. g_warning ("openconnect died from an unknown cause");
  281. /* Reap child if needed. */
  282. waitpid (priv->pid, NULL, WNOHANG);
  283. priv->pid = 0;
  284. if (priv->tun_name) {
  285. destroy_persistent_tundev (priv->tun_name);
  286. g_free (priv->tun_name);
  287. priv->tun_name = NULL;
  288. }
  289. /* Must be after data->state is set since signals use data->state */
  290. switch (error) {
  291. case 2:
  292. /* Couldn't log in due to bad user/pass */
  293. nm_vpn_plugin_failure (NM_VPN_PLUGIN (plugin), NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED);
  294. break;
  295. case 1:
  296. /* Other error (couldn't bind to address, etc) */
  297. nm_vpn_plugin_failure (NM_VPN_PLUGIN (plugin), NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED);
  298. break;
  299. default:
  300. break;
  301. }
  302. nm_vpn_plugin_set_state (NM_VPN_PLUGIN (plugin), NM_VPN_SERVICE_STATE_STOPPED);
  303. }
  304. static gint
  305. nm_openconnect_start_openconnect_binary (NMOpenconnectPlugin *plugin,
  306. NMSettingVPN *s_vpn,
  307. GError **error)
  308. {
  309. NMOpenconnectPluginPrivate *priv = NM_OPENCONNECT_PLUGIN_GET_PRIVATE (plugin);
  310. GPid pid;
  311. const char **openconnect_binary = NULL;
  312. GPtrArray *openconnect_argv;
  313. GSource *openconnect_watch;
  314. gint stdin_fd;
  315. const char *props_vpn_gw, *props_cookie, *props_cacert, *props_mtu, *props_gwcert, *props_proxy;
  316. /* Find openconnect */
  317. openconnect_binary = openconnect_binary_paths;
  318. while (*openconnect_binary != NULL) {
  319. if (g_file_test (*openconnect_binary, G_FILE_TEST_EXISTS))
  320. break;
  321. openconnect_binary++;
  322. }
  323. if (!*openconnect_binary) {
  324. g_set_error (error,
  325. NM_VPN_PLUGIN_ERROR,
  326. NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED,
  327. "%s",
  328. _("Could not find openconnect binary."));
  329. return -1;
  330. }
  331. /* The actual gateway to use (after redirection) comes from the auth
  332. dialog, so it's in the secrets hash not the properties */
  333. props_vpn_gw = nm_setting_vpn_get_secret (s_vpn, NM_OPENCONNECT_KEY_GATEWAY);
  334. if (!props_vpn_gw || !strlen (props_vpn_gw) ) {
  335. g_set_error (error,
  336. NM_VPN_PLUGIN_ERROR,
  337. NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED,
  338. "%s",
  339. _("No VPN gateway specified."));
  340. return -1;
  341. }
  342. props_cookie = nm_setting_vpn_get_secret (s_vpn, NM_OPENCONNECT_KEY_COOKIE);
  343. if (!props_cookie || !strlen (props_cookie)) {
  344. g_set_error (error,
  345. NM_VPN_PLUGIN_ERROR,
  346. NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED,
  347. "%s",
  348. _("No WebVPN cookie provided."));
  349. return -1;
  350. }
  351. props_gwcert = nm_setting_vpn_get_secret (s_vpn, NM_OPENCONNECT_KEY_GWCERT);
  352. props_cacert = nm_setting_vpn_get_data_item (s_vpn, NM_OPENCONNECT_KEY_CACERT);
  353. props_mtu = nm_setting_vpn_get_data_item (s_vpn, NM_OPENCONNECT_KEY_MTU);
  354. props_proxy = nm_setting_vpn_get_data_item (s_vpn, NM_OPENCONNECT_KEY_PROXY);
  355. openconnect_argv = g_ptr_array_new ();
  356. g_ptr_array_add (openconnect_argv, (gpointer) (*openconnect_binary));
  357. if (props_gwcert && strlen(props_gwcert)) {
  358. g_ptr_array_add (openconnect_argv, (gpointer) "--servercert");
  359. g_ptr_array_add (openconnect_argv, (gpointer) props_gwcert);
  360. } else if (props_cacert && strlen(props_cacert)) {
  361. g_ptr_array_add (openconnect_argv, (gpointer) "--cafile");
  362. g_ptr_array_add (openconnect_argv, (gpointer) props_cacert);
  363. }
  364. if (props_mtu && strlen(props_mtu)) {
  365. g_ptr_array_add (openconnect_argv, (gpointer) "--mtu");
  366. g_ptr_array_add (openconnect_argv, (gpointer) props_mtu);
  367. }
  368. if (props_proxy && strlen(props_proxy)) {
  369. g_ptr_array_add (openconnect_argv, (gpointer) "--proxy");
  370. g_ptr_array_add (openconnect_argv, (gpointer) props_proxy);
  371. }
  372. g_ptr_array_add (openconnect_argv, (gpointer) "--syslog");
  373. g_ptr_array_add (openconnect_argv, (gpointer) "--cookie-on-stdin");
  374. g_ptr_array_add (openconnect_argv, (gpointer) "--script");
  375. g_ptr_array_add (openconnect_argv, (gpointer) NM_OPENCONNECT_HELPER_PATH);
  376. priv->tun_name = create_persistent_tundev ();
  377. if (priv->tun_name) {
  378. g_ptr_array_add (openconnect_argv, (gpointer) "--interface");
  379. g_ptr_array_add (openconnect_argv, (gpointer) priv->tun_name);
  380. }
  381. g_ptr_array_add (openconnect_argv, (gpointer) props_vpn_gw);
  382. if (debug)
  383. g_ptr_array_add (openconnect_argv, (gpointer) "--verbose");
  384. g_ptr_array_add (openconnect_argv, NULL);
  385. if (!g_spawn_async_with_pipes (NULL, (char **) openconnect_argv->pdata, NULL,
  386. G_SPAWN_DO_NOT_REAP_CHILD,
  387. openconnect_drop_child_privs, priv->tun_name,
  388. &pid, &stdin_fd, NULL, NULL, error)) {
  389. g_ptr_array_free (openconnect_argv, TRUE);
  390. g_warning ("openconnect failed to start. error: '%s'", (*error)->message);
  391. return -1;
  392. }
  393. g_ptr_array_free (openconnect_argv, TRUE);
  394. g_message ("openconnect started with pid %d", pid);
  395. if (write(stdin_fd, props_cookie, strlen(props_cookie)) != strlen(props_cookie) ||
  396. write(stdin_fd, "\n", 1) != 1) {
  397. g_warning ("openconnect didn't eat the cookie we fed it");
  398. return -1;
  399. }
  400. close(stdin_fd);
  401. NM_OPENCONNECT_PLUGIN_GET_PRIVATE (plugin)->pid = pid;
  402. openconnect_watch = g_child_watch_source_new (pid);
  403. g_source_set_callback (openconnect_watch, (GSourceFunc) openconnect_watch_cb, plugin, NULL);
  404. g_source_attach (openconnect_watch, NULL);
  405. g_source_unref (openconnect_watch);
  406. return 0;
  407. }
  408. static gboolean
  409. real_connect (NMVPNPlugin *plugin,
  410. NMConnection *connection,
  411. GError **error)
  412. {
  413. NMSettingVPN *s_vpn;
  414. gint openconnect_fd = -1;
  415. s_vpn = nm_connection_get_setting_vpn (connection);
  416. g_assert (s_vpn);
  417. if (!nm_openconnect_properties_validate (s_vpn, error))
  418. goto out;
  419. if (!nm_openconnect_secrets_validate (s_vpn, error))
  420. goto out;
  421. if (debug)
  422. nm_connection_dump (connection);
  423. openconnect_fd = nm_openconnect_start_openconnect_binary (NM_OPENCONNECT_PLUGIN (plugin), s_vpn, error);
  424. if (!openconnect_fd)
  425. return TRUE;
  426. out:
  427. return FALSE;
  428. }
  429. static gboolean
  430. real_need_secrets (NMVPNPlugin *plugin,
  431. NMConnection *connection,
  432. char **setting_name,
  433. GError **error)
  434. {
  435. NMSettingVPN *s_vpn;
  436. g_return_val_if_fail (NM_IS_VPN_PLUGIN (plugin), FALSE);
  437. g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
  438. s_vpn = nm_connection_get_setting_vpn (connection);
  439. if (!s_vpn) {
  440. g_set_error (error,
  441. NM_VPN_PLUGIN_ERROR,
  442. NM_VPN_PLUGIN_ERROR_CONNECTION_INVALID,
  443. "%s",
  444. "Could not process the request because the VPN connection settings were invalid.");
  445. return FALSE;
  446. }
  447. /* We just need the WebVPN cookie, and the final IP address of the gateway
  448. (after HTTP redirects, which do happen). All the certificate/SecurID
  449. nonsense can be handled for us, in the user's context, by auth-dialog */
  450. if (!nm_setting_vpn_get_secret (s_vpn, NM_OPENCONNECT_KEY_GATEWAY)) {
  451. *setting_name = NM_SETTING_VPN_SETTING_NAME;
  452. return TRUE;
  453. }
  454. if (!nm_setting_vpn_get_secret (s_vpn, NM_OPENCONNECT_KEY_COOKIE)) {
  455. *setting_name = NM_SETTING_VPN_SETTING_NAME;
  456. return TRUE;
  457. }
  458. if (!nm_setting_vpn_get_secret (s_vpn, NM_OPENCONNECT_KEY_GWCERT)) {
  459. *setting_name = NM_SETTING_VPN_SETTING_NAME;
  460. return TRUE;
  461. }
  462. return FALSE;
  463. }
  464. static gboolean
  465. ensure_killed (gpointer data)
  466. {
  467. int pid = GPOINTER_TO_INT (data);
  468. if (kill (pid, 0) == 0)
  469. kill (pid, SIGKILL);
  470. return FALSE;
  471. }
  472. static gboolean
  473. real_disconnect (NMVPNPlugin *plugin,
  474. GError **err)
  475. {
  476. NMOpenconnectPluginPrivate *priv = NM_OPENCONNECT_PLUGIN_GET_PRIVATE (plugin);
  477. if (priv->pid) {
  478. if (kill (priv->pid, SIGTERM) == 0)
  479. g_timeout_add (2000, ensure_killed, GINT_TO_POINTER (priv->pid));
  480. else
  481. kill (priv->pid, SIGKILL);
  482. g_message ("Terminated openconnect daemon with PID %d.", priv->pid);
  483. priv->pid = 0;
  484. }
  485. return TRUE;
  486. }
  487. static void
  488. nm_openconnect_plugin_init (NMOpenconnectPlugin *plugin)
  489. {
  490. }
  491. static void
  492. nm_openconnect_plugin_class_init (NMOpenconnectPluginClass *openconnect_class)
  493. {
  494. GObjectClass *object_class = G_OBJECT_CLASS (openconnect_class);
  495. NMVPNPluginClass *parent_class = NM_VPN_PLUGIN_CLASS (openconnect_class);
  496. g_type_class_add_private (object_class, sizeof (NMOpenconnectPluginPrivate));
  497. /* virtual methods */
  498. parent_class->connect = real_connect;
  499. parent_class->need_secrets = real_need_secrets;
  500. parent_class->disconnect = real_disconnect;
  501. }
  502. NMOpenconnectPlugin *
  503. nm_openconnect_plugin_new (void)
  504. {
  505. return (NMOpenconnectPlugin *) g_object_new (NM_TYPE_OPENCONNECT_PLUGIN,
  506. NM_VPN_PLUGIN_DBUS_SERVICE_NAME, NM_DBUS_SERVICE_OPENCONNECT,
  507. NULL);
  508. }
  509. static void
  510. signal_handler (int signo)
  511. {
  512. if (signo == SIGINT || signo == SIGTERM)
  513. g_main_loop_quit (loop);
  514. }
  515. static void
  516. setup_signals (void)
  517. {
  518. struct sigaction action;
  519. sigset_t mask;
  520. sigemptyset (&mask);
  521. action.sa_handler = signal_handler;
  522. action.sa_mask = mask;
  523. action.sa_flags = 0;
  524. sigaction (SIGTERM, &action, NULL);
  525. sigaction (SIGINT, &action, NULL);
  526. }
  527. static void
  528. quit_mainloop (NMOpenconnectPlugin *plugin, gpointer user_data)
  529. {
  530. g_main_loop_quit ((GMainLoop *) user_data);
  531. }
  532. int main (int argc, char *argv[])
  533. {
  534. NMOpenconnectPlugin *plugin;
  535. gboolean persist = FALSE;
  536. GOptionContext *opt_ctx = NULL;
  537. GOptionEntry options[] = {
  538. { "persist", 0, 0, G_OPTION_ARG_NONE, &persist, N_("Don't quit when VPN connection terminates"), NULL },
  539. { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, N_("Enable verbose debug logging (may expose passwords)"), NULL },
  540. {NULL}
  541. };
  542. #if !GLIB_CHECK_VERSION (2, 35, 0)
  543. g_type_init ();
  544. #endif
  545. /* locale will be set according to environment LC_* variables */
  546. setlocale (LC_ALL, "");
  547. bindtextdomain (GETTEXT_PACKAGE, NM_OPENCONNECT_LOCALEDIR);
  548. bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
  549. textdomain (GETTEXT_PACKAGE);
  550. /* Parse options */
  551. opt_ctx = g_option_context_new (NULL);
  552. g_option_context_set_translation_domain (opt_ctx, GETTEXT_PACKAGE);
  553. g_option_context_set_ignore_unknown_options (opt_ctx, FALSE);
  554. g_option_context_set_help_enabled (opt_ctx, TRUE);
  555. g_option_context_add_main_entries (opt_ctx, options, NULL);
  556. g_option_context_set_summary (opt_ctx,
  557. _("nm-openconnect-service provides integrated "
  558. "Cisco AnyConnect SSL VPN capability to NetworkManager."));
  559. g_option_context_parse (opt_ctx, &argc, &argv, NULL);
  560. g_option_context_free (opt_ctx);
  561. if (getenv ("OPENCONNECT_DEBUG"))
  562. debug = TRUE;
  563. if (debug)
  564. g_message ("nm-openconnect-service (version " DIST_VERSION ") starting...");
  565. if (system ("/sbin/modprobe tun") == -1)
  566. exit (EXIT_FAILURE);
  567. plugin = nm_openconnect_plugin_new ();
  568. if (!plugin)
  569. exit (EXIT_FAILURE);
  570. loop = g_main_loop_new (NULL, FALSE);
  571. if (!persist)
  572. g_signal_connect (plugin, "quit", G_CALLBACK (quit_mainloop), loop);
  573. setup_signals ();
  574. g_main_loop_run (loop);
  575. g_main_loop_unref (loop);
  576. g_object_unref (plugin);
  577. exit (EXIT_SUCCESS);
  578. }