main.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
  2. /* NetworkManager Wireless Applet -- Display wireless access points and allow user control
  3. *
  4. * Dan Williams <dcbw@redhat.com>
  5. * Tim Niemueller <tim@niemueller.de>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along
  18. * with this program; if not, write to the Free Software Foundation, Inc.,
  19. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  20. *
  21. * (C) Copyright 2004 - 2008 Red Hat, Inc.
  22. * 2005 Tim Niemueller [www.niemueller.de]
  23. */
  24. #ifdef HAVE_CONFIG_H
  25. #include <config.h>
  26. #endif
  27. #include <errno.h>
  28. #include <string.h>
  29. #include <stdlib.h>
  30. #include <glib/gi18n.h>
  31. #include <gtk/gtk.h>
  32. #define SECRET_API_SUBJECT_TO_CHANGE
  33. #include <libsecret/secret.h>
  34. #include <nm-setting-vpn.h>
  35. #include <nm-setting-connection.h>
  36. #include <nm-vpn-plugin-utils.h>
  37. #include <nm-vpn-password-dialog.h>
  38. #include "common/utils.h"
  39. #include "src/nm-openvpn-service.h"
  40. #define KEYRING_UUID_TAG "connection-uuid"
  41. #define KEYRING_SN_TAG "setting-name"
  42. #define KEYRING_SK_TAG "setting-key"
  43. static const SecretSchema network_manager_secret_schema = {
  44. "org.freedesktop.NetworkManager.Connection",
  45. SECRET_SCHEMA_DONT_MATCH_NAME,
  46. {
  47. { KEYRING_UUID_TAG, SECRET_SCHEMA_ATTRIBUTE_STRING },
  48. { KEYRING_SN_TAG, SECRET_SCHEMA_ATTRIBUTE_STRING },
  49. { KEYRING_SK_TAG, SECRET_SCHEMA_ATTRIBUTE_STRING },
  50. { NULL, 0 },
  51. }
  52. };
  53. #define UI_KEYFILE_GROUP "VPN Plugin UI"
  54. static char *
  55. keyring_lookup_secret (const char *uuid, const char *secret_name)
  56. {
  57. GHashTable *attrs;
  58. GList *list;
  59. char *secret = NULL;
  60. attrs = secret_attributes_build (&network_manager_secret_schema,
  61. KEYRING_UUID_TAG, uuid,
  62. KEYRING_SN_TAG, NM_SETTING_VPN_SETTING_NAME,
  63. KEYRING_SK_TAG, secret_name,
  64. NULL);
  65. list = secret_service_search_sync (NULL, &network_manager_secret_schema, attrs,
  66. SECRET_SEARCH_ALL | SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS,
  67. NULL, NULL);
  68. if (list && list->data) {
  69. SecretItem *item = list->data;
  70. SecretValue *value = secret_item_get_secret (item);
  71. if (value) {
  72. secret = g_strdup (secret_value_get (value, NULL));
  73. secret_value_unref (value);
  74. }
  75. }
  76. g_list_free_full (list, g_object_unref);
  77. g_hash_table_unref (attrs);
  78. return secret;
  79. }
  80. /*****************************************************************/
  81. typedef void (*NoSecretsRequiredFunc) (void);
  82. /* Returns TRUE on success, FALSE on cancel */
  83. typedef gboolean (*AskUserFunc) (const char *vpn_name,
  84. const char *prompt,
  85. gboolean need_password,
  86. const char *existing_password,
  87. char **out_new_password,
  88. gboolean need_certpass,
  89. const char *existing_certpass,
  90. char **out_new_certpass);
  91. typedef void (*FinishFunc) (const char *vpn_name,
  92. const char *prompt,
  93. gboolean allow_interaction,
  94. gboolean need_password,
  95. const char *password,
  96. gboolean need_certpass,
  97. const char *certpass);
  98. /*****************************************************************/
  99. /* External UI mode stuff */
  100. static void
  101. keyfile_add_entry_info (GKeyFile *keyfile,
  102. const gchar *key,
  103. const gchar *value,
  104. const gchar *label,
  105. gboolean is_secret,
  106. gboolean should_ask)
  107. {
  108. g_key_file_set_string (keyfile, key, "Value", value);
  109. g_key_file_set_string (keyfile, key, "Label", label);
  110. g_key_file_set_boolean (keyfile, key, "IsSecret", is_secret);
  111. g_key_file_set_boolean (keyfile, key, "ShouldAsk", should_ask);
  112. }
  113. static void
  114. keyfile_print_stdout (GKeyFile *keyfile)
  115. {
  116. gchar *data;
  117. gsize length;
  118. data = g_key_file_to_data (keyfile, &length, NULL);
  119. fputs (data, stdout);
  120. g_free (data);
  121. }
  122. static void
  123. eui_no_secrets_required (void)
  124. {
  125. GKeyFile *keyfile;
  126. keyfile = g_key_file_new ();
  127. g_key_file_set_integer (keyfile, UI_KEYFILE_GROUP, "Version", 2);
  128. keyfile_add_entry_info (keyfile, NM_OPENVPN_KEY_NOSECRET, "true", "", TRUE, FALSE);
  129. keyfile_print_stdout (keyfile);
  130. g_key_file_unref (keyfile);
  131. }
  132. static void
  133. eui_finish (const char *vpn_name,
  134. const char *prompt,
  135. gboolean allow_interaction,
  136. gboolean need_password,
  137. const char *existing_password,
  138. gboolean need_certpass,
  139. const char *existing_certpass)
  140. {
  141. GKeyFile *keyfile;
  142. char *title;
  143. keyfile = g_key_file_new ();
  144. g_key_file_set_integer (keyfile, UI_KEYFILE_GROUP, "Version", 2);
  145. g_key_file_set_string (keyfile, UI_KEYFILE_GROUP, "Description", prompt);
  146. title = g_strdup_printf (_("Authenticate VPN %s"), vpn_name);
  147. g_key_file_set_string (keyfile, UI_KEYFILE_GROUP, "Title", title);
  148. g_free (title);
  149. keyfile_add_entry_info (keyfile,
  150. NM_OPENVPN_KEY_PASSWORD,
  151. existing_password ? existing_password : "",
  152. _("Password:"),
  153. TRUE,
  154. need_password && allow_interaction);
  155. keyfile_add_entry_info (keyfile,
  156. NM_OPENVPN_KEY_CERTPASS,
  157. existing_certpass ? existing_certpass : "",
  158. _("Certificate password:"),
  159. TRUE,
  160. need_certpass && allow_interaction);
  161. keyfile_print_stdout (keyfile);
  162. g_key_file_unref (keyfile);
  163. }
  164. /*****************************************************************/
  165. static void
  166. std_no_secrets_required (void)
  167. {
  168. printf ("%s\n%s\n\n\n", NM_OPENVPN_KEY_NOSECRET, "true");
  169. }
  170. static gboolean
  171. std_ask_user (const char *vpn_name,
  172. const char *prompt,
  173. gboolean need_password,
  174. const char *existing_password,
  175. char **out_new_password,
  176. gboolean need_certpass,
  177. const char *existing_certpass,
  178. char **out_new_certpass)
  179. {
  180. NMAVpnPasswordDialog *dialog;
  181. gboolean success = FALSE;
  182. g_return_val_if_fail (vpn_name != NULL, FALSE);
  183. g_return_val_if_fail (prompt != NULL, FALSE);
  184. g_return_val_if_fail (out_new_password != NULL, FALSE);
  185. g_return_val_if_fail (out_new_certpass != NULL, FALSE);
  186. dialog = NMA_VPN_PASSWORD_DIALOG (nma_vpn_password_dialog_new (_("Authenticate VPN"), prompt, NULL));
  187. /* pre-fill dialog with existing passwords */
  188. nma_vpn_password_dialog_set_show_password (dialog, need_password);
  189. if (need_password)
  190. nma_vpn_password_dialog_set_password (dialog, existing_password);
  191. nma_vpn_password_dialog_set_show_password_secondary (dialog, need_certpass);
  192. if (need_certpass) {
  193. nma_vpn_password_dialog_set_password_secondary_label (dialog, _("Certificate pass_word:") );
  194. nma_vpn_password_dialog_set_password_secondary (dialog, existing_certpass);
  195. }
  196. gtk_widget_show (GTK_WIDGET (dialog));
  197. if (nma_vpn_password_dialog_run_and_block (dialog)) {
  198. if (need_password)
  199. *out_new_password = g_strdup (nma_vpn_password_dialog_get_password (dialog));
  200. if (need_certpass)
  201. *out_new_certpass = g_strdup (nma_vpn_password_dialog_get_password_secondary (dialog));
  202. success = TRUE;
  203. }
  204. gtk_widget_destroy (GTK_WIDGET (dialog));
  205. return success;
  206. }
  207. static void
  208. wait_for_quit (void)
  209. {
  210. GString *str;
  211. char c;
  212. ssize_t n;
  213. time_t start;
  214. str = g_string_sized_new (10);
  215. start = time (NULL);
  216. do {
  217. errno = 0;
  218. n = read (0, &c, 1);
  219. if (n == 0 || (n < 0 && errno == EAGAIN))
  220. g_usleep (G_USEC_PER_SEC / 10);
  221. else if (n == 1) {
  222. g_string_append_c (str, c);
  223. if (strstr (str->str, "QUIT") || (str->len > 10))
  224. break;
  225. } else
  226. break;
  227. } while (time (NULL) < start + 20);
  228. g_string_free (str, TRUE);
  229. }
  230. static void
  231. std_finish (const char *vpn_name,
  232. const char *prompt,
  233. gboolean allow_interaction,
  234. gboolean need_password,
  235. const char *password,
  236. gboolean need_certpass,
  237. const char *certpass)
  238. {
  239. /* Send the passwords back to our parent */
  240. if (password)
  241. printf ("%s\n%s\n", NM_OPENVPN_KEY_PASSWORD, password);
  242. if (certpass)
  243. printf ("%s\n%s\n", NM_OPENVPN_KEY_CERTPASS, certpass);
  244. printf ("\n\n");
  245. /* for good measure, flush stdout since Kansas is going Bye-Bye */
  246. fflush (stdout);
  247. /* Wait for quit signal */
  248. wait_for_quit ();
  249. }
  250. /*****************************************************************/
  251. static void
  252. get_existing_passwords (GHashTable *vpn_data,
  253. GHashTable *existing_secrets,
  254. const char *vpn_uuid,
  255. gboolean need_password,
  256. gboolean need_certpass,
  257. char **out_password,
  258. char **out_certpass)
  259. {
  260. NMSettingSecretFlags pw_flags = NM_SETTING_SECRET_FLAG_NONE;
  261. NMSettingSecretFlags cp_flags = NM_SETTING_SECRET_FLAG_NONE;
  262. g_return_if_fail (out_password != NULL);
  263. g_return_if_fail (out_certpass != NULL);
  264. nm_vpn_plugin_utils_get_secret_flags (vpn_data, NM_OPENVPN_KEY_PASSWORD, &pw_flags);
  265. if (need_password) {
  266. if (!(pw_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)) {
  267. *out_password = g_strdup (g_hash_table_lookup (existing_secrets, NM_OPENVPN_KEY_PASSWORD));
  268. if (!*out_password)
  269. *out_password = keyring_lookup_secret (vpn_uuid, NM_OPENVPN_KEY_PASSWORD);
  270. }
  271. }
  272. nm_vpn_plugin_utils_get_secret_flags (vpn_data, NM_OPENVPN_KEY_CERTPASS, &cp_flags);
  273. if (need_certpass) {
  274. if (!(cp_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)) {
  275. *out_certpass = g_strdup (g_hash_table_lookup (existing_secrets, NM_OPENVPN_KEY_CERTPASS));
  276. if (!*out_certpass)
  277. *out_certpass = keyring_lookup_secret (vpn_uuid, NM_OPENVPN_KEY_CERTPASS);
  278. }
  279. }
  280. }
  281. #define VPN_MSG_TAG "x-vpn-message:"
  282. static char *
  283. get_passwords_required (GHashTable *data,
  284. char **hints,
  285. gboolean *out_need_password,
  286. gboolean *out_need_certpass)
  287. {
  288. const char *ctype, *val;
  289. NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
  290. char *prompt = NULL;
  291. char **iter;
  292. /* If hints are given, then always ask for what the hints require */
  293. if (hints && g_strv_length (hints)) {
  294. for (iter = hints; iter && *iter; iter++) {
  295. if (!prompt && g_str_has_prefix (*iter, VPN_MSG_TAG))
  296. prompt = g_strdup (*iter + strlen (VPN_MSG_TAG));
  297. else if (strcmp (*iter, NM_OPENVPN_KEY_PASSWORD) == 0)
  298. *out_need_password = TRUE;
  299. else if (strcmp (*iter, NM_OPENVPN_KEY_CERTPASS) == 0)
  300. *out_need_certpass = TRUE;
  301. }
  302. return prompt;
  303. }
  304. ctype = g_hash_table_lookup (data, NM_OPENVPN_KEY_CONNECTION_TYPE);
  305. g_return_val_if_fail (ctype != NULL, NULL);
  306. if (!strcmp (ctype, NM_OPENVPN_CONTYPE_TLS) || !strcmp (ctype, NM_OPENVPN_CONTYPE_PASSWORD_TLS)) {
  307. /* Normal user password */
  308. nm_vpn_plugin_utils_get_secret_flags (data, NM_OPENVPN_KEY_PASSWORD, &flags);
  309. if ( !strcmp (ctype, NM_OPENVPN_CONTYPE_PASSWORD_TLS)
  310. && !(flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED))
  311. *out_need_password = TRUE;
  312. /* Encrypted private key password */
  313. val = g_hash_table_lookup (data, NM_OPENVPN_KEY_KEY);
  314. if (val)
  315. *out_need_certpass = is_encrypted (val);
  316. } else if (!strcmp (ctype, NM_OPENVPN_CONTYPE_PASSWORD)) {
  317. nm_vpn_plugin_utils_get_secret_flags (data, NM_OPENVPN_KEY_PASSWORD, &flags);
  318. if (!(flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED))
  319. *out_need_password = TRUE;
  320. }
  321. return NULL;
  322. }
  323. static void
  324. free_secret (char *p)
  325. {
  326. if (p) {
  327. memset (p, 0, strlen (p));
  328. g_free (p);
  329. }
  330. }
  331. int
  332. main (int argc, char *argv[])
  333. {
  334. gboolean retry = FALSE, allow_interaction = FALSE;
  335. gchar *vpn_name = NULL;
  336. gchar *vpn_uuid = NULL;
  337. gchar *vpn_service = NULL;
  338. GHashTable *data = NULL, *secrets = NULL;
  339. gboolean need_password = FALSE, need_certpass = FALSE;
  340. char *existing_password = NULL, *existing_certpass = NULL;
  341. char *new_password = NULL, *new_certpass = NULL;
  342. char **hints = NULL;
  343. char *prompt = NULL;
  344. gboolean external_ui_mode = FALSE, canceled = FALSE, ask_user = FALSE;
  345. NoSecretsRequiredFunc no_secrets_required_func = NULL;
  346. AskUserFunc ask_user_func = NULL;
  347. FinishFunc finish_func = NULL;
  348. GOptionContext *context;
  349. GOptionEntry entries[] = {
  350. { "reprompt", 'r', 0, G_OPTION_ARG_NONE, &retry, "Reprompt for passwords", NULL},
  351. { "uuid", 'u', 0, G_OPTION_ARG_STRING, &vpn_uuid, "UUID of VPN connection", NULL},
  352. { "name", 'n', 0, G_OPTION_ARG_STRING, &vpn_name, "Name of VPN connection", NULL},
  353. { "service", 's', 0, G_OPTION_ARG_STRING, &vpn_service, "VPN service type", NULL},
  354. { "allow-interaction", 'i', 0, G_OPTION_ARG_NONE, &allow_interaction, "Allow user interaction", NULL},
  355. { "external-ui-mode", 0, 0, G_OPTION_ARG_NONE, &external_ui_mode, "External UI mode", NULL},
  356. { "hint", 't', 0, G_OPTION_ARG_STRING_ARRAY, &hints, "Hints from the VPN plugin", NULL},
  357. { NULL }
  358. };
  359. bindtextdomain (GETTEXT_PACKAGE, NULL);
  360. bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
  361. textdomain (GETTEXT_PACKAGE);
  362. gtk_init (&argc, &argv);
  363. context = g_option_context_new ("- openvpn auth dialog");
  364. g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
  365. g_option_context_parse (context, &argc, &argv, NULL);
  366. g_option_context_free (context);
  367. if (vpn_uuid == NULL || vpn_name == NULL || vpn_service == NULL) {
  368. fprintf (stderr, "Have to supply ID, name, and service\n");
  369. return EXIT_FAILURE;
  370. }
  371. if (strcmp (vpn_service, NM_DBUS_SERVICE_OPENVPN) != 0) {
  372. fprintf (stderr, "This dialog only works with the '%s' service\n", NM_DBUS_SERVICE_OPENVPN);
  373. return EXIT_FAILURE;
  374. }
  375. if (!nm_vpn_plugin_utils_read_vpn_details (0, &data, &secrets)) {
  376. fprintf (stderr, "Failed to read '%s' (%s) data and secrets from stdin.\n",
  377. vpn_name, vpn_uuid);
  378. return 1;
  379. }
  380. if (external_ui_mode) {
  381. no_secrets_required_func = eui_no_secrets_required;
  382. finish_func = eui_finish;
  383. } else {
  384. no_secrets_required_func = std_no_secrets_required;
  385. ask_user_func = std_ask_user;
  386. finish_func = std_finish;
  387. }
  388. /* Determine which passwords are actually required, either from hints or
  389. * from looking at the VPN configuration.
  390. */
  391. prompt = get_passwords_required (data, hints, &need_password, &need_certpass);
  392. if (!prompt)
  393. prompt = g_strdup_printf (_("You need to authenticate to access the Virtual Private Network '%s'."), vpn_name);
  394. /* Exit early if we don't need any passwords */
  395. if (!need_password && !need_certpass)
  396. no_secrets_required_func ();
  397. else {
  398. get_existing_passwords (data,
  399. secrets,
  400. vpn_uuid,
  401. need_password,
  402. need_certpass,
  403. &existing_password,
  404. &existing_certpass);
  405. if (need_password && !existing_password)
  406. ask_user = TRUE;
  407. if (need_certpass && !existing_certpass)
  408. ask_user = TRUE;
  409. /* If interaction is allowed then ask the user, otherwise pass back
  410. * whatever existing secrets we can find.
  411. */
  412. if (ask_user_func && allow_interaction && (ask_user || retry)) {
  413. canceled = !ask_user_func (vpn_name,
  414. prompt,
  415. need_password,
  416. existing_password,
  417. &new_password,
  418. need_certpass,
  419. existing_certpass,
  420. &new_certpass);
  421. }
  422. if (!canceled) {
  423. finish_func (vpn_name,
  424. prompt,
  425. allow_interaction,
  426. need_password,
  427. new_password ? new_password : existing_password,
  428. need_certpass,
  429. new_certpass ? new_certpass : existing_certpass);
  430. }
  431. free_secret (existing_password);
  432. free_secret (existing_certpass);
  433. free_secret (new_password);
  434. free_secret (new_certpass);
  435. }
  436. if (data)
  437. g_hash_table_unref (data);
  438. if (secrets)
  439. g_hash_table_unref (secrets);
  440. if (hints)
  441. g_strfreev (hints);
  442. g_free (prompt);
  443. return canceled ? 1 : 0;
  444. }