nm-ssh-service.c 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781
  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
  2. /* nm-ssh-service - ssh integration with NetworkManager
  3. *
  4. * Copyright (C) 2013 Dan Fruehauf <malkodan@gmail.com>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License along
  17. * with this program; if not, write to the Free Software Foundation, Inc.,
  18. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  19. *
  20. * $Id: nm-ssh-service.c 4232 2008-10-29 09:13:40Z tambeti $
  21. *
  22. */
  23. #ifdef HAVE_CONFIG_H
  24. #include <config.h>
  25. #endif
  26. #include <glib/gi18n.h>
  27. #include <gio/gio.h>
  28. #include <dbus/dbus.h>
  29. #include <dbus/dbus-glib-lowlevel.h>
  30. #include <dbus/dbus-glib.h>
  31. #include <stdio.h>
  32. #include <string.h>
  33. #include <stdlib.h>
  34. #include <unistd.h>
  35. #include <fcntl.h>
  36. #include <signal.h>
  37. #include <sys/stat.h>
  38. #include <sys/wait.h>
  39. #include <sys/socket.h>
  40. #include <sys/types.h>
  41. #include <netinet/in.h>
  42. #include <arpa/inet.h>
  43. #include <netdb.h>
  44. #include <ctype.h>
  45. #include <errno.h>
  46. #include <pwd.h>
  47. #include <locale.h>
  48. #include <NetworkManager.h>
  49. #include <NetworkManagerVPN.h>
  50. #include <nm-setting-vpn.h>
  51. #include "nm-ssh-service.h"
  52. #include "nm-utils.h"
  53. #if !defined(DIST_VERSION)
  54. # define DIST_VERSION VERSION
  55. #endif
  56. static gboolean debug = FALSE;
  57. static GMainLoop *loop = NULL;
  58. G_DEFINE_TYPE (NMSshPlugin, nm_ssh_plugin, NM_TYPE_VPN_PLUGIN)
  59. #define NM_SSH_PLUGIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SSH_PLUGIN, NMSshPluginPrivate))
  60. typedef struct {
  61. char *username;
  62. char *password;
  63. /* IPv4 variables */
  64. char *remote_gw;
  65. char *local_addr;
  66. char *remote_addr;
  67. char *netmask;
  68. /* IPv6 variables */
  69. gboolean ipv6;
  70. char *local_addr_6;
  71. char *remote_addr_6;
  72. char *netmask_6;
  73. /* Replace or not the default route, the default is to replace */
  74. gboolean no_default_route;
  75. /* fds for handling input/output of the SSH process */
  76. GIOChannel *ssh_stdin_channel;
  77. GIOChannel *ssh_stdout_channel;
  78. GIOChannel *ssh_stderr_channel;
  79. guint socket_channel_stdout_eventid;
  80. guint socket_channel_stderr_eventid;
  81. /* hold local and remote tun/tap numbers
  82. * dev_type can be only "tap" or "tun" */
  83. gchar dev_type[4];
  84. gint remote_dev_number;
  85. gint local_dev_number;
  86. guint mtu;
  87. } NMSshPluginIOData;
  88. typedef struct {
  89. GPid pid;
  90. guint connect_timer;
  91. guint connect_count;
  92. NMSshPluginIOData *io_data;
  93. } NMSshPluginPrivate;
  94. typedef struct {
  95. const char *name;
  96. GType type;
  97. gint int_min;
  98. gint int_max;
  99. gboolean address;
  100. } ValidProperty;
  101. static ValidProperty valid_properties[] = {
  102. /* TRUE/FALSE will dictate whether it is an address (X.X.X.X) or not */
  103. { NM_SSH_KEY_REMOTE, G_TYPE_STRING, 0, 0, FALSE },
  104. { NM_SSH_KEY_LOCAL_IP, G_TYPE_STRING, 0, 0, TRUE },
  105. { NM_SSH_KEY_REMOTE_IP, G_TYPE_STRING, 0, 0, TRUE },
  106. { NM_SSH_KEY_NETMASK, G_TYPE_STRING, 0, 0, TRUE },
  107. { NM_SSH_KEY_PORT, G_TYPE_INT, 1, 65535, FALSE },
  108. { NM_SSH_KEY_TUNNEL_MTU, G_TYPE_INT, 1, 9000, FALSE },
  109. { NM_SSH_KEY_EXTRA_OPTS, G_TYPE_STRING, 0, 0, FALSE },
  110. { NM_SSH_KEY_REMOTE_DEV, G_TYPE_INT, 0, 255, FALSE },
  111. { NM_SSH_KEY_TAP_DEV, G_TYPE_BOOLEAN, 0, 0, FALSE },
  112. { NM_SSH_KEY_REMOTE_USERNAME, G_TYPE_STRING, 0, 0, FALSE },
  113. { NM_SSH_KEY_NO_DEFAULT_ROUTE, G_TYPE_BOOLEAN, 0, 0, FALSE },
  114. /* FIXME should fix host validation for IPv6 addresses */
  115. { NM_SSH_KEY_IP_6, G_TYPE_BOOLEAN, 0, 0, FALSE },
  116. { NM_SSH_KEY_REMOTE_IP_6, G_TYPE_STRING, 0, 0, FALSE },
  117. { NM_SSH_KEY_LOCAL_IP_6, G_TYPE_STRING, 0, 0, FALSE },
  118. { NM_SSH_KEY_NETMASK_6, G_TYPE_STRING, 0, 0, FALSE },
  119. { NM_SSH_KEY_AUTH_TYPE, G_TYPE_STRING, 0, 0, FALSE },
  120. { NM_SSH_KEY_KEY_FILE, G_TYPE_STRING, 0, 0, FALSE },
  121. { NM_SSH_KEY_PASSWORD"-flags", G_TYPE_STRING, 0, 0, FALSE },
  122. { NULL, G_TYPE_NONE, FALSE }
  123. };
  124. static gboolean
  125. validate_address (const char *address)
  126. {
  127. const char *p = address;
  128. if (!address || !strlen (address))
  129. return FALSE;
  130. /* Ensure it's a valid DNS name or IP address */
  131. while (*p) {
  132. if (!isalnum (*p) && (*p != '-') && (*p != '.') && (*p != ':'))
  133. return FALSE;
  134. p++;
  135. }
  136. return TRUE;
  137. }
  138. typedef struct ValidateInfo {
  139. ValidProperty *table;
  140. GError **error;
  141. gboolean have_items;
  142. } ValidateInfo;
  143. static void
  144. validate_one_property (const char *key, const char *value, gpointer user_data)
  145. {
  146. ValidateInfo *info = (ValidateInfo *) user_data;
  147. int i;
  148. if (*(info->error))
  149. return;
  150. info->have_items = TRUE;
  151. /* 'name' is the setting name; always allowed but unused */
  152. if (!strncmp (key, NM_SETTING_NAME, strlen(NM_SETTING_NAME)))
  153. return;
  154. for (i = 0; info->table[i].name; i++) {
  155. ValidProperty prop = info->table[i];
  156. long int tmp;
  157. if (strncmp (prop.name, key, strlen(prop.name)))
  158. continue;
  159. switch (prop.type) {
  160. case G_TYPE_STRING:
  161. if (!prop.address || validate_address (value))
  162. return; /* valid */
  163. g_set_error (info->error,
  164. NM_VPN_PLUGIN_ERROR,
  165. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  166. _("invalid address '%s'"),
  167. key);
  168. break;
  169. case G_TYPE_INT:
  170. errno = 0;
  171. tmp = strtol (value, NULL, 10);
  172. if (errno == 0 && tmp >= prop.int_min && tmp <= prop.int_max)
  173. return; /* valid */
  174. g_set_error (info->error,
  175. NM_VPN_PLUGIN_ERROR,
  176. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  177. _("invalid integer property '%s' or out of range [%d -> %d]"),
  178. key, prop.int_min, prop.int_max);
  179. break;
  180. case G_TYPE_BOOLEAN:
  181. if (IS_YES(value) || !strncmp (value, NO, strlen(NO)))
  182. return; /* valid */
  183. g_set_error (info->error,
  184. NM_VPN_PLUGIN_ERROR,
  185. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  186. _("invalid boolean property '%s' (not yes or no)"),
  187. key);
  188. break;
  189. default:
  190. g_set_error (info->error,
  191. NM_VPN_PLUGIN_ERROR,
  192. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  193. _("unhandled property '%s' type %s"),
  194. key, g_type_name (prop.type));
  195. break;
  196. }
  197. }
  198. /* Did not find the property from valid_properties or the type did not match */
  199. if (!info->table[i].name) {
  200. g_set_error (info->error,
  201. NM_VPN_PLUGIN_ERROR,
  202. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  203. _("property '%s' invalid or not supported"),
  204. key);
  205. }
  206. }
  207. static gboolean
  208. nm_ssh_properties_validate (NMSettingVPN *s_vpn, GError **error)
  209. {
  210. GError *validate_error = NULL;
  211. ValidateInfo info = { &valid_properties[0], &validate_error, FALSE };
  212. nm_setting_vpn_foreach_data_item (s_vpn, validate_one_property, &info);
  213. if (!info.have_items) {
  214. g_set_error (error,
  215. NM_VPN_PLUGIN_ERROR,
  216. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  217. "%s",
  218. _("No VPN configuration options."));
  219. return FALSE;
  220. }
  221. if (validate_error) {
  222. *error = validate_error;
  223. return FALSE;
  224. }
  225. return TRUE;
  226. }
  227. static GValue *
  228. uint_to_gvalue (guint32 num)
  229. {
  230. GValue *val;
  231. if (num == 0)
  232. return NULL;
  233. val = g_slice_new0 (GValue);
  234. g_value_init (val, G_TYPE_UINT);
  235. g_value_set_uint (val, num);
  236. return val;
  237. }
  238. static GValue *
  239. addr6_to_gvalue (const char *str)
  240. {
  241. struct in6_addr temp_addr;
  242. GValue *val;
  243. GByteArray *ba;
  244. /* Empty */
  245. if (!str || strlen (str) < 1)
  246. return NULL;
  247. if (inet_pton (AF_INET6, str, &temp_addr) <= 0)
  248. return NULL;
  249. val = g_slice_new0 (GValue);
  250. g_value_init (val, DBUS_TYPE_G_UCHAR_ARRAY);
  251. ba = g_byte_array_new ();
  252. g_byte_array_append (ba, (guint8 *) &temp_addr, sizeof (temp_addr));
  253. g_value_take_boxed (val, ba);
  254. return val;
  255. }
  256. static GValue *
  257. bool_to_gvalue (gboolean b)
  258. {
  259. GValue *val;
  260. val = g_slice_new0 (GValue);
  261. g_value_init (val, G_TYPE_BOOLEAN);
  262. g_value_set_boolean (val, b);
  263. return val;
  264. }
  265. static GValue *
  266. str_to_gvalue (const char *str, gboolean try_convert)
  267. {
  268. GValue *val;
  269. /* Empty */
  270. if (!str || strlen (str) < 1)
  271. return NULL;
  272. if (!g_utf8_validate (str, -1, NULL)) {
  273. if (try_convert && !(str = g_convert (str, -1, "ISO-8859-1", "UTF-8", NULL, NULL, NULL)))
  274. str = g_convert (str, -1, "C", "UTF-8", NULL, NULL, NULL);
  275. if (!str)
  276. /* Invalid */
  277. return NULL;
  278. }
  279. val = g_slice_new0 (GValue);
  280. g_value_init (val, G_TYPE_STRING);
  281. g_value_set_string (val, str);
  282. return val;
  283. }
  284. static GValue *
  285. addr_to_gvalue (const char *str)
  286. {
  287. struct in_addr temp_addr;
  288. GValue *val;
  289. /* Empty */
  290. if (!str || strlen (str) < 1)
  291. return NULL;
  292. if (inet_pton (AF_INET, str, &temp_addr) <= 0)
  293. return NULL;
  294. val = g_slice_new0 (GValue);
  295. g_value_init (val, G_TYPE_UINT);
  296. g_value_set_uint (val, temp_addr.s_addr);
  297. return val;
  298. }
  299. static char *
  300. resolve_hostname (const char *hostname)
  301. {
  302. struct in_addr addr;
  303. char *ip = NULL;
  304. const char *p;
  305. gboolean is_name = FALSE;
  306. /* Check if it seems to be a hostname hostname */
  307. p = hostname;
  308. while (*p) {
  309. if (*p != '.' && !isdigit (*p)) {
  310. is_name = TRUE;
  311. break;
  312. }
  313. p++;
  314. }
  315. /* Resolve a hostname if required */
  316. if (is_name) {
  317. struct addrinfo hints;
  318. struct addrinfo *result = NULL, *rp;
  319. int err;
  320. memset (&hints, 0, sizeof (hints));
  321. hints.ai_family = AF_INET;
  322. hints.ai_flags = AI_ADDRCONFIG;
  323. err = getaddrinfo (hostname, NULL, &hints, &result);
  324. if (err != 0) {
  325. g_warning ("%s: failed to look up VPN gateway address '%s' (%d)",
  326. __func__, hostname, err);
  327. return NULL;
  328. }
  329. /* FIXME: so what if the name resolves to multiple IP addresses? We
  330. * don't know which one pptp decided to use so we could end up using a
  331. * different one here, and the VPN just won't work.
  332. */
  333. for (rp = result; rp; rp = rp->ai_next) {
  334. if ( (rp->ai_family == AF_INET)
  335. && (rp->ai_addrlen == sizeof (struct sockaddr_in))) {
  336. struct sockaddr_in *inptr = (struct sockaddr_in *) rp->ai_addr;
  337. ip = g_strdup(inet_ntoa (inptr->sin_addr));
  338. if (debug)
  339. g_message("Resolved gateway '%s'->'%s'", hostname, ip);
  340. break;
  341. }
  342. }
  343. freeaddrinfo (result);
  344. } else {
  345. errno = 0;
  346. if (inet_pton (AF_INET, hostname, &addr) <= 0) {
  347. g_warning ("%s: failed to convert VPN gateway address '%s' (%d)",
  348. __func__, hostname, errno);
  349. return NULL;
  350. }
  351. ip = g_strdup (hostname);
  352. }
  353. return ip;
  354. }
  355. static gboolean
  356. send_network_config (NMSshPlugin *plugin)
  357. {
  358. NMSshPluginPrivate *priv = NM_SSH_PLUGIN_GET_PRIVATE (plugin);
  359. NMSshPluginIOData *io_data = priv->io_data;
  360. DBusGConnection *connection;
  361. DBusGProxy *proxy;
  362. GHashTable *config, *ip4config, *ip6config;
  363. GValue *val;
  364. GError *err = NULL;
  365. char *device;
  366. char *resolved_hostname;
  367. connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err);
  368. if (!connection) {
  369. g_warning ("Could not get the system bus: %s", err->message);
  370. nm_vpn_plugin_set_state ((NMVPNPlugin*)plugin, NM_VPN_SERVICE_STATE_STOPPED);
  371. return FALSE;
  372. }
  373. config = g_hash_table_new (g_str_hash, g_str_equal);
  374. ip4config = g_hash_table_new (g_str_hash, g_str_equal);
  375. ip6config = g_hash_table_new (g_str_hash, g_str_equal);
  376. if (debug) {
  377. g_message ("Local device: '%s%d'", io_data->dev_type, io_data->local_dev_number);
  378. g_message ("Remote gateway: '%s'", io_data->remote_gw);
  379. g_message ("Remote IP: '%s'", io_data->remote_addr);
  380. g_message ("Local IP: '%s'", io_data->local_addr);
  381. g_message ("Netmask: '%s'", io_data->netmask);
  382. if (io_data->ipv6) {
  383. g_message ("IPv6 Remote IP: '%s'", io_data->remote_addr_6);
  384. g_message ("IPv6 Local IP: '%s'", io_data->local_addr_6);
  385. g_message ("IPv6 Prefix: '%s'", io_data->netmask_6);
  386. }
  387. }
  388. /* General non IPv4 or IPv6 values (remote_gw, device, mtu) */
  389. /* remote_gw */
  390. if (io_data->remote_gw)
  391. {
  392. /* We might have to resolve that */
  393. resolved_hostname = resolve_hostname (io_data->remote_gw);
  394. if (resolved_hostname) {
  395. val = addr_to_gvalue (resolved_hostname);
  396. g_hash_table_insert (config, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY, val);
  397. g_free (resolved_hostname);
  398. } else {
  399. g_warning ("Could not resolve remote_gw.");
  400. }
  401. }
  402. else
  403. g_warning ("remote_gw unset.");
  404. /* device */
  405. if (io_data->local_dev_number != -1)
  406. {
  407. device =
  408. (gpointer) g_strdup_printf ("%s%d", io_data->dev_type, io_data->local_dev_number);
  409. val = str_to_gvalue (device, FALSE);
  410. g_free(device);
  411. g_hash_table_insert (config, NM_VPN_PLUGIN_CONFIG_TUNDEV, val);
  412. }
  413. else
  414. g_warning ("local_dev_number unset.");
  415. /* mtu */
  416. if (io_data->mtu > 0)
  417. {
  418. val = str_to_gvalue (g_strdup_printf("%d", io_data->mtu), FALSE);
  419. g_hash_table_insert (config, NM_VPN_PLUGIN_CONFIG_MTU, val);
  420. }
  421. else
  422. g_warning ("local_dev_number unset.");
  423. /* End General non IPv4 or IPv6 values */
  424. /* ---------------------------------------------------- */
  425. /* IPv4 specific (local_addr, remote_addr, netmask) */
  426. g_hash_table_insert (config, NM_VPN_PLUGIN_CONFIG_HAS_IP4, bool_to_gvalue (TRUE));
  427. /* replace default route? */
  428. if (io_data->no_default_route) {
  429. g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_NEVER_DEFAULT, bool_to_gvalue (TRUE));
  430. }
  431. /* local_address */
  432. if (io_data->local_addr)
  433. {
  434. val = addr_to_gvalue (io_data->local_addr);
  435. g_hash_table_insert (ip4config, NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS, val);
  436. }
  437. else
  438. g_warning ("local_addr unset.");
  439. /* remote_addr */
  440. if (io_data->remote_addr)
  441. {
  442. val = addr_to_gvalue (io_data->remote_addr);
  443. g_hash_table_insert (ip4config, NM_VPN_PLUGIN_IP4_CONFIG_INT_GATEWAY, val);
  444. g_hash_table_insert (ip4config, NM_VPN_PLUGIN_IP4_CONFIG_PTP, val);
  445. }
  446. else
  447. g_warning ("remote_addr unset.");
  448. /* netmask */
  449. if (io_data->netmask && g_str_has_prefix (io_data->netmask, "255.")) {
  450. guint32 addr;
  451. val = addr_to_gvalue(io_data->netmask);
  452. addr = g_value_get_uint (val);
  453. g_value_set_uint (val, nm_utils_ip4_netmask_to_prefix (addr));
  454. g_hash_table_insert (ip4config, NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, val);
  455. } else
  456. g_warning ("netmask unset.");
  457. /* End IPv4 specific (local_addr, remote_addr, netmask) */
  458. /* ---------------------------------------------------- */
  459. /* IPv6 specific (local_addr_6, remote_addr_6, netmask_6) */
  460. if (io_data->ipv6) {
  461. g_hash_table_insert (config, NM_VPN_PLUGIN_CONFIG_HAS_IP6, bool_to_gvalue (TRUE));
  462. /* replace default route? */
  463. if (io_data->no_default_route) {
  464. g_hash_table_insert (config, NM_VPN_PLUGIN_IP6_CONFIG_NEVER_DEFAULT, bool_to_gvalue (TRUE));
  465. }
  466. /* local_addr_6 */
  467. if (io_data->local_addr_6)
  468. {
  469. val = addr6_to_gvalue (io_data->local_addr_6);
  470. g_hash_table_insert (ip6config, NM_VPN_PLUGIN_IP6_CONFIG_ADDRESS, val);
  471. }
  472. else
  473. g_warning ("local_addr_6 unset.");
  474. /* remote_addr_6 */
  475. if (io_data->remote_addr_6)
  476. {
  477. val = addr6_to_gvalue (io_data->remote_addr_6);
  478. g_hash_table_insert (ip6config, NM_VPN_PLUGIN_IP6_CONFIG_INT_GATEWAY, val);
  479. g_hash_table_insert (ip6config, NM_VPN_PLUGIN_IP6_CONFIG_PTP, val);
  480. }
  481. else
  482. g_warning ("remote_addr_6 unset.");
  483. /* netmask_6 */
  484. if (io_data->netmask_6) {
  485. val = uint_to_gvalue (strtol (io_data->netmask_6, NULL, 10));
  486. g_hash_table_insert (ip6config, NM_VPN_PLUGIN_IP6_CONFIG_PREFIX, val);
  487. } else
  488. g_warning ("netmask_6 unset.");
  489. }
  490. /* End IPv6 specific (local_addr_6, remote_addr_6, netmask_6) */
  491. /* ---------------------------------------------------- */
  492. proxy = dbus_g_proxy_new_for_name (
  493. connection,
  494. NM_DBUS_SERVICE_SSH,
  495. NM_VPN_DBUS_PLUGIN_PATH,
  496. NM_VPN_DBUS_PLUGIN_INTERFACE);
  497. /* Send general config */
  498. dbus_g_proxy_call_no_reply (
  499. proxy, "SetConfig",
  500. dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE),
  501. config,
  502. G_TYPE_INVALID,
  503. G_TYPE_INVALID);
  504. /* Send IPv6 config */
  505. if (io_data->ipv6) {
  506. dbus_g_proxy_call_no_reply (
  507. proxy, "SetIp6Config",
  508. dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE),
  509. ip6config,
  510. G_TYPE_INVALID,
  511. G_TYPE_INVALID);
  512. }
  513. /* Send IPv4 config */
  514. dbus_g_proxy_call_no_reply (
  515. proxy, "SetIp4Config",
  516. dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE),
  517. ip4config,
  518. G_TYPE_INVALID,
  519. G_TYPE_INVALID);
  520. g_object_unref (proxy);
  521. return TRUE;
  522. }
  523. static gboolean
  524. nm_ssh_local_device_up_cb (gpointer data)
  525. {
  526. NMSshPlugin *plugin = NM_SSH_PLUGIN (data);
  527. NMSshPluginPrivate *priv = NM_SSH_PLUGIN_GET_PRIVATE (plugin);
  528. NMSshPluginIOData *io_data = priv->io_data;
  529. char *ifconfig_cmd_4, *ifconfig_cmd_6;
  530. priv->connect_count++;
  531. /* IPv4 ifconfig command */
  532. ifconfig_cmd_4 = (gpointer) g_strdup_printf (
  533. "%s %s%d %s netmask %s pointopoint %s mtu %d up",
  534. IFCONFIG,
  535. io_data->dev_type,
  536. io_data->local_dev_number,
  537. io_data->local_addr,
  538. io_data->netmask,
  539. io_data->remote_addr,
  540. priv->io_data->mtu);
  541. /* IPv6 ifconfig command */
  542. if (io_data->ipv6) {
  543. ifconfig_cmd_6 = (gpointer) g_strdup_printf (
  544. "%s %s%d add %s/%s",
  545. IFCONFIG,
  546. io_data->dev_type,
  547. io_data->local_dev_number,
  548. io_data->local_addr_6,
  549. io_data->netmask_6);
  550. } else {
  551. /* No IPv6, we'll just have a null command */
  552. ifconfig_cmd_6 = g_strdup("");
  553. }
  554. if (debug) {
  555. g_message ("IPv4 ifconfig: '%s'", ifconfig_cmd_4);
  556. g_message ("IPv6 ifconfig: '%s'", ifconfig_cmd_6);
  557. }
  558. if ((system(ifconfig_cmd_4) != 0 || system(ifconfig_cmd_6) != 0 ) &&
  559. priv->connect_count <= 30)
  560. {
  561. /* We failed, but we'll try again soon... */
  562. g_free(ifconfig_cmd_4);
  563. g_free(ifconfig_cmd_6);
  564. return TRUE;
  565. }
  566. g_free(ifconfig_cmd_4);
  567. g_free(ifconfig_cmd_6);
  568. g_message ("Interface %s%d configured.", io_data->dev_type, io_data->local_dev_number);
  569. priv->connect_timer = 0;
  570. send_network_config(plugin);
  571. /* Return false so we don't get called again */
  572. return FALSE;
  573. }
  574. static void
  575. nm_ssh_schedule_ifconfig_timer (NMSshPlugin *plugin)
  576. {
  577. NMSshPluginPrivate *priv = NM_SSH_PLUGIN_GET_PRIVATE (plugin);
  578. if (priv->connect_timer == 0)
  579. priv->connect_timer = g_timeout_add (1000, nm_ssh_local_device_up_cb, plugin);
  580. }
  581. static gboolean
  582. nm_ssh_stdout_cb (GIOChannel *source, GIOCondition condition, gpointer user_data)
  583. {
  584. NMVPNPlugin *plugin = NM_VPN_PLUGIN (user_data);
  585. char *str = NULL;
  586. if (!(condition & G_IO_IN))
  587. return TRUE;
  588. if (g_io_channel_read_line (source, &str, NULL, NULL, NULL) != G_IO_STATUS_NORMAL)
  589. return TRUE;
  590. if (strlen (str) < 1) {
  591. g_free(str);
  592. return TRUE;
  593. }
  594. /* Probe for the remote interface number */
  595. if (g_str_has_prefix(str, "debug1: Requesting tun unit")) {
  596. } else if (g_str_has_prefix(str, "debug1: Requesting tun unit")) {
  597. /* This message denotes the tun/tap device opening on the remote host */
  598. } else if (g_str_has_prefix (str, "debug1: sys_tun_open:")) {
  599. /* This message denotes the tun/tap device opening on the local host
  600. * Starting timer here for getting local interface up... */
  601. } else if (g_str_has_prefix (str, "Tunnel device open failed.")) {
  602. /* Opening of local tun device failed... :( */
  603. g_warning("Tunnel device open failed.");
  604. nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STOPPED);
  605. } else if (g_str_has_prefix (str, "debug1: Sending command:")) {
  606. /* If we got to sending the command, it means that things are
  607. * established, we should start the timer to get the local
  608. * interface up... */
  609. if (NM_VPN_SERVICE_STATE_STOPPED != nm_vpn_plugin_get_state (plugin))
  610. nm_ssh_schedule_ifconfig_timer ((NMSshPlugin*)plugin);
  611. else if(debug)
  612. g_message("Not starting local timer because plugin is in STOPPED state");
  613. } else if (g_str_has_prefix (str, "debug1: Remote: Server has rejected tunnel device forwarding")) {
  614. /* Opening of remote tun device failed... :( */
  615. g_warning("Tunnel device open failed on remote server.");
  616. g_warning("Make sure you have privileges to open tun/tap devices and that your SSH server is configured with 'PermitTunnel=yes'");
  617. nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STOPPED);
  618. } else if (g_str_has_prefix (str, "debug1: Remote: Failed to open the tunnel device.")) {
  619. /* Opening of remote tun device failed... device busy? */
  620. g_warning("Tunnel device open failed on remote server.");
  621. g_warning("Is this device free on the remote host?");
  622. nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STOPPED);
  623. } else if (strncmp (str, "The authenticity of host", 24) == 0) {
  624. /* User will have to accept this new host with its fingerprint */
  625. g_warning("It is not a known host, continue connecting?");
  626. nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STOPPED);
  627. }
  628. g_message("%s", str);
  629. g_free(str);
  630. return TRUE;
  631. }
  632. static gint
  633. nm_ssh_get_free_device (const char *device_type)
  634. {
  635. gint device;
  636. char *system_cmd;
  637. for (device = 0; device <= 255; device++)
  638. {
  639. system_cmd = (gpointer) g_strdup_printf ("%s %s%d >& /dev/null", IFCONFIG, device_type, device);
  640. if (system(system_cmd) != 0)
  641. {
  642. g_free(system_cmd);
  643. return device;
  644. }
  645. g_free(system_cmd);
  646. }
  647. return -1;
  648. }
  649. static void
  650. ssh_watch_cb (GPid pid, gint status, gpointer user_data)
  651. {
  652. NMVPNPlugin *plugin = NM_VPN_PLUGIN (user_data);
  653. NMSshPluginPrivate *priv = NM_SSH_PLUGIN_GET_PRIVATE (plugin);
  654. NMVPNPluginFailure failure = NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED;
  655. guint error = 0;
  656. gboolean good_exit = FALSE;
  657. if (WIFEXITED (status)) {
  658. error = WEXITSTATUS (status);
  659. if (error != 0)
  660. g_warning ("ssh exited with error code %d", error);
  661. }
  662. else if (WIFSTOPPED (status))
  663. g_warning ("ssh stopped unexpectedly with signal %d", WSTOPSIG (status));
  664. else if (WIFSIGNALED (status))
  665. g_warning ("ssh died with signal %d", WTERMSIG (status));
  666. else
  667. g_warning ("ssh died from an unknown cause");
  668. if (0 != priv->connect_timer) {
  669. g_source_remove(priv->connect_timer);
  670. priv->connect_timer = 0;
  671. }
  672. /* Reap child if needed. */
  673. waitpid (priv->pid, NULL, WNOHANG);
  674. priv->pid = 0;
  675. /* SSH doesn't supply useful exit codes :( */
  676. switch (error) {
  677. case 0:
  678. good_exit = TRUE;
  679. break;
  680. default:
  681. failure = NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED;
  682. break;
  683. }
  684. /* Try to get the last bits of data from ssh */
  685. if (priv->io_data && priv->io_data->ssh_stdout_channel) {
  686. GIOChannel *channel = priv->io_data->ssh_stdout_channel;
  687. GIOCondition condition;
  688. while ((condition = g_io_channel_get_buffer_condition (channel)) & G_IO_IN) {
  689. if (!nm_ssh_stdout_cb (channel, condition, plugin)) {
  690. good_exit = FALSE;
  691. break;
  692. }
  693. }
  694. }
  695. g_source_remove(priv->io_data->socket_channel_stdout_eventid);
  696. close (g_io_channel_unix_get_fd(priv->io_data->ssh_stdout_channel));
  697. /* Try to get the last bits of data from ssh */
  698. if (priv->io_data && priv->io_data->ssh_stderr_channel) {
  699. GIOChannel *channel = priv->io_data->ssh_stderr_channel;
  700. GIOCondition condition;
  701. while ((condition = g_io_channel_get_buffer_condition (channel)) & G_IO_IN) {
  702. if (!nm_ssh_stdout_cb (channel, condition, plugin)) {
  703. good_exit = FALSE;
  704. break;
  705. }
  706. }
  707. }
  708. g_source_remove(priv->io_data->socket_channel_stderr_eventid);
  709. close (g_io_channel_unix_get_fd(priv->io_data->ssh_stderr_channel));
  710. if (!good_exit)
  711. nm_vpn_plugin_failure (plugin, failure);
  712. nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STOPPED);
  713. }
  714. static const char *
  715. nm_find_ssh (void)
  716. {
  717. static const char *ssh_binary_paths[] = {
  718. "/usr/bin/ssh",
  719. "/bin/ssh",
  720. "/usr/local/bin/ssh",
  721. NULL
  722. };
  723. const char **ssh_binary = ssh_binary_paths;
  724. while (*ssh_binary != NULL) {
  725. if (g_file_test (*ssh_binary, G_FILE_TEST_EXISTS))
  726. break;
  727. ssh_binary++;
  728. }
  729. return *ssh_binary;
  730. }
  731. /* FIXME refactor this with nm_find_ssh */
  732. static const char *
  733. nm_find_sshpass (void)
  734. {
  735. static const char *sshpass_binary_paths[] = {
  736. "/usr/bin/sshpass",
  737. "/bin/sshpass",
  738. "/usr/local/bin/sshpass",
  739. NULL
  740. };
  741. const char **sshpass_binary = sshpass_binary_paths;
  742. while (*sshpass_binary != NULL) {
  743. if (g_file_test (*sshpass_binary, G_FILE_TEST_EXISTS))
  744. break;
  745. sshpass_binary++;
  746. }
  747. return *sshpass_binary;
  748. }
  749. static void
  750. free_ssh_args (GPtrArray *args)
  751. {
  752. g_ptr_array_foreach (args, (GFunc) g_free, NULL);
  753. g_ptr_array_free (args, TRUE);
  754. }
  755. static void
  756. add_ssh_arg (GPtrArray *args, const char *arg)
  757. {
  758. g_return_if_fail (args != NULL);
  759. g_return_if_fail (arg != NULL);
  760. g_ptr_array_add (args, (gpointer) g_strdup (arg));
  761. }
  762. static void
  763. add_ssh_extra_opts (GPtrArray *args, const char *extra_opts)
  764. {
  765. gchar **extra_opts_split;
  766. gchar **iter;
  767. /* Needs to separate arguements nicely */
  768. extra_opts_split = g_strsplit (extra_opts, " ", 256);
  769. iter = extra_opts_split;
  770. /* Ensure it's a valid DNS name or IP address */
  771. while (*iter) {
  772. g_message("%s", *iter);
  773. add_ssh_arg (args, *iter);
  774. iter++;
  775. }
  776. g_strfreev (extra_opts_split);
  777. }
  778. static gboolean
  779. get_ssh_arg_int (const char *arg, long int *retval)
  780. {
  781. long int tmp_int;
  782. /* Convert -> int and back to string for security's sake since
  783. * strtol() ignores some leading and trailing characters.
  784. */
  785. errno = 0;
  786. tmp_int = strtol (arg, NULL, 10);
  787. if (errno != 0)
  788. return FALSE;
  789. *retval = tmp_int;
  790. return TRUE;
  791. }
  792. static char*
  793. get_known_hosts_file(const char *username,
  794. const char* ssh_agent_socket)
  795. {
  796. struct stat info;
  797. struct passwd *pw = NULL;
  798. char *ssh_known_hosts = NULL;
  799. /* Probe by passed username */
  800. if (username) {
  801. pw = getpwnam(username);
  802. /* Probe by passed ssh-agent socket ownership */
  803. } else if (ssh_agent_socket) {
  804. if (0 == stat(ssh_agent_socket, &info)) {
  805. pw = getpwuid(info.st_uid);
  806. } else {
  807. g_warning("Error getting ssh-agent socket ownership: %d", errno);
  808. }
  809. }
  810. /* FIXME Check if provided SSH_KNOWN_HOSTS_PATH really exists */
  811. if (pw) {
  812. ssh_known_hosts = g_strdup_printf("%s/%s", pw->pw_dir, SSH_KNOWN_HOSTS_PATH);
  813. if (0 != stat(ssh_known_hosts, &info)) {
  814. g_warning("No known_hosts at '%s': %d.", ssh_known_hosts, errno);
  815. g_free(ssh_known_hosts);
  816. }
  817. }
  818. return ssh_known_hosts;
  819. }
  820. static gboolean
  821. nm_ssh_start_ssh_binary (NMSshPlugin *plugin,
  822. NMSettingVPN *s_vpn,
  823. const char *default_username,
  824. GError **error)
  825. {
  826. /* This giant function is basically taking care of passing all the
  827. * correct parameters to ssh (and sshpass) */
  828. NMSshPluginPrivate *priv = NM_SSH_PLUGIN_GET_PRIVATE (plugin);
  829. const char *ssh_binary, *sshpass_binary, *tmp;
  830. const char *remote, *port, *mtu, *ssh_agent_socket, *auth_type;
  831. char *known_hosts_file;
  832. char *tmp_arg;
  833. char *ifconfig_cmd_4, *ifconfig_cmd_6;
  834. char *envp[16];
  835. long int tmp_int;
  836. GPtrArray *args;
  837. GSource *ssh_watch;
  838. GPid pid;
  839. gint ssh_stdin_fd, ssh_stdout_fd, ssh_stderr_fd;
  840. int sshpass_pipe[2];
  841. const gchar *password = NULL;
  842. /* Find ssh */
  843. ssh_binary = nm_find_ssh ();
  844. if (!ssh_binary) {
  845. g_set_error (error,
  846. NM_VPN_PLUGIN_ERROR,
  847. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  848. "%s",
  849. _("Could not find the ssh binary."));
  850. return FALSE;
  851. }
  852. /* Allocate io_data structure */
  853. priv->io_data = g_malloc0 (sizeof (NMSshPluginIOData));
  854. args = g_ptr_array_new ();
  855. /* Get auth_type from s_vpn */
  856. auth_type = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_AUTH_TYPE);
  857. /* Handle different behaviour for different auth types */
  858. envp[0] = NULL;
  859. if (!strncmp (auth_type, NM_SSH_AUTH_TYPE_PASSWORD, strlen(NM_SSH_AUTH_TYPE_PASSWORD))) {
  860. /* If the user wishes to supply a password */
  861. /* Find sshpass, we'll use it to wrap ssh and provide a password from
  862. * the command line */
  863. sshpass_binary = nm_find_sshpass ();
  864. if (!sshpass_binary) {
  865. g_set_error (error,
  866. NM_VPN_PLUGIN_ERROR,
  867. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  868. "%s",
  869. _("Could not find the sshpass binary."));
  870. return FALSE;
  871. }
  872. /* Use sshpass binary */
  873. add_ssh_arg (args, sshpass_binary);
  874. /* Get password */
  875. password = nm_setting_vpn_get_secret (s_vpn, NM_SSH_KEY_PASSWORD);
  876. if (password && strlen(password)) {
  877. if (pipe(sshpass_pipe))
  878. {
  879. g_set_error (
  880. error,
  881. NM_VPN_PLUGIN_ERROR,
  882. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  883. "%s",
  884. _("Failed creating pipe."));
  885. free_ssh_args (args);
  886. return FALSE;
  887. }
  888. tmp = (gpointer) g_strdup_printf ("-d%d", sshpass_pipe[0]);
  889. add_ssh_arg (args, tmp);
  890. g_free((gpointer) tmp);
  891. } else {
  892. /* No password specified? Exit! */
  893. g_set_error (
  894. error,
  895. NM_VPN_PLUGIN_ERROR,
  896. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  897. "%s",
  898. _("No password specified."));
  899. free_ssh_args (args);
  900. return FALSE;
  901. }
  902. /* And add the ssh_binary */
  903. add_ssh_arg (args, ssh_binary);
  904. /* Prompt just once for password, it's enough */
  905. add_ssh_arg (args, "-o"); add_ssh_arg (args, "NumberOfPasswordPrompts=1");
  906. add_ssh_arg (args, "-o"); add_ssh_arg (args, "PreferredAuthentications=password");
  907. } else {
  908. /* Add the ssh binary, as we're not going to use sshpass */
  909. add_ssh_arg (args, ssh_binary);
  910. /* No password prompts, only key authentication if user specifies
  911. * key of ssh agent auth */
  912. add_ssh_arg (args, "-o"); add_ssh_arg (args, "NumberOfPasswordPrompts=0");
  913. add_ssh_arg (args, "-o"); add_ssh_arg (args, "PreferredAuthentications=publickey");
  914. /* Passing a id_dsa/id_rsa key as an argument to ssh */
  915. if (!strncmp (auth_type, NM_SSH_AUTH_TYPE_KEY, strlen(NM_SSH_AUTH_TYPE_KEY))) {
  916. tmp = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_KEY_FILE);
  917. if (tmp && strlen (tmp)) {
  918. /* Specify key file */
  919. add_ssh_arg (args, "-i");
  920. add_ssh_arg (args, tmp);
  921. } else {
  922. /* No key specified? Exit! */
  923. g_set_error (error,
  924. NM_VPN_PLUGIN_ERROR,
  925. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  926. "%s",
  927. _("Key authentication selected, but no key file specified."));
  928. free_ssh_args (args);
  929. return FALSE;
  930. }
  931. } else if (!strncmp (auth_type, NM_SSH_AUTH_TYPE_SSH_AGENT, strlen(NM_SSH_AUTH_TYPE_SSH_AGENT))) {
  932. /* Last but not least, the original nm-ssh default behaviour which
  933. * which is the sanest of all - SSH_AGENT socket */
  934. /* FIXME add all the ssh agent logic here */
  935. /* Set SSH_AUTH_SOCK from ssh-agent
  936. * Passes as a secret key from the user's context
  937. * using auth-dialog */
  938. ssh_agent_socket = nm_setting_vpn_get_secret (s_vpn, NM_SSH_KEY_SSH_AUTH_SOCK);
  939. if (ssh_agent_socket && strlen(ssh_agent_socket)) {
  940. envp[0] = (gpointer) g_strdup_printf ("%s=%s", SSH_AUTH_SOCK, ssh_agent_socket);
  941. } else {
  942. /* No SSH_AUTH_SOCK passed from user context */
  943. g_set_error (error,
  944. NM_VPN_PLUGIN_ERROR,
  945. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  946. "%s",
  947. _("Missing required SSH_AUTH_SOCK."));
  948. free_ssh_args (args);
  949. return FALSE;
  950. }
  951. envp[1] = NULL;
  952. if (debug)
  953. g_message ("Using ssh-agent socket: '%s'", envp[0]);
  954. } else {
  955. g_set_error (
  956. error,
  957. NM_VPN_PLUGIN_ERROR,
  958. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  959. _("Unknown authentication type: %s."), auth_type);
  960. free_ssh_args (args);
  961. return FALSE;
  962. }
  963. }
  964. /* Set verbose mode, we'll parse the arguments */
  965. add_ssh_arg (args, "-v");
  966. /* Dictate whether to replace the default route or not */
  967. tmp = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_NO_DEFAULT_ROUTE);
  968. if (tmp && IS_YES(tmp)) {
  969. priv->io_data->no_default_route = TRUE;
  970. } else {
  971. /* That's the default - to replace the default route
  972. It's a VPN after all!! :) */
  973. priv->io_data->no_default_route = FALSE;
  974. }
  975. /* FIXME if not using SSH_AUTH_SOCK we can't know where is known_hosts */
  976. /* We have SSH_AUTH_SOCK, we'll assume it's owned by the user
  977. * that we should use its .ssh/known_hosts file
  978. * So we'll probe the user owning SSH_AUTH_SOCK and then use
  979. * -o UserKnownHostsFile=$HOME/.ssh/known_hosts */
  980. known_hosts_file = get_known_hosts_file(default_username, ssh_agent_socket);
  981. if (!(known_hosts_file && strlen (known_hosts_file))) {
  982. g_warning("Using root's .ssh/known_hosts");
  983. } else {
  984. if (debug)
  985. g_message("Using known_hosts at: '%s'", known_hosts_file);
  986. add_ssh_arg (args, "-o");
  987. add_ssh_arg (args, g_strdup_printf("UserKnownHostsFile=%s", known_hosts_file) );
  988. g_free(known_hosts_file);
  989. }
  990. /* Extra SSH options */
  991. tmp = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_EXTRA_OPTS);
  992. if (tmp && strlen (tmp)) {
  993. add_ssh_extra_opts (args, tmp);
  994. } else {
  995. /* Add default extra options */
  996. add_ssh_extra_opts (args, NM_SSH_DEFAULT_EXTRA_OPTS);
  997. }
  998. /* Device, either tun or tap */
  999. add_ssh_arg (args, "-o");
  1000. tmp = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_TAP_DEV);
  1001. if (tmp && IS_YES(tmp)) {
  1002. add_ssh_arg (args, "Tunnel=ethernet");
  1003. g_strlcpy ((gchar *) &priv->io_data->dev_type, "tap", 4);
  1004. } else {
  1005. add_ssh_arg (args, "Tunnel=point-to-point");
  1006. g_strlcpy ((gchar *) &priv->io_data->dev_type, "tun", 4);
  1007. }
  1008. /* Get a local tun/tap */
  1009. priv->io_data->local_dev_number = nm_ssh_get_free_device(priv->io_data->dev_type);
  1010. if (priv->io_data->local_dev_number == -1)
  1011. {
  1012. g_warning("Could not assign a free tun/tap device.");
  1013. nm_vpn_plugin_set_state ((NMVPNPlugin*)plugin, NM_VPN_SERVICE_STATE_STOPPED);
  1014. return FALSE;
  1015. }
  1016. /* Remote */
  1017. remote = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_REMOTE);
  1018. if (!(remote && strlen (remote))) {
  1019. g_set_error (error,
  1020. NM_VPN_PLUGIN_ERROR,
  1021. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  1022. _("Please set remote address."));
  1023. free_ssh_args (args);
  1024. return FALSE;
  1025. } else {
  1026. priv->io_data->remote_gw = g_strdup(remote);
  1027. }
  1028. /* Port */
  1029. port = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_PORT);
  1030. add_ssh_arg (args, "-o");
  1031. if (port && strlen (port)) {
  1032. /* Range validation is done in dialog... */
  1033. if (!get_ssh_arg_int (port, &tmp_int)) {
  1034. g_set_error (error,
  1035. NM_VPN_PLUGIN_ERROR,
  1036. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  1037. _("Invalid port number '%s'."),
  1038. port);
  1039. free_ssh_args (args);
  1040. return FALSE;
  1041. }
  1042. add_ssh_arg (args, (gpointer) g_strdup_printf ("Port=%d", (guint32) tmp_int));
  1043. } else {
  1044. /* Default to SSH port 22 */
  1045. add_ssh_arg (args, (gpointer) g_strdup_printf("Port=%d", (guint32) NM_SSH_DEFAULT_PORT));
  1046. }
  1047. /* TUN MTU size */
  1048. mtu = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_TUNNEL_MTU);
  1049. if (mtu && strlen (mtu)) {
  1050. /* Range validation is done in dialog... */
  1051. if (!get_ssh_arg_int (mtu, &tmp_int)) {
  1052. g_set_error (error,
  1053. NM_VPN_PLUGIN_ERROR,
  1054. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  1055. _("Invalid TUN MTU size '%s'."),
  1056. mtu);
  1057. free_ssh_args (args);
  1058. return FALSE;
  1059. }
  1060. priv->io_data->mtu = tmp_int;
  1061. } else {
  1062. /* Default MTU of 1500 */
  1063. priv->io_data->mtu = NM_SSH_DEFAULT_MTU;
  1064. }
  1065. /* Remote device */
  1066. tmp = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_REMOTE_DEV);
  1067. if (tmp && strlen (tmp)) {
  1068. /* Range validation is done in dialog... */
  1069. if (!get_ssh_arg_int (tmp, &tmp_int)) {
  1070. g_set_error (error,
  1071. NM_VPN_PLUGIN_ERROR,
  1072. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  1073. _("Invalid TUN/TAP device number '%s'."),
  1074. tmp);
  1075. free_ssh_args (args);
  1076. return FALSE;
  1077. }
  1078. priv->io_data->remote_dev_number = tmp_int;
  1079. } else {
  1080. /* Use tun100/tap100 by default */
  1081. priv->io_data->remote_dev_number = NM_SSH_DEFAULT_REMOTE_DEV;
  1082. }
  1083. /* Remote IP */
  1084. tmp = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_REMOTE_IP);
  1085. if (!tmp) {
  1086. /* Insufficient data (FIXME: this should really be detected when validating the properties */
  1087. g_set_error (error,
  1088. NM_VPN_PLUGIN_ERROR,
  1089. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  1090. "%s",
  1091. _("Missing required remote IP address."));
  1092. free_ssh_args (args);
  1093. return FALSE;
  1094. }
  1095. priv->io_data->remote_addr = g_strdup(tmp);
  1096. /* Local IP */
  1097. tmp = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_LOCAL_IP);
  1098. if (!tmp) {
  1099. /* Insufficient data (FIXME: this should really be detected when validating the properties */
  1100. g_set_error (error,
  1101. NM_VPN_PLUGIN_ERROR,
  1102. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  1103. "%s",
  1104. _("Missing required local IP address."));
  1105. free_ssh_args (args);
  1106. return FALSE;
  1107. }
  1108. priv->io_data->local_addr = g_strdup(tmp);
  1109. /* Netmask */
  1110. tmp = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_NETMASK);
  1111. if (!tmp) {
  1112. priv->io_data->netmask = g_strdup(tmp);
  1113. /* Insufficient data (FIXME: this should really be detected when validating the properties */
  1114. g_set_error (error,
  1115. NM_VPN_PLUGIN_ERROR,
  1116. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  1117. "%s",
  1118. _("Missing required netmask."));
  1119. free_ssh_args (args);
  1120. return FALSE;
  1121. }
  1122. priv->io_data->netmask = g_strdup(tmp);
  1123. /* IPv6 enabled? */
  1124. tmp = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_IP_6);
  1125. if (tmp && IS_YES(tmp)) {
  1126. /* IPv6 is enabled */
  1127. priv->io_data->ipv6 = TRUE;
  1128. /* Remote IP IPv6 */
  1129. tmp = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_REMOTE_IP_6);
  1130. if (!tmp) {
  1131. /* Insufficient data (FIXME: this should really be detected when validating the properties */
  1132. g_set_error (error,
  1133. NM_VPN_PLUGIN_ERROR,
  1134. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  1135. "%s",
  1136. _("Missing required IPv6 remote IP address."));
  1137. free_ssh_args (args);
  1138. return FALSE;
  1139. }
  1140. priv->io_data->remote_addr_6 = g_strdup(tmp);
  1141. /* Local IP IPv6 */
  1142. tmp = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_LOCAL_IP_6);
  1143. if (!tmp) {
  1144. /* Insufficient data (FIXME: this should really be detected when validating the properties */
  1145. g_set_error (error,
  1146. NM_VPN_PLUGIN_ERROR,
  1147. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  1148. "%s",
  1149. _("Missing required IPv6 local IP address."));
  1150. free_ssh_args (args);
  1151. return FALSE;
  1152. }
  1153. priv->io_data->local_addr_6 = g_strdup(tmp);
  1154. /* Prefix IPv6 */
  1155. tmp = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_NETMASK_6);
  1156. if (!tmp) {
  1157. /* Insufficient data (FIXME: this should really be detected when validating the properties */
  1158. g_set_error (error,
  1159. NM_VPN_PLUGIN_ERROR,
  1160. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  1161. "%s",
  1162. _("Missing required IPv6 netmask."));
  1163. free_ssh_args (args);
  1164. return FALSE;
  1165. }
  1166. priv->io_data->netmask_6 = g_strdup(tmp);
  1167. } else {
  1168. /* Set the values so they are not NULL */
  1169. priv->io_data->ipv6 = FALSE;
  1170. priv->io_data->remote_addr_6 = g_strdup("");
  1171. priv->io_data->local_addr_6 = g_strdup("");
  1172. priv->io_data->netmask_6 = g_strdup("");
  1173. }
  1174. /* The -w option, provide a remote and local tun/tap device */
  1175. tmp_arg = (gpointer) g_strdup_printf (
  1176. "TunnelDevice=%d:%d", priv->io_data->local_dev_number, priv->io_data->remote_dev_number);
  1177. add_ssh_arg (args, "-o"); add_ssh_arg (args, tmp_arg);
  1178. g_free(tmp_arg);
  1179. /* Remote username, should usually be root */
  1180. tmp = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_REMOTE_USERNAME);
  1181. if (tmp && strlen (tmp)) {
  1182. priv->io_data->username = g_strdup (tmp);
  1183. } else {
  1184. /* Add default username - root */
  1185. priv->io_data->username = g_strdup (NM_SSH_DEFAULT_REMOTE_USERNAME);
  1186. }
  1187. tmp_arg = g_strdup_printf ("User=%s", priv->io_data->username);
  1188. add_ssh_arg (args, "-o");
  1189. add_ssh_arg (args, tmp_arg);
  1190. g_free(tmp_arg);
  1191. /* Connect to remote */
  1192. tmp_arg = g_strdup_printf ("HostName=%s", priv->io_data->remote_gw);
  1193. add_ssh_arg (args, "-o");
  1194. add_ssh_arg (args, tmp_arg);
  1195. g_free(tmp_arg);
  1196. /* This is supposedly the hostname parameter, but we can pass anything
  1197. * as we passed '-o Hostname=' before that */
  1198. add_ssh_arg (args, "NetworkManager-ssh");
  1199. /* Command line to run on remote machine */
  1200. ifconfig_cmd_4 = (gpointer) g_strdup_printf (
  1201. "%s %s%d inet %s netmask %s pointopoint %s mtu %d",
  1202. IFCONFIG,
  1203. priv->io_data->dev_type,
  1204. priv->io_data->remote_dev_number,
  1205. priv->io_data->remote_addr,
  1206. priv->io_data->netmask,
  1207. priv->io_data->local_addr,
  1208. priv->io_data->mtu);
  1209. /* IPv6 ifconfig command to run on remote machine */
  1210. if (priv->io_data->ipv6) {
  1211. ifconfig_cmd_6 = (gpointer) g_strdup_printf (
  1212. "%s %s%d add %s/%s",
  1213. IFCONFIG,
  1214. priv->io_data->dev_type,
  1215. priv->io_data->remote_dev_number,
  1216. priv->io_data->remote_addr_6,
  1217. priv->io_data->netmask_6);
  1218. } else {
  1219. ifconfig_cmd_6 = g_strdup("");
  1220. }
  1221. /* Concatenate ifconfig_cmd_4 and ifconfig_cmd_6 to one command */
  1222. tmp_arg = g_strconcat(ifconfig_cmd_4, "; ", ifconfig_cmd_6, NULL);
  1223. add_ssh_arg (args, tmp_arg);
  1224. g_free(ifconfig_cmd_4);
  1225. g_free(ifconfig_cmd_6);
  1226. g_free(tmp_arg);
  1227. /* Wrap it up */
  1228. g_ptr_array_add (args, NULL);
  1229. /* Spawn with pipes */
  1230. if (!g_spawn_async_with_pipes (NULL, (char **) args->pdata, envp,
  1231. G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_LEAVE_DESCRIPTORS_OPEN, NULL, NULL, &pid,
  1232. &ssh_stdin_fd, &ssh_stdout_fd, &ssh_stderr_fd,
  1233. error)) {
  1234. free_ssh_args (args);
  1235. return FALSE;
  1236. }
  1237. free_ssh_args (args);
  1238. /* Write password to fd, so sshpass can pick it up */
  1239. if (!strncmp (auth_type, NM_SSH_AUTH_TYPE_PASSWORD, strlen(NM_SSH_AUTH_TYPE_PASSWORD))) {
  1240. tmp_arg = g_strdup_printf ("%s\n", password);
  1241. if (write(sshpass_pipe[1], tmp_arg, strlen(tmp_arg)) != strlen(tmp_arg)) {
  1242. g_set_error (error,
  1243. NM_VPN_PLUGIN_ERROR,
  1244. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  1245. "%s",
  1246. _("Could not pass password to sshpass."));
  1247. free_ssh_args (args);
  1248. g_free(tmp_arg);
  1249. return FALSE;
  1250. }
  1251. g_free(tmp_arg);
  1252. close(sshpass_pipe[0]);
  1253. close(sshpass_pipe[1]);
  1254. }
  1255. g_message ("ssh started with pid %d", pid);
  1256. /* Add a watch for the SSH stdout and stderr */
  1257. priv->io_data->ssh_stdin_channel = g_io_channel_unix_new (ssh_stdin_fd);
  1258. priv->io_data->ssh_stdout_channel = g_io_channel_unix_new (ssh_stdout_fd);
  1259. priv->io_data->ssh_stderr_channel = g_io_channel_unix_new (ssh_stderr_fd);
  1260. /* Set io watches on stdout and stderr */
  1261. /* stdout */
  1262. priv->io_data->socket_channel_stdout_eventid = g_io_add_watch (
  1263. priv->io_data->ssh_stdout_channel,
  1264. G_IO_IN,
  1265. nm_ssh_stdout_cb,
  1266. plugin);
  1267. /* stderr */
  1268. priv->io_data->socket_channel_stderr_eventid = g_io_add_watch (
  1269. priv->io_data->ssh_stderr_channel,
  1270. G_IO_IN,
  1271. nm_ssh_stdout_cb,
  1272. plugin);
  1273. /* Set encoding to NULL */
  1274. g_io_channel_set_encoding (priv->io_data->ssh_stdout_channel, NULL, NULL);
  1275. g_io_channel_set_encoding (priv->io_data->ssh_stderr_channel, NULL, NULL);
  1276. /* Add a watch for the process */
  1277. priv->pid = pid;
  1278. ssh_watch = g_child_watch_source_new (pid);
  1279. g_source_set_callback (ssh_watch, (GSourceFunc) ssh_watch_cb, plugin, NULL);
  1280. g_source_attach (ssh_watch, NULL);
  1281. g_source_unref (ssh_watch);
  1282. return TRUE;
  1283. }
  1284. static gboolean
  1285. validate_ssh_agent_socket(const char* ssh_agent_socket, GError **error)
  1286. {
  1287. GFile *gfile = NULL;
  1288. GFileInfo *info = NULL;
  1289. if (debug)
  1290. g_message ("Inspecing ssh agent socket at: '%s'\n", ssh_agent_socket);
  1291. if (!g_file_test (ssh_agent_socket, G_FILE_TEST_EXISTS))
  1292. return FALSE;
  1293. gfile = g_file_new_for_path(ssh_agent_socket);
  1294. if (!gfile)
  1295. return FALSE;
  1296. info = g_file_query_info (gfile, "standard::*,owner::user", 0, NULL, error);
  1297. if (info && G_FILE_TYPE_SPECIAL == g_file_info_get_file_type (info)) {
  1298. g_message ("Found ssh agent socket at: '%s'\n", ssh_agent_socket);
  1299. return TRUE;
  1300. }
  1301. return FALSE;
  1302. }
  1303. static gboolean
  1304. real_connect (NMVPNPlugin *plugin,
  1305. NMConnection *connection,
  1306. GError **error)
  1307. {
  1308. NMSettingVPN *s_vpn;
  1309. const char *user_name;
  1310. s_vpn = NM_SETTING_VPN (nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN));
  1311. if (!s_vpn) {
  1312. g_set_error (error,
  1313. NM_VPN_PLUGIN_ERROR,
  1314. NM_VPN_PLUGIN_ERROR_CONNECTION_INVALID,
  1315. "%s",
  1316. _("Could not process the request because the VPN connection settings were invalid."));
  1317. return FALSE;
  1318. }
  1319. user_name = nm_setting_vpn_get_user_name (s_vpn);
  1320. /* Validate the properties */
  1321. if (!nm_ssh_properties_validate (s_vpn, error))
  1322. return FALSE;
  1323. /* Finally try to start SSH */
  1324. if (!nm_ssh_start_ssh_binary (NM_SSH_PLUGIN (plugin), s_vpn, user_name, error))
  1325. return FALSE;
  1326. return TRUE;
  1327. }
  1328. static gboolean
  1329. real_need_secrets (NMVPNPlugin *plugin,
  1330. NMConnection *connection,
  1331. char **setting_name,
  1332. GError **error)
  1333. {
  1334. NMSettingVPN *s_vpn;
  1335. gboolean need_secrets = FALSE;
  1336. const char *auth_type = NULL;
  1337. NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
  1338. g_return_val_if_fail (NM_IS_VPN_PLUGIN (plugin), FALSE);
  1339. g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
  1340. if (debug) {
  1341. g_message ("%s: connection -------------------------------------", __func__);
  1342. nm_connection_dump (connection);
  1343. }
  1344. s_vpn = NM_SETTING_VPN (nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN));
  1345. if (!s_vpn) {
  1346. g_set_error (error,
  1347. NM_VPN_PLUGIN_ERROR,
  1348. NM_VPN_PLUGIN_ERROR_CONNECTION_INVALID,
  1349. "%s",
  1350. _("Could not process the request because the VPN connection settings were invalid."));
  1351. return FALSE;
  1352. }
  1353. /* Get auth_type from s_vpn */
  1354. auth_type = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_AUTH_TYPE);
  1355. if (!auth_type)
  1356. auth_type = NM_SSH_AUTH_TYPE_SSH_AGENT;
  1357. /* Lets see if we need some passwords... */
  1358. if (!strncmp (auth_type, NM_SSH_AUTH_TYPE_PASSWORD, strlen(NM_SSH_AUTH_TYPE_PASSWORD))) {
  1359. /* Password auth */
  1360. need_secrets = TRUE;
  1361. nm_setting_get_secret_flags (NM_SETTING (s_vpn), NM_SSH_KEY_PASSWORD, &flags, NULL);
  1362. /* If the password is saved and we can retrieve it, it's not required */
  1363. if (nm_setting_vpn_get_secret (NM_SETTING_VPN (s_vpn), NM_SSH_KEY_PASSWORD)) {
  1364. need_secrets = FALSE;
  1365. }
  1366. } else if (!strncmp (auth_type, NM_SSH_AUTH_TYPE_KEY, strlen(NM_SSH_AUTH_TYPE_KEY))) {
  1367. /* FIXME check if key file needs a password */
  1368. need_secrets = FALSE;
  1369. } else if (!strncmp (auth_type, NM_SSH_AUTH_TYPE_SSH_AGENT, strlen(NM_SSH_AUTH_TYPE_SSH_AGENT))) {
  1370. /* If we don't have our SSH_AUTH_SOCK set, we need it
  1371. * SSH_AUTH_SOCK is passed as a secret only because it has to come
  1372. * from a user's context and this plugin will run as root... */
  1373. const gchar *ssh_agent_socket = NULL;
  1374. ssh_agent_socket = nm_setting_vpn_get_secret (s_vpn, NM_SSH_KEY_SSH_AUTH_SOCK);
  1375. if (ssh_agent_socket && validate_ssh_agent_socket (ssh_agent_socket, error)) {
  1376. need_secrets = FALSE;
  1377. } else {
  1378. need_secrets = TRUE;
  1379. }
  1380. } else {
  1381. g_set_error (
  1382. error,
  1383. NM_VPN_PLUGIN_ERROR,
  1384. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  1385. _("Unknown authentication type: %s."), auth_type);
  1386. }
  1387. if (need_secrets)
  1388. *setting_name = NM_SETTING_VPN_SETTING_NAME;
  1389. return need_secrets;
  1390. }
  1391. static gboolean
  1392. ensure_killed (gpointer data)
  1393. {
  1394. int pid = GPOINTER_TO_INT (data);
  1395. if (kill (pid, 0) == 0)
  1396. kill (pid, SIGKILL);
  1397. return FALSE;
  1398. }
  1399. static gboolean
  1400. real_disconnect (NMVPNPlugin *plugin,
  1401. GError **err)
  1402. {
  1403. NMSshPluginPrivate *priv = NM_SSH_PLUGIN_GET_PRIVATE (plugin);
  1404. if (priv->pid) {
  1405. if (kill (priv->pid, SIGTERM) == 0)
  1406. g_timeout_add (2000, ensure_killed, GINT_TO_POINTER (priv->pid));
  1407. else
  1408. kill (priv->pid, SIGKILL);
  1409. g_message ("Terminated ssh daemon with PID %d.", priv->pid);
  1410. priv->pid = 0;
  1411. }
  1412. return TRUE;
  1413. }
  1414. static void
  1415. nm_ssh_plugin_init (NMSshPlugin *plugin)
  1416. {
  1417. }
  1418. static void
  1419. nm_ssh_plugin_class_init (NMSshPluginClass *plugin_class)
  1420. {
  1421. GObjectClass *object_class = G_OBJECT_CLASS (plugin_class);
  1422. NMVPNPluginClass *parent_class = NM_VPN_PLUGIN_CLASS (plugin_class);
  1423. g_type_class_add_private (object_class, sizeof (NMSshPluginPrivate));
  1424. /* virtual methods */
  1425. parent_class->connect = real_connect;
  1426. parent_class->need_secrets = real_need_secrets;
  1427. parent_class->disconnect = real_disconnect;
  1428. }
  1429. static void
  1430. plugin_state_changed (NMSshPlugin *plugin,
  1431. NMVPNServiceState state,
  1432. gpointer user_data)
  1433. {
  1434. switch (state) {
  1435. case NM_VPN_SERVICE_STATE_UNKNOWN:
  1436. case NM_VPN_SERVICE_STATE_INIT:
  1437. case NM_VPN_SERVICE_STATE_SHUTDOWN:
  1438. case NM_VPN_SERVICE_STATE_STOPPING:
  1439. case NM_VPN_SERVICE_STATE_STOPPED:
  1440. default:
  1441. break;
  1442. }
  1443. }
  1444. NMSshPlugin *
  1445. nm_ssh_plugin_new (void)
  1446. {
  1447. NMSshPlugin *plugin;
  1448. plugin = (NMSshPlugin *) g_object_new (NM_TYPE_SSH_PLUGIN,
  1449. NM_VPN_PLUGIN_DBUS_SERVICE_NAME,
  1450. NM_DBUS_SERVICE_SSH,
  1451. NULL);
  1452. if (plugin)
  1453. g_signal_connect (G_OBJECT (plugin), "state-changed", G_CALLBACK (plugin_state_changed), NULL);
  1454. return plugin;
  1455. }
  1456. static void
  1457. signal_handler (int signo)
  1458. {
  1459. if (signo == SIGINT || signo == SIGTERM)
  1460. g_main_loop_quit (loop);
  1461. }
  1462. static void
  1463. setup_signals (void)
  1464. {
  1465. struct sigaction action;
  1466. sigset_t mask;
  1467. sigemptyset (&mask);
  1468. action.sa_handler = signal_handler;
  1469. action.sa_mask = mask;
  1470. action.sa_flags = 0;
  1471. sigaction (SIGTERM, &action, NULL);
  1472. sigaction (SIGINT, &action, NULL);
  1473. }
  1474. static void
  1475. quit_mainloop (NMVPNPlugin *plugin, gpointer user_data)
  1476. {
  1477. g_main_loop_quit ((GMainLoop *) user_data);
  1478. }
  1479. int
  1480. main (int argc, char *argv[])
  1481. {
  1482. NMSshPlugin *plugin;
  1483. gboolean persist = FALSE;
  1484. GOptionContext *opt_ctx = NULL;
  1485. GOptionEntry options[] = {
  1486. { "persist", 0, 0, G_OPTION_ARG_NONE, &persist, N_("Don't quit when VPN connection terminates"), NULL },
  1487. { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, N_("Enable verbose debug logging (may expose passwords)"), NULL },
  1488. {NULL}
  1489. };
  1490. #if !GLIB_CHECK_VERSION (2, 35, 3)
  1491. g_type_init ();
  1492. #endif
  1493. /* locale will be set according to environment LC_* variables */
  1494. setlocale (LC_ALL, "");
  1495. bindtextdomain (GETTEXT_PACKAGE, NM_SSH_LOCALEDIR);
  1496. bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
  1497. textdomain (GETTEXT_PACKAGE);
  1498. /* Parse options */
  1499. opt_ctx = g_option_context_new (NULL);
  1500. g_option_context_set_translation_domain (opt_ctx, GETTEXT_PACKAGE);
  1501. g_option_context_set_ignore_unknown_options (opt_ctx, FALSE);
  1502. g_option_context_set_help_enabled (opt_ctx, TRUE);
  1503. g_option_context_add_main_entries (opt_ctx, options, NULL);
  1504. g_option_context_set_summary (opt_ctx,
  1505. _("nm-ssh-service provides integrated SSH capability to NetworkManager."));
  1506. g_option_context_parse (opt_ctx, &argc, &argv, NULL);
  1507. g_option_context_free (opt_ctx);
  1508. if (getenv ("SSH_DEBUG"))
  1509. debug = TRUE;
  1510. if (debug)
  1511. g_message ("nm-ssh-service (version " DIST_VERSION ") starting...");
  1512. if (system ("/sbin/modprobe tun") == -1)
  1513. exit (EXIT_FAILURE);
  1514. plugin = nm_ssh_plugin_new ();
  1515. if (!plugin)
  1516. exit (EXIT_FAILURE);
  1517. loop = g_main_loop_new (NULL, FALSE);
  1518. if (!persist)
  1519. g_signal_connect (plugin, "quit", G_CALLBACK (quit_mainloop), loop);
  1520. setup_signals ();
  1521. g_main_loop_run (loop);
  1522. g_main_loop_unref (loop);
  1523. g_object_unref (plugin);
  1524. exit (EXIT_SUCCESS);
  1525. }