// SPDX-License-Identifier: GPL-2.0+ /* NetworkManager Applet -- allow user control over networking * * Dan Williams * * Copyright 2007 - 2015 Red Hat, Inc. */ #include "nm-default.h" #include "utils.h" #include #include #include #include #include "nm-utils.h" #include "nm-utils/nm-shared-utils.h" /* * utils_ether_addr_valid * * Compares an Ethernet address against known invalid addresses. * */ gboolean utils_ether_addr_valid (const struct ether_addr *test_addr) { guint8 invalid_addr1[ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; guint8 invalid_addr2[ETH_ALEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; guint8 invalid_addr3[ETH_ALEN] = {0x44, 0x44, 0x44, 0x44, 0x44, 0x44}; guint8 invalid_addr4[ETH_ALEN] = {0x00, 0x30, 0xb4, 0x00, 0x00, 0x00}; /* prism54 dummy MAC */ g_return_val_if_fail (test_addr != NULL, FALSE); /* Compare the AP address the card has with invalid ethernet MAC addresses. */ if (!memcmp (test_addr->ether_addr_octet, &invalid_addr1, ETH_ALEN)) return FALSE; if (!memcmp (test_addr->ether_addr_octet, &invalid_addr2, ETH_ALEN)) return FALSE; if (!memcmp (test_addr->ether_addr_octet, &invalid_addr3, ETH_ALEN)) return FALSE; if (!memcmp (test_addr->ether_addr_octet, &invalid_addr4, ETH_ALEN)) return FALSE; if (test_addr->ether_addr_octet[0] & 1) /* Multicast addresses */ return FALSE; return TRUE; } char * utils_hash_ap (GBytes *ssid, NM80211Mode mode, guint32 flags, guint32 wpa_flags, guint32 rsn_flags) { unsigned char input[66]; memset (&input[0], 0, sizeof (input)); if (ssid) memcpy (input, g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid)); if (mode == NM_802_11_MODE_INFRA) input[32] |= (1 << 0); else if (mode == NM_802_11_MODE_ADHOC) input[32] |= (1 << 1); else input[32] |= (1 << 2); /* Separate out no encryption, WEP-only, and WPA-capable */ if ( !(flags & NM_802_11_AP_FLAGS_PRIVACY) && (wpa_flags == NM_802_11_AP_SEC_NONE) && (rsn_flags == NM_802_11_AP_SEC_NONE)) input[32] |= (1 << 3); else if ( (flags & NM_802_11_AP_FLAGS_PRIVACY) && (wpa_flags == NM_802_11_AP_SEC_NONE) && (rsn_flags == NM_802_11_AP_SEC_NONE)) input[32] |= (1 << 4); else if ( !(flags & NM_802_11_AP_FLAGS_PRIVACY) && (wpa_flags != NM_802_11_AP_SEC_NONE) && (rsn_flags != NM_802_11_AP_SEC_NONE)) input[32] |= (1 << 5); else input[32] |= (1 << 6); /* duplicate it */ memcpy (&input[33], &input[0], 32); return g_compute_checksum_for_data (G_CHECKSUM_MD5, input, sizeof (input)); } typedef struct { const char *tag; const char *replacement; } Tag; static Tag escaped_tags[] = { { "
", NULL }, { "
", NULL }, { "

", "\n" }, { "

", NULL }, { "", "" }, { "", "" }, { "", "" }, { "", "" }, { "", "" }, { "", "" }, { "&", "&" }, { NULL, NULL } }; char * utils_escape_notify_message (const char *src) { const char *p = src; GString *escaped; /* Filter the source text and get rid of some HTML tags since the * notification spec only allows a subset of HTML. Substitute * HTML code for characters like & that are invalid in HTML. */ escaped = g_string_sized_new (strlen (src) + 5); while (*p) { Tag *t = &escaped_tags[0]; gboolean found = FALSE; while (t->tag) { if (strncasecmp (p, t->tag, strlen (t->tag)) == 0) { p += strlen (t->tag); if (t->replacement) g_string_append (escaped, t->replacement); found = TRUE; break; } t++; } if (!found) g_string_append_c (escaped, *p++); } return g_string_free (escaped, FALSE); } char * utils_create_mobile_connection_id (const char *provider, const char *plan_name) { g_return_val_if_fail (provider != NULL, NULL); if (plan_name) return g_strdup_printf ("%s %s", provider, plan_name); /* The %s is a mobile provider name, eg "T-Mobile" */ return g_strdup_printf (_("%s connection"), provider); } void utils_show_error_dialog (const char *title, const char *text1, const char *text2, gboolean modal, GtkWindow *parent) { GtkWidget *err_dialog; g_return_if_fail (text1 != NULL); err_dialog = gtk_message_dialog_new (parent, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", text1); gtk_window_set_position (GTK_WINDOW (err_dialog), GTK_WIN_POS_CENTER_ALWAYS); if (text2) gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (err_dialog), "%s", text2); if (title) gtk_window_set_title (GTK_WINDOW (err_dialog), title); if (modal) { gtk_dialog_run (GTK_DIALOG (err_dialog)); gtk_widget_destroy (err_dialog); } else { g_signal_connect (err_dialog, "delete-event", G_CALLBACK (gtk_widget_destroy), NULL); g_signal_connect (err_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL); gtk_widget_show (err_dialog); gtk_window_present (GTK_WINDOW (err_dialog)); } } gboolean utils_char_is_ascii_print (char character) { return g_ascii_isprint (character); } gboolean utils_char_is_ascii_digit (char character) { return g_ascii_isdigit (character); } gboolean utils_char_is_ascii_ip4_address (char character) { return g_ascii_isdigit (character) || character == '.'; } gboolean utils_char_is_ascii_ip6_address (char character) { return g_ascii_isxdigit (character) || character == ':'; } gboolean utils_char_is_ascii_apn (char character) { return g_ascii_isalnum (character) || character == '.' || character == '_' || character == '-'; } /** * Filters the characters from a text that was just input into GtkEditable. * Returns FALSE, if after filtering no characters were left. TRUE means, * that valid characters were added and the content of the GtkEditable changed. **/ gboolean utils_filter_editable_on_insert_text (GtkEditable *editable, const gchar *text, gint length, gint *position, void *user_data, UtilsFilterGtkEditableFunc validate_character, gpointer block_func) { int i, count = 0; gchar *result = g_new (gchar, length+1); for (i = 0; i < length; i++) { if (validate_character (text[i])) result[count++] = text[i]; } result[count] = 0; if (count > 0) { if (block_func) { g_signal_handlers_block_by_func (G_OBJECT (editable), G_CALLBACK (block_func), user_data); } gtk_editable_insert_text (editable, result, count, position); if (block_func) { g_signal_handlers_unblock_by_func (G_OBJECT (editable), G_CALLBACK (block_func), user_data); } } g_signal_stop_emission_by_name (G_OBJECT (editable), "insert-text"); g_free (result); return count > 0; } /** * utils_override_bg_color: * * The function can be used to set background color for a widget. * There are functions for that in Gtk2 [1] and Gtk3 [2]. Unfortunately, they * have been deprecated, and moreover gtk_widget_override_background_color() * stopped working at some point for some Gtk themes, including the default * Adwaita theme. * [1] gtk_widget_modify_bg() or gtk_widget_modify_base() * [2] gtk_widget_override_background_color() * * Related links: * https://bugzilla.gnome.org/show_bug.cgi?id=656461 * https://mail.gnome.org/archives/gtk-list/2015-February/msg00053.html */ void utils_override_bg_color (GtkWidget *widget, GdkRGBA *rgba) { GtkCssProvider *provider; char *css; provider = (GtkCssProvider *) g_object_get_data (G_OBJECT (widget), "our-css-provider"); if (G_UNLIKELY (!provider)) { provider = gtk_css_provider_new (); gtk_style_context_add_provider (gtk_widget_get_style_context (widget), GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); g_object_set_data_full (G_OBJECT (widget), "our-css-provider", provider, (GDestroyNotify) g_object_unref); } if (rgba) { css = g_strdup_printf ("* { background-color: %s; background-image: none; }", gdk_rgba_to_string (rgba)); #if GTK_CHECK_VERSION(3,90,0) gtk_css_provider_load_from_data (provider, css, -1); #else gtk_css_provider_load_from_data (provider, css, -1, NULL); #endif g_free (css); } else { #if GTK_CHECK_VERSION(3,90,0) gtk_css_provider_load_from_data (provider, "", -1); #else gtk_css_provider_load_from_data (provider, "", -1, NULL); #endif } } void utils_set_cell_background (GtkCellRenderer *cell, const char *color, const char *value) { if (color) { if (!value || !*value) { g_object_set (G_OBJECT (cell), "cell-background-set", TRUE, "cell-background", color, NULL); } else { char *markup; markup = g_markup_printf_escaped ("%s", color, value); g_object_set (G_OBJECT (cell), "markup", markup, NULL); g_free (markup); g_object_set (G_OBJECT (cell), "cell-background-set", FALSE, NULL); } } else g_object_set (G_OBJECT (cell), "cell-background-set", FALSE, NULL); } void widget_set_error (GtkWidget *widget) { g_return_if_fail (GTK_IS_WIDGET (widget)); gtk_style_context_add_class (gtk_widget_get_style_context (widget), "error"); } void widget_unset_error (GtkWidget *widget) { g_return_if_fail (GTK_IS_WIDGET (widget)); gtk_style_context_remove_class (gtk_widget_get_style_context (widget), "error"); } gboolean utils_tree_model_get_int64 (GtkTreeModel *model, GtkTreeIter *iter, int column, gint64 min_value, gint64 max_value, gboolean fail_if_missing, gint64 *out, char **out_raw) { char *item = NULL; gboolean success = FALSE; gint64 val; g_return_val_if_fail (model, FALSE); g_return_val_if_fail (iter, FALSE); gtk_tree_model_get (model, iter, column, &item, -1); if (out_raw) *out_raw = item; if (!item || !strlen (item)) { if (!out_raw) g_free (item); return fail_if_missing ? FALSE : TRUE; } val = _nm_utils_ascii_str_to_int64 (item, 10, min_value, max_value, 0); if (errno) goto out; *out = val; success = TRUE; out: if (!out_raw) g_free (item); return success; } gboolean utils_tree_model_get_address (GtkTreeModel *model, GtkTreeIter *iter, int column, int family, gboolean fail_if_missing, char **out, char **out_raw) { char *item = NULL; union { struct in_addr addr4; struct in6_addr addr6; } tmp_addr; g_return_val_if_fail (model, FALSE); g_return_val_if_fail (iter, FALSE); g_return_val_if_fail (family == AF_INET || family == AF_INET6, FALSE); gtk_tree_model_get (model, iter, column, &item, -1); if (out_raw) *out_raw = item; if (!item || !strlen (item)) { if (!out_raw) g_free (item); return fail_if_missing ? FALSE : TRUE; } if (inet_pton (family, item, &tmp_addr) == 0) return FALSE; if ( (family == AF_INET && tmp_addr.addr4.s_addr == 0) || (family == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED (&tmp_addr.addr6))) { if (!out_raw) g_free (item); return fail_if_missing ? FALSE : TRUE; } *out = item; return TRUE; } gboolean utils_tree_model_get_ip4_prefix (GtkTreeModel *model, GtkTreeIter *iter, int column, gboolean fail_if_missing, guint32 *out, char **out_raw) { char *item = NULL; struct in_addr tmp_addr = { 0 }; gboolean success = FALSE; glong tmp_prefix; g_return_val_if_fail (model, FALSE); g_return_val_if_fail (iter, FALSE); gtk_tree_model_get (model, iter, column, &item, -1); if (out_raw) *out_raw = item; if (!item || !strlen (item)) { if (!out_raw) g_free (item); return fail_if_missing ? FALSE : TRUE; } errno = 0; /* Is it a prefix? */ if (!strchr (item, '.')) { tmp_prefix = strtol (item, NULL, 10); if (!errno && tmp_prefix >= 0 && tmp_prefix <= 32) { *out = tmp_prefix; success = TRUE; goto out; } } /* Is it a netmask? */ if (inet_pton (AF_INET, item, &tmp_addr) > 0) { *out = nm_utils_ip4_netmask_to_prefix (tmp_addr.s_addr); success = TRUE; } out: if (!out_raw) g_free (item); return success; } static gboolean file_has_extension (const char *filename, const char *const*extensions) { const char *p; gs_free char *ext = NULL; if (!filename) return FALSE; p = strrchr (filename, '.'); if (!p) return FALSE; ext = g_ascii_strdown (p, -1); return g_strv_contains (extensions, ext); } static gboolean cert_filter (const GtkFileFilterInfo *filter_info, gpointer data) { static const char *const extensions[] = { ".der", ".pem", ".crt", ".cer", ".p12", NULL }; return file_has_extension (filter_info->filename, extensions); } static gboolean privkey_filter (const GtkFileFilterInfo *filter_info, gpointer user_data) { static const char *const extensions[] = { ".der", ".pem", ".p12", ".key", NULL }; return file_has_extension (filter_info->filename, extensions); } GtkFileFilter * utils_cert_filter (void) { GtkFileFilter *filter; filter = gtk_file_filter_new (); gtk_file_filter_add_custom (filter, GTK_FILE_FILTER_FILENAME, cert_filter, NULL, NULL); gtk_file_filter_set_name (filter, _("PEM certificates (*.pem, *.crt, *.cer)")); return filter; } GtkFileFilter * utils_key_filter (void) { GtkFileFilter *filter; filter = gtk_file_filter_new (); gtk_file_filter_add_custom (filter, GTK_FILE_FILTER_FILENAME, privkey_filter, NULL, NULL); gtk_file_filter_set_name (filter, _("DER, PEM, or PKCS#12 private keys (*.der, *.pem, *.p12, *.key)")); return filter; }