/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright (C) 2009 Red Hat, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "shvar.h" #include "reader.h" #include "writer.h" #include "utils.h" #include "crypto.h" #define PLUGIN_WARN(pname, fmt, args...) \ { g_warning (" " pname ": " fmt, ##args); } static void set_secret (shvarFile *ifcfg, const char *key, const char *value) { shvarFile *keyfile; keyfile = utils_get_keys_ifcfg (ifcfg->fileName, TRUE); if (!keyfile) { PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: could not create key file for '%s'", ifcfg->fileName); goto error; } /* Clear the secret from the actual ifcfg */ svSetValue (ifcfg, key, NULL, FALSE); svSetValue (keyfile, key, value, FALSE); if (svWriteFile (keyfile, 0600)) { PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: could not update key file '%s'", keyfile->fileName); svCloseFile (keyfile); goto error; } svCloseFile (keyfile); return; error: /* Try setting the secret in the actual ifcfg */ svSetValue (ifcfg, key, value, FALSE); } static gboolean write_secret_file (const char *path, const char *data, gsize len, GError **error) { char *tmppath; int fd = -1, written; gboolean success = FALSE; tmppath = g_malloc0 (strlen (path) + 10); if (!tmppath) { g_set_error (error, ifcfg_plugin_error_quark (), 0, "Could not allocate memory for temporary file for '%s'", path); return FALSE; } memcpy (tmppath, path, strlen (path)); strcat (tmppath, ".XXXXXX"); errno = 0; fd = mkstemp (tmppath); if (fd < 0) { g_set_error (error, ifcfg_plugin_error_quark (), 0, "Could not create temporary file for '%s': %d", path, errno); goto out; } /* Only readable by root */ errno = 0; if (fchmod (fd, S_IRUSR | S_IWUSR)) { close (fd); unlink (tmppath); g_set_error (error, ifcfg_plugin_error_quark (), 0, "Could not set permissions for temporary file '%s': %d", path, errno); goto out; } errno = 0; written = write (fd, data, len); if (written != len) { close (fd); unlink (tmppath); g_set_error (error, ifcfg_plugin_error_quark (), 0, "Could not write temporary file for '%s': %d", path, errno); goto out; } close (fd); /* Try to rename */ errno = 0; if (rename (tmppath, path)) { unlink (tmppath); g_set_error (error, ifcfg_plugin_error_quark (), 0, "Could not rename temporary file to '%s': %d", path, errno); goto out; } success = TRUE; out: return success; } typedef struct ObjectType { const char *setting_key; const char *ifcfg_key; const char *path_tag; const char *hash_tag; const char *suffix; } ObjectType; static const ObjectType ca_type = { NM_SETTING_802_1X_CA_CERT, "IEEE_8021X_CA_CERT", TAG_CA_CERT_PATH, TAG_CA_CERT_HASH, "ca-cert.der" }; static const ObjectType phase2_ca_type = { NM_SETTING_802_1X_PHASE2_CA_CERT, "IEEE_8021X_INNER_CA_CERT", TAG_PHASE2_CA_CERT_PATH, TAG_PHASE2_CA_CERT_HASH, "inner-ca-cert.der" }; static const ObjectType client_type = { NM_SETTING_802_1X_CLIENT_CERT, "IEEE_8021X_CLIENT_CERT", TAG_CLIENT_CERT_PATH, TAG_CLIENT_CERT_HASH, "client-cert.der" }; static const ObjectType phase2_client_type = { NM_SETTING_802_1X_PHASE2_CLIENT_CERT, "IEEE_8021X_INNER_CLIENT_CERT", TAG_PHASE2_CLIENT_CERT_PATH, TAG_PHASE2_CLIENT_CERT_HASH, "inner-client-cert.der" }; static const ObjectType pk_type = { NM_SETTING_802_1X_PRIVATE_KEY, "IEEE_8021X_PRIVATE_KEY", TAG_PRIVATE_KEY_PATH, TAG_PRIVATE_KEY_HASH, "private-key.pem" }; static const ObjectType phase2_pk_type = { NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, "IEEE_8021X_INNER_PRIVATE_KEY", TAG_PHASE2_PRIVATE_KEY_PATH, TAG_PHASE2_PRIVATE_KEY_HASH, "inner-private-key.pem" }; static const ObjectType p12_type = { NM_SETTING_802_1X_PRIVATE_KEY, "IEEE_8021X_PRIVATE_KEY", TAG_PRIVATE_KEY_PATH, TAG_PRIVATE_KEY_HASH, "private-key.p12" }; static const ObjectType phase2_p12_type = { NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, "IEEE_8021X_INNER_PRIVATE_KEY", TAG_PHASE2_PRIVATE_KEY_PATH, TAG_PHASE2_PRIVATE_KEY_HASH, "inner-private-key.p12" }; static gboolean write_object (NMSetting8021x *s_8021x, shvarFile *ifcfg, const GByteArray *object, const ObjectType *objtype, gboolean *wrote, GError **error) { const char *orig_hash, *orig_file; char *new_hash = NULL, *new_file = NULL; gboolean success = FALSE; GError *write_error = NULL; g_return_val_if_fail (objtype != NULL, FALSE); g_return_val_if_fail (ifcfg != NULL, FALSE); g_return_val_if_fail (wrote != NULL, FALSE); *wrote = FALSE; if (!object) { svSetValue (ifcfg, objtype->ifcfg_key, NULL, FALSE); return TRUE; } new_hash = utils_hash_byte_array (object); if (!new_hash) { g_set_error (error, ifcfg_plugin_error_quark (), 0, "Could not hash certificate/key data for %s / %s", NM_SETTING_802_1X_SETTING_NAME, objtype->setting_key); return FALSE; } orig_hash = g_object_get_data (G_OBJECT (s_8021x), objtype->hash_tag); orig_file = g_object_get_data (G_OBJECT (s_8021x), objtype->path_tag); if (!orig_hash || !orig_file || strcmp (new_hash, orig_hash)) { /* if the cert data has changed, or there wasn't a cert * originally, write data out to the standard file. */ new_file = utils_cert_path (ifcfg->fileName, objtype->suffix); if (!new_file) { g_set_error (error, ifcfg_plugin_error_quark (), 0, "Could not create file path for %s / %s", NM_SETTING_802_1X_SETTING_NAME, objtype->setting_key); goto out; } if (!write_secret_file (new_file, (const char *) object->data, object->len, &write_error)) { g_set_error (error, ifcfg_plugin_error_quark (), 0, "Could not write certificate/key for %s / %s: %s", NM_SETTING_802_1X_SETTING_NAME, objtype->setting_key, (write_error && write_error->message) ? write_error->message : "(unknown)"); g_clear_error (&write_error); goto out; } *wrote = TRUE; svSetValue (ifcfg, objtype->ifcfg_key, new_file, FALSE); g_object_set_data_full (G_OBJECT (s_8021x), objtype->path_tag, new_file, g_free); new_file = NULL; /* g_object_set_data_full() took ownership */ g_object_set_data_full (G_OBJECT (s_8021x), objtype->hash_tag, new_hash, g_free); new_hash = NULL; /* g_object_set_data_full() took ownership */ } else { /* cert data hasn't changed */ svSetValue (ifcfg, objtype->ifcfg_key, orig_file, FALSE); } success = TRUE; out: g_free (new_hash); g_free (new_file); return success; } static gboolean write_8021x_certs (NMSetting8021x *s_8021x, gboolean phase2, shvarFile *ifcfg, GError **error) { const GByteArray *data; GByteArray *enc_key = NULL; const char *password = NULL; char *generated_pw = NULL; gboolean success = FALSE, is_pkcs12 = FALSE, wrote; const ObjectType *otype = NULL; const char *prop; /* CA certificate */ data = NULL; if (phase2) { prop = NM_SETTING_802_1X_PHASE2_CA_CERT; otype = &phase2_ca_type; } else { prop = NM_SETTING_802_1X_CA_CERT; otype = &ca_type; } g_object_get (G_OBJECT (s_8021x), prop, &data, NULL); if (!write_object (s_8021x, ifcfg, data, otype, &wrote, error)) return FALSE; /* Private key */ if (phase2) { if (nm_setting_802_1x_get_phase2_private_key (s_8021x)) { if (nm_setting_802_1x_get_phase2_private_key_type (s_8021x) == NM_SETTING_802_1X_CK_TYPE_PKCS12) is_pkcs12 = TRUE; } prop = NM_SETTING_802_1X_PHASE2_PRIVATE_KEY; password = nm_setting_802_1x_get_phase2_private_key_password (s_8021x); } else { if (nm_setting_802_1x_get_private_key (s_8021x)) { if (nm_setting_802_1x_get_private_key_type (s_8021x) == NM_SETTING_802_1X_CK_TYPE_PKCS12) is_pkcs12 = TRUE; } prop = NM_SETTING_802_1X_PRIVATE_KEY; password = nm_setting_802_1x_get_private_key_password (s_8021x); } if (is_pkcs12) otype = phase2 ? &phase2_p12_type : &p12_type; else otype = phase2 ? &phase2_pk_type : &pk_type; data = NULL; g_object_get (G_OBJECT (s_8021x), prop, &data, NULL); if (data && !is_pkcs12) { GByteArray *array; if (!password) { /* Create a random private key */ array = crypto_random (32, error); if (!array) goto out; password = generated_pw = utils_bin2hexstr ((const char *) array->data, array->len, -1); memset (array->data, 0, array->len); g_byte_array_free (array, TRUE); } /* Re-encrypt the private key if it's not PKCS#12 (which never decrypted by NM) */ enc_key = crypto_key_to_pem (data, password, error); if (!enc_key) goto out; } if (!write_object (s_8021x, ifcfg, enc_key ? enc_key : data, otype, &wrote, error)) goto out; /* Private key password */ if (phase2) set_secret (ifcfg, "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD", password); else set_secret (ifcfg, "IEEE_8021X_PRIVATE_KEY_PASSWORD", password); if (enc_key) { memset (enc_key->data, 0, enc_key->len); g_byte_array_free (enc_key, TRUE); } /* Client certificate */ if (is_pkcs12) { svSetValue (ifcfg, phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT", NULL, FALSE); } else { if (phase2) { prop = NM_SETTING_802_1X_PHASE2_CLIENT_CERT; otype = &phase2_client_type; } else { prop = NM_SETTING_802_1X_CLIENT_CERT; otype = &client_type; } data = NULL; g_object_get (G_OBJECT (s_8021x), prop, &data, NULL); if (!write_object (s_8021x, ifcfg, data, otype, &wrote, error)) goto out; } success = TRUE; out: if (generated_pw) { memset (generated_pw, 0, strlen (generated_pw)); g_free (generated_pw); } return success; } static gboolean write_8021x_setting (NMConnection *connection, shvarFile *ifcfg, gboolean wired, GError **error) { NMSetting8021x *s_8021x; const char *value; char *tmp = NULL; gboolean success = FALSE; GString *phase2_auth; s_8021x = (NMSetting8021x *) nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X); if (!s_8021x) { /* If wired, clear KEY_MGMT */ if (wired) svSetValue (ifcfg, "KEY_MGMT", NULL, FALSE); return TRUE; } /* If wired, write KEY_MGMT */ if (wired) svSetValue (ifcfg, "KEY_MGMT", "IEEE8021X", FALSE); /* EAP method */ if (nm_setting_802_1x_get_num_eap_methods (s_8021x)) { value = nm_setting_802_1x_get_eap_method (s_8021x, 0); if (value) tmp = g_ascii_strup (value, -1); } svSetValue (ifcfg, "IEEE_8021X_EAP_METHODS", tmp ? tmp : NULL, FALSE); g_free (tmp); svSetValue (ifcfg, "IEEE_8021X_IDENTITY", nm_setting_802_1x_get_identity (s_8021x), FALSE); svSetValue (ifcfg, "IEEE_8021X_ANON_IDENTITY", nm_setting_802_1x_get_anonymous_identity (s_8021x), FALSE); set_secret (ifcfg, "IEEE_8021X_PASSWORD", nm_setting_802_1x_get_password (s_8021x)); /* PEAP version */ value = nm_setting_802_1x_get_phase1_peapver (s_8021x); svSetValue (ifcfg, "IEEE_8021X_PEAP_VERSION", NULL, FALSE); if (value && (!strcmp (value, "0") || !strcmp (value, "1"))) svSetValue (ifcfg, "IEEE_8021X_PEAP_VERSION", value, FALSE); /* Force new PEAP label */ value = nm_setting_802_1x_get_phase1_peaplabel (s_8021x); svSetValue (ifcfg, "IEEE_8021X_PEAP_FORCE_NEW_LABEL", NULL, FALSE); if (value && !strcmp (value, "1")) svSetValue (ifcfg, "IEEE_8021X_PEAP_FORCE_NEW_LABEL", "yes", FALSE); /* Phase2 auth methods */ svSetValue (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS", NULL, FALSE); phase2_auth = g_string_new (NULL); value = nm_setting_802_1x_get_phase2_auth (s_8021x); if (value) { tmp = g_ascii_strup (value, -1); g_string_append (phase2_auth, tmp); g_free (tmp); } value = nm_setting_802_1x_get_phase2_autheap (s_8021x); if (value) { if (phase2_auth->len) g_string_append_c (phase2_auth, ' '); tmp = g_ascii_strup (value, -1); g_string_append_printf (phase2_auth, "EAP-%s", tmp); g_free (tmp); } svSetValue (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS", phase2_auth->len ? phase2_auth->str : NULL, FALSE); g_string_free (phase2_auth, TRUE); success = write_8021x_certs (s_8021x, FALSE, ifcfg, error); if (success) { /* phase2/inner certs */ success = write_8021x_certs (s_8021x, TRUE, ifcfg, error); } return success; } static gboolean write_wireless_security_setting (NMConnection *connection, shvarFile *ifcfg, gboolean adhoc, gboolean *no_8021x, GError **error) { NMSettingWirelessSecurity *s_wsec; const char *key_mgmt, *auth_alg, *key, *proto, *cipher, *psk; gboolean wep = FALSE, wpa = FALSE; char *tmp; guint32 i, num; GString *str; s_wsec = (NMSettingWirelessSecurity *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY); if (!s_wsec) { g_set_error (error, ifcfg_plugin_error_quark (), 0, "Missing '%s' setting", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); return FALSE; } key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); g_assert (key_mgmt); auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec); svSetValue (ifcfg, "DEFAULTKEY", NULL, FALSE); if (!strcmp (key_mgmt, "none")) { wep = TRUE; *no_8021x = TRUE; } else if (!strcmp (key_mgmt, "wpa-none") || !strcmp (key_mgmt, "wpa-psk")) { svSetValue (ifcfg, "KEY_MGMT", "WPA-PSK", FALSE); wpa = TRUE; *no_8021x = TRUE; } else if (!strcmp (key_mgmt, "ieee8021x")) { svSetValue (ifcfg, "KEY_MGMT", "IEEE8021X", FALSE); } else if (!strcmp (key_mgmt, "wpa-eap")) { svSetValue (ifcfg, "KEY_MGMT", "WPA-EAP", FALSE); wpa = TRUE; } svSetValue (ifcfg, "SECURITYMODE", NULL, FALSE); if (auth_alg) { if (!strcmp (auth_alg, "shared")) svSetValue (ifcfg, "SECURITYMODE", "restricted", FALSE); else if (!strcmp (auth_alg, "open")) svSetValue (ifcfg, "SECURITYMODE", "open", FALSE); else if (!strcmp (auth_alg, "leap")) { svSetValue (ifcfg, "SECURITYMODE", "leap", FALSE); svSetValue (ifcfg, "IEEE_8021X_USERNAME", nm_setting_wireless_security_get_leap_username (s_wsec), FALSE); set_secret (ifcfg, "IEEE_8021X_PASSWORD", nm_setting_wireless_security_get_leap_password (s_wsec)); *no_8021x = TRUE; } } if (wep) { /* Default WEP TX key index */ tmp = g_strdup_printf ("%d", nm_setting_wireless_security_get_wep_tx_keyidx (s_wsec) + 1); svSetValue (ifcfg, "DEFAULTKEY", tmp, FALSE); g_free (tmp); } /* WEP keys */ set_secret (ifcfg, "KEY", NULL); /* Clear any default key */ for (i = 0; i < 4; i++) { key = nm_setting_wireless_security_get_wep_key (s_wsec, i); tmp = g_strdup_printf ("KEY%d", i + 1); set_secret (ifcfg, tmp, (wep && key) ? key : NULL); g_free (tmp); } /* WPA protos */ svSetValue (ifcfg, "WPA_ALLOW_WPA", NULL, FALSE); svSetValue (ifcfg, "WPA_ALLOW_WPA2", NULL, FALSE); num = nm_setting_wireless_security_get_num_protos (s_wsec); for (i = 0; i < num; i++) { proto = nm_setting_wireless_security_get_proto (s_wsec, i); if (proto && !strcmp (proto, "wpa")) svSetValue (ifcfg, "WPA_ALLOW_WPA", "yes", FALSE); else if (proto && !strcmp (proto, "rsn")) svSetValue (ifcfg, "WPA_ALLOW_WPA2", "yes", FALSE); } /* WPA Pairwise ciphers */ svSetValue (ifcfg, "CIPHER_PAIRWISE", NULL, FALSE); str = g_string_new (NULL); num = nm_setting_wireless_security_get_num_pairwise (s_wsec); for (i = 0; i < num; i++) { if (i > 0) g_string_append_c (str, ' '); cipher = nm_setting_wireless_security_get_pairwise (s_wsec, i); tmp = g_ascii_strup (cipher, -1); g_string_append (str, tmp); g_free (tmp); } if (strlen (str->str)) svSetValue (ifcfg, "CIPHER_PAIRWISE", str->str, FALSE); g_string_free (str, TRUE); /* WPA Group ciphers */ svSetValue (ifcfg, "CIPHER_GROUP", NULL, FALSE); str = g_string_new (NULL); num = nm_setting_wireless_security_get_num_groups (s_wsec); for (i = 0; i < num; i++) { if (i > 0) g_string_append_c (str, ' '); cipher = nm_setting_wireless_security_get_group (s_wsec, i); tmp = g_ascii_strup (cipher, -1); g_string_append (str, tmp); g_free (tmp); } if (strlen (str->str)) svSetValue (ifcfg, "CIPHER_GROUP", str->str, FALSE); g_string_free (str, TRUE); /* WPA Passphrase */ psk = nm_setting_wireless_security_get_psk (s_wsec); set_secret (ifcfg, "WPA_PSK", (wpa && psk) ? psk : NULL); return TRUE; } static gboolean write_wireless_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *no_8021x, GError **error) { NMSettingWireless *s_wireless; char *tmp, *tmp2; const GByteArray *ssid, *mac, *bssid; const char *mode; char buf[33]; guint32 mtu, chan, i; gboolean adhoc = FALSE, hex_ssid = FALSE; s_wireless = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS); if (!s_wireless) { g_set_error (error, ifcfg_plugin_error_quark (), 0, "Missing '%s' setting", NM_SETTING_WIRELESS_SETTING_NAME); return FALSE; } svSetValue (ifcfg, "HWADDR", NULL, FALSE); mac = nm_setting_wireless_get_mac_address (s_wireless); if (mac) { tmp = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X", mac->data[0], mac->data[1], mac->data[2], mac->data[3], mac->data[4], mac->data[5]); svSetValue (ifcfg, "HWADDR", tmp, FALSE); g_free (tmp); } svSetValue (ifcfg, "MTU", NULL, FALSE); mtu = nm_setting_wireless_get_mtu (s_wireless); if (mtu) { tmp = g_strdup_printf ("%u", mtu); svSetValue (ifcfg, "MTU", tmp, FALSE); g_free (tmp); } ssid = nm_setting_wireless_get_ssid (s_wireless); if (!ssid) { g_set_error (error, ifcfg_plugin_error_quark (), 0, "Missing SSID in '%s' setting", NM_SETTING_WIRELESS_SETTING_NAME); return FALSE; } if (!ssid->len || ssid->len > 32) { g_set_error (error, ifcfg_plugin_error_quark (), 0, "Invalid SSID in '%s' setting", NM_SETTING_WIRELESS_SETTING_NAME); return FALSE; } /* If the SSID contains any non-printable characters, we need to use the * hex notation of the SSID instead. */ for (i = 0; i < ssid->len; i++) { if (!isprint (ssid->data[i])) { hex_ssid = TRUE; break; } } if (hex_ssid) { GString *str; /* Hex SSIDs don't get quoted */ str = g_string_sized_new (ssid->len * 2 + 3); g_string_append (str, "0x"); for (i = 0; i < ssid->len; i++) g_string_append_printf (str, "%02X", ssid->data[i]); svSetValue (ifcfg, "ESSID", str->str, TRUE); g_string_free (str, TRUE); } else { /* Printable SSIDs get quoted */ memset (buf, 0, sizeof (buf)); memcpy (buf, ssid->data, ssid->len); tmp2 = svEscape (buf); tmp = g_strdup_printf ("\"%s\"", tmp2); svSetValue (ifcfg, "ESSID", tmp, TRUE); g_free (tmp2); g_free (tmp); } mode = nm_setting_wireless_get_mode (s_wireless); if (!mode || !strcmp (mode, "infrastructure")) { svSetValue (ifcfg, "MODE", "Managed", FALSE); } else if (!strcmp (mode, "adhoc")) { svSetValue (ifcfg, "MODE", "Ad-Hoc", FALSE); adhoc = TRUE; } else { g_set_error (error, ifcfg_plugin_error_quark (), 0, "Invalid mode '%s' in '%s' setting", mode, NM_SETTING_WIRELESS_SETTING_NAME); return FALSE; } svSetValue (ifcfg, "CHANNEL", NULL, FALSE); chan = nm_setting_wireless_get_channel (s_wireless); if (chan) { tmp = g_strdup_printf ("%u", chan); svSetValue (ifcfg, "CHANNEL", tmp, FALSE); g_free (tmp); } svSetValue (ifcfg, "BSSID", NULL, FALSE); bssid = nm_setting_wireless_get_bssid (s_wireless); if (bssid) { tmp = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X", bssid->data[0], bssid->data[1], bssid->data[2], bssid->data[3], bssid->data[4], bssid->data[5]); svSetValue (ifcfg, "BSSID", tmp, FALSE); g_free (tmp); } if (nm_setting_wireless_get_security (s_wireless)) { if (!write_wireless_security_setting (connection, ifcfg, adhoc, no_8021x, error)) return FALSE; } svSetValue (ifcfg, "TYPE", TYPE_WIRELESS, FALSE); return TRUE; } static gboolean write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) { NMSettingWired *s_wired; const GByteArray *mac; char *tmp; guint32 mtu; s_wired = (NMSettingWired *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED); if (!s_wired) { g_set_error (error, ifcfg_plugin_error_quark (), 0, "Missing '%s' setting", NM_SETTING_WIRED_SETTING_NAME); return FALSE; } mac = nm_setting_wired_get_mac_address (s_wired); if (mac) { tmp = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X", mac->data[0], mac->data[1], mac->data[2], mac->data[3], mac->data[4], mac->data[5]); svSetValue (ifcfg, "HWADDR", tmp, FALSE); g_free (tmp); } mtu = nm_setting_wired_get_mtu (s_wired); if (mtu) { tmp = g_strdup_printf ("%u", mtu); svSetValue (ifcfg, "MTU", tmp, FALSE); g_free (tmp); } svSetValue (ifcfg, "TYPE", TYPE_ETHERNET, FALSE); return TRUE; } static void write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) { char *tmp; svSetValue (ifcfg, "NAME", nm_setting_connection_get_id (s_con), FALSE); svSetValue (ifcfg, "UUID", nm_setting_connection_get_uuid (s_con), FALSE); svSetValue (ifcfg, "ONBOOT", nm_setting_connection_get_autoconnect (s_con) ? "yes" : "no", FALSE); svSetValue (ifcfg, "LAST_CONNECT", NULL, FALSE); if (nm_setting_connection_get_timestamp (s_con)) { tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, nm_setting_connection_get_timestamp (s_con)); svSetValue (ifcfg, "LAST_CONNECT", tmp, FALSE); g_free (tmp); } } static gboolean write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) { NMSettingIP4Config *s_ip4; const char *value; char *addr_key, *prefix_key, *gw_key, *tmp; guint32 i, num; GString *searches; s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); if (!s_ip4) { g_set_error (error, ifcfg_plugin_error_quark (), 0, "Missing '%s' setting", NM_SETTING_IP4_CONFIG_SETTING_NAME); return FALSE; } value = nm_setting_ip4_config_get_method (s_ip4); g_assert (value); if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) svSetValue (ifcfg, "BOOTPROTO", "dhcp", FALSE); else if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) svSetValue (ifcfg, "BOOTPROTO", "none", FALSE); else if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) svSetValue (ifcfg, "BOOTPROTO", "autoip", FALSE); else if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) svSetValue (ifcfg, "BOOTPROTO", "shared", FALSE); num = nm_setting_ip4_config_get_num_addresses (s_ip4); for (i = 0; i < 254; i++) { char buf[INET_ADDRSTRLEN + 1]; NMIP4Address *addr; guint32 ip; if (i == 0) { addr_key = g_strdup ("IPADDR"); prefix_key = g_strdup ("PREFIX"); gw_key = g_strdup ("GATEWAY"); } else { addr_key = g_strdup_printf ("IPADDR%d", i + 1); prefix_key = g_strdup_printf ("PREFIX%d", i + 1); gw_key = g_strdup_printf ("GATEWAY%d", i + 1); } if (i >= num) { svSetValue (ifcfg, addr_key, NULL, FALSE); svSetValue (ifcfg, prefix_key, NULL, FALSE); svSetValue (ifcfg, gw_key, NULL, FALSE); } else { addr = nm_setting_ip4_config_get_address (s_ip4, i); memset (buf, 0, sizeof (buf)); ip = nm_ip4_address_get_address (addr); inet_ntop (AF_INET, (const void *) &ip, &buf[0], sizeof (buf)); svSetValue (ifcfg, addr_key, &buf[0], FALSE); tmp = g_strdup_printf ("%u", nm_ip4_address_get_prefix (addr)); svSetValue (ifcfg, prefix_key, tmp, FALSE); g_free (tmp); if (nm_ip4_address_get_gateway (addr)) { memset (buf, 0, sizeof (buf)); ip = nm_ip4_address_get_gateway (addr); inet_ntop (AF_INET, (const void *) &ip, &buf[0], sizeof (buf)); svSetValue (ifcfg, gw_key, &buf[0], FALSE); } else svSetValue (ifcfg, gw_key, NULL, FALSE); } g_free (addr_key); g_free (prefix_key); g_free (gw_key); } num = nm_setting_ip4_config_get_num_dns (s_ip4); for (i = 0; i < 254; i++) { char buf[INET_ADDRSTRLEN + 1]; guint32 ip; addr_key = g_strdup_printf ("DNS%d", i + 1); if (i >= num) svSetValue (ifcfg, addr_key, NULL, FALSE); else { ip = nm_setting_ip4_config_get_dns (s_ip4, i); memset (buf, 0, sizeof (buf)); inet_ntop (AF_INET, (const void *) &ip, &buf[0], sizeof (buf)); svSetValue (ifcfg, addr_key, &buf[0], FALSE); } g_free (addr_key); } num = nm_setting_ip4_config_get_num_dns_searches (s_ip4); if (num > 0) { searches = g_string_new (NULL); for (i = 0; i < num; i++) { if (i > 0) g_string_append_c (searches, ' '); g_string_append (searches, nm_setting_ip4_config_get_dns_search (s_ip4, i)); } svSetValue (ifcfg, "DOMAIN", searches->str, FALSE); g_string_free (searches, TRUE); } else svSetValue (ifcfg, "DOMAIN", NULL, FALSE); svSetValue (ifcfg, "PEERDNS", NULL, FALSE); svSetValue (ifcfg, "PEERROUTES", NULL, FALSE); svSetValue (ifcfg, "DHCP_HOSTNAME", NULL, FALSE); svSetValue (ifcfg, "DHCP_CLIENT_ID", NULL, FALSE); if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { svSetValue (ifcfg, "PEERDNS", nm_setting_ip4_config_get_ignore_auto_dns (s_ip4) ? "no" : "yes", FALSE); svSetValue (ifcfg, "PEERROUTES", nm_setting_ip4_config_get_ignore_auto_routes (s_ip4) ? "no" : "yes", FALSE); value = nm_setting_ip4_config_get_dhcp_hostname (s_ip4); if (value) svSetValue (ifcfg, "DHCP_HOSTNAME", value, FALSE); value = nm_setting_ip4_config_get_dhcp_client_id (s_ip4); if (value) svSetValue (ifcfg, "DHCP_CLIENT_ID", value, FALSE); } return TRUE; } static char * escape_id (const char *id) { char *escaped = g_strdup (id); char *p = escaped; /* Escape random stuff */ while (*p) { if (*p == ' ') *p = '_'; else if (*p == '/') *p = '-'; else if (*p == '\\') *p = '-'; p++; } return escaped; } static gboolean write_connection (NMConnection *connection, const char *ifcfg_dir, const char *filename, const char *keyfile, char **out_filename, GError **error) { NMSettingConnection *s_con; gboolean success = FALSE; shvarFile *ifcfg = NULL; char *ifcfg_name = NULL; const char *type; gboolean no_8021x = FALSE; gboolean wired = FALSE; s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); if (!s_con) { g_set_error (error, ifcfg_plugin_error_quark (), 0, "Missing '%s' setting", NM_SETTING_CONNECTION_SETTING_NAME); return FALSE; } if (filename) { /* For existing connections, 'filename' should be full path to ifcfg file */ ifcfg = svNewFile (filename); ifcfg_name = g_strdup (filename); } else { char *escaped; escaped = escape_id (nm_setting_connection_get_id (s_con)); ifcfg_name = g_strdup_printf ("%s/ifcfg-%s", ifcfg_dir, escaped); ifcfg = svCreateFile (ifcfg_name); g_free (escaped); } if (!ifcfg) { g_set_error (error, ifcfg_plugin_error_quark (), 0, "Failed to open/create ifcfg file '%s'", ifcfg_name); goto out; } type = nm_setting_connection_get_connection_type (s_con); if (!type) { g_set_error (error, ifcfg_plugin_error_quark (), 0, "Missing connection type!"); goto out; } if (!strcmp (type, NM_SETTING_WIRED_SETTING_NAME)) { // FIXME: can't write PPPoE at this time if (nm_connection_get_setting (connection, NM_TYPE_SETTING_PPPOE)) { g_set_error (error, ifcfg_plugin_error_quark (), 0, "Can't write connection type '%s'", NM_SETTING_PPPOE_SETTING_NAME); goto out; } if (!write_wired_setting (connection, ifcfg, error)) goto out; wired = TRUE; } else if (!strcmp (type, NM_SETTING_WIRELESS_SETTING_NAME)) { if (!write_wireless_setting (connection, ifcfg, &no_8021x, error)) goto out; } else { g_set_error (error, ifcfg_plugin_error_quark (), 0, "Can't write connection type '%s'", type); goto out; } if (!no_8021x) { if (!write_8021x_setting (connection, ifcfg, wired, error)) goto out; } if (!write_ip4_setting (connection, ifcfg, error)) goto out; write_connection_setting (s_con, ifcfg); if (svWriteFile (ifcfg, 0644)) { g_set_error (error, ifcfg_plugin_error_quark (), 0, "Can't write connection '%s'", ifcfg->fileName); goto out; } /* Only return the filename if this was a newly written ifcfg */ if (out_filename && !filename) *out_filename = g_strdup (ifcfg_name); success = TRUE; out: if (ifcfg) svCloseFile (ifcfg); g_free (ifcfg_name); return success; } gboolean writer_new_connection (NMConnection *connection, const char *ifcfg_dir, char **out_filename, GError **error) { return write_connection (connection, ifcfg_dir, NULL, NULL, out_filename, error); } gboolean writer_update_connection (NMConnection *connection, const char *ifcfg_dir, const char *filename, const char *keyfile, GError **error) { return write_connection (connection, ifcfg_dir, filename, keyfile, NULL, error); }