diff options
| author | Michael Biebl <biebl@debian.org> | 2020-06-28 18:58:14 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2020-06-28 18:58:14 +0200 |
| commit | a54ac63bbf9b2c71026ac9028a8ffaf186cf3c82 (patch) | |
| tree | 6a32883bd916c4096357b35298beff6db8bcd0b5 /shared | |
| parent | 45e8e1149027529194982212c804c0468aa01d98 (diff) | |
New upstream version 1.25.90 upstream/1.25.90
Diffstat (limited to 'shared')
76 files changed, 1903 insertions, 7352 deletions
diff --git a/shared/meson.build b/shared/meson.build index 9a8768f5..48880ec4 100644 --- a/shared/meson.build +++ b/shared/meson.build @@ -107,27 +107,12 @@ nm_version_macro_header = configure_file( configuration: data_conf, ) -nm_ethtool_utils_source = files('nm-libnm-core-intern/nm-ethtool-utils.c') - -nm_libnm_core_aux_source = files('nm-libnm-core-aux/nm-libnm-core-aux.c') - -nm_libnm_core_utils_source = files('nm-libnm-core-intern/nm-libnm-core-utils.c') - -nm_keyfile_source = files( - 'nm-keyfile/nm-keyfile-utils.c', - 'nm-keyfile/nm-keyfile.c', -) - -nm_libnm_aux_source = files('nm-libnm-aux/nm-libnm-aux.c') - nm_meta_setting_source = files('nm-meta-setting.c') nm_test_utils_impl_source = files('nm-test-utils-impl.c') nm_vpn_plugin_utils_source = files('nm-utils/nm-vpn-plugin-utils.c') -nm_auth_subject_source = files('nm-libnm-core-intern/nm-auth-subject.c') - c_flags = [ '-DG_LOG_DOMAIN="@0@"'.format(libnm_name), '-DNETWORKMANAGER_COMPILATION=0', diff --git a/shared/n-acd/src/n-acd.c b/shared/n-acd/src/n-acd.c index d434d1d1..af3328c1 100644 --- a/shared/n-acd/src/n-acd.c +++ b/shared/n-acd/src/n-acd.c @@ -328,6 +328,7 @@ int n_acd_ensure_bpf_map_space(NAcd *acd) { _c_public_ int n_acd_new(NAcd **acdp, NAcdConfig *config) { _c_cleanup_(n_acd_unrefp) NAcd *acd = NULL; _c_cleanup_(c_closep) int fd_bpf_prog = -1; + struct epoll_event eevent; int r; if (config->ifindex <= 0 || @@ -370,19 +371,19 @@ _c_public_ int n_acd_new(NAcd **acdp, NAcdConfig *config) { if (r) return r; - r = epoll_ctl(acd->fd_epoll, EPOLL_CTL_ADD, acd->timer.fd, - &(struct epoll_event){ - .events = EPOLLIN, - .data.u32 = N_ACD_EPOLL_TIMER, - }); + eevent = (struct epoll_event){ + .events = EPOLLIN, + .data.u32 = N_ACD_EPOLL_TIMER, + }; + r = epoll_ctl(acd->fd_epoll, EPOLL_CTL_ADD, acd->timer.fd, &eevent); if (r < 0) return -c_errno(); - r = epoll_ctl(acd->fd_epoll, EPOLL_CTL_ADD, acd->fd_socket, - &(struct epoll_event){ - .events = EPOLLIN, - .data.u32 = N_ACD_EPOLL_SOCKET, - }); + eevent = (struct epoll_event){ + .events = EPOLLIN, + .data.u32 = N_ACD_EPOLL_SOCKET, + }; + r = epoll_ctl(acd->fd_epoll, EPOLL_CTL_ADD, acd->fd_socket, &eevent); if (r < 0) return -c_errno(); diff --git a/shared/n-dhcp4/src/n-dhcp4-c-connection.c b/shared/n-dhcp4/src/n-dhcp4-c-connection.c index 30514e28..8c32a984 100644 --- a/shared/n-dhcp4/src/n-dhcp4-c-connection.c +++ b/shared/n-dhcp4/src/n-dhcp4-c-connection.c @@ -23,6 +23,7 @@ * @connection: connection to operate on * @client_config: client configuration to use * @probe_config: client probe configuration to use + * @log_queue: the log queue for logging events * @fd_epoll: epoll context to attach to, or -1 * * This initializes a new client connection using the configuration given in @@ -47,11 +48,13 @@ int n_dhcp4_c_connection_init(NDhcp4CConnection *connection, NDhcp4ClientConfig *client_config, NDhcp4ClientProbeConfig *probe_config, + NDhcp4LogQueue *log_queue, int fd_epoll) { *connection = (NDhcp4CConnection)N_DHCP4_C_CONNECTION_NULL(*connection); connection->client_config = client_config; connection->probe_config = probe_config; connection->fd_epoll = fd_epoll; + connection->log_queue = log_queue; /* * We explicitly allow initializing connections with an invalid @@ -1066,25 +1069,27 @@ static int n_dhcp4_c_connection_send_request(NDhcp4CConnection *connection, } if (request->userdata.client_addr == INADDR_ANY) { - n_dhcp4_c_log(connection->client_config, LOG_INFO, - "send %s to %s%s", - message_type_to_str(request->userdata.message_type), - broadcast ? - "255.255.255.255" : - inet_ntop(AF_INET, &connection->server_ip, - server_addr, sizeof(server_addr)), - error_msg); + n_dhcp4_log(connection->log_queue, + LOG_INFO, + "send %s to %s%s", + message_type_to_str(request->userdata.message_type), + broadcast ? + "255.255.255.255" : + inet_ntop(AF_INET, &connection->server_ip, + server_addr, sizeof(server_addr)), + error_msg); } else { - n_dhcp4_c_log(connection->client_config, LOG_INFO, - "send %s of %s to %s%s", - message_type_to_str(request->userdata.message_type), - inet_ntop(AF_INET, &request->userdata.client_addr, - client_addr, sizeof(client_addr)), - broadcast ? - "255.255.255.255" : - inet_ntop(AF_INET, &connection->server_ip, - server_addr, sizeof(server_addr)), - error_msg); + n_dhcp4_log(connection->log_queue, + LOG_INFO, + "send %s of %s to %s%s", + message_type_to_str(request->userdata.message_type), + inet_ntop(AF_INET, &request->userdata.client_addr, + client_addr, sizeof(client_addr)), + broadcast ? + "255.255.255.255" : + inet_ntop(AF_INET, &connection->server_ip, + server_addr, sizeof(server_addr)), + error_msg); } ++request->userdata.n_send; @@ -1207,19 +1212,21 @@ int n_dhcp4_c_connection_dispatch_io(NDhcp4CConnection *connection, return N_DHCP4_E_AGAIN; if (type == N_DHCP4_MESSAGE_OFFER || type == N_DHCP4_MESSAGE_ACK) { - n_dhcp4_c_log(connection->client_config, LOG_INFO, - "received %s of %s from %s", - message_type_to_str(type), - inet_ntop(AF_INET, &message->message.header.yiaddr, - client_addr, sizeof(client_addr)), - inet_ntop(AF_INET, &message->message.header.siaddr, - serv_addr, sizeof(serv_addr))); + n_dhcp4_log(connection->log_queue, + LOG_INFO, + "received %s of %s from %s", + message_type_to_str(type), + inet_ntop(AF_INET, &message->message.header.yiaddr, + client_addr, sizeof(client_addr)), + inet_ntop(AF_INET, &message->message.header.siaddr, + serv_addr, sizeof(serv_addr))); } else { - n_dhcp4_c_log(connection->client_config, LOG_INFO, - "received %s from %s", - message_type_to_str(type), - inet_ntop(AF_INET, &message->message.header.siaddr, - serv_addr, sizeof(serv_addr))); + n_dhcp4_log(connection->log_queue, + LOG_INFO, + "received %s from %s", + message_type_to_str(type), + inet_ntop(AF_INET, &message->message.header.siaddr, + serv_addr, sizeof(serv_addr))); } switch (type) { diff --git a/shared/n-dhcp4/src/n-dhcp4-c-probe.c b/shared/n-dhcp4/src/n-dhcp4-c-probe.c index 5e971298..f3d4f265 100644 --- a/shared/n-dhcp4/src/n-dhcp4-c-probe.c +++ b/shared/n-dhcp4/src/n-dhcp4-c-probe.c @@ -429,9 +429,13 @@ int n_dhcp4_client_probe_new(NDhcp4ClientProbe **probep, */ n_dhcp4_client_probe_config_initialize_random_seed(probe->config); + /* The new probe keeps a reference on @client. So we are sure that &client->log_queue + * stays alive as long as we need it. */ + r = n_dhcp4_c_connection_init(&probe->connection, client->config, probe->config, + &client->log_queue, active ? client->fd_epoll : -1); if (r) return r; @@ -1091,7 +1095,7 @@ int n_dhcp4_client_probe_transition_accept(NDhcp4ClientProbe *probe, NDhcp4Incom probe->state = N_DHCP4_CLIENT_PROBE_STATE_BOUND; - n_dhcp4_client_arm_timer (probe->client); + n_dhcp4_client_arm_timer(probe->client); break; diff --git a/shared/n-dhcp4/src/n-dhcp4-client.c b/shared/n-dhcp4/src/n-dhcp4-client.c index 6b015e81..403a6932 100644 --- a/shared/n-dhcp4/src/n-dhcp4-client.c +++ b/shared/n-dhcp4/src/n-dhcp4-client.c @@ -94,9 +94,6 @@ int n_dhcp4_client_config_dup(NDhcp4ClientConfig *config, NDhcp4ClientConfig **d dup->n_mac = config->n_mac; memcpy(dup->broadcast_mac, config->broadcast_mac, sizeof(dup->broadcast_mac)); dup->n_broadcast_mac = config->n_broadcast_mac; - dup->log.level = config->log.level; - dup->log.func = config->log.func; - dup->log.data = config->log.data; r = n_dhcp4_client_config_set_client_id(dup, config->client_id, @@ -227,37 +224,64 @@ _c_public_ void n_dhcp4_client_config_set_broadcast_mac(NDhcp4ClientConfig *conf * n_dhcp4_client_config_set_client_id() - set client-id property * @config: client configuration to operate on * @id: client id - * @n_id: length of the client id in bytes + * @n_id: length of the client id in bytes. The length + * must be from 2 up to 255 bytes. Set it to 0 + * to unset the client-id. * * This sets the client-id property of @config. It copies the entire client-id * buffer into the configuration. + * See RFC 2132 (section 9.14) for the format of the Client Identifier. * * Return: 0 on success, negative error code on failure. */ _c_public_ int n_dhcp4_client_config_set_client_id(NDhcp4ClientConfig *config, const uint8_t *id, size_t n_id) { uint8_t *t; + if (n_id == 0) { + config->client_id = c_free(config->client_id); + config->n_client_id = 0; + return 0; + } + + if (n_id < 2 || n_id > 255) + return -EINVAL; + t = malloc(n_id + 1); if (!t) return -ENOMEM; + memcpy(t, id, n_id); + t[n_id] = 0; /* safety 0 for debugging */ + free(config->client_id); config->client_id = t; config->n_client_id = n_id; - - memcpy(config->client_id, id, n_id); - config->client_id[n_id] = 0; /* safety 0 for debugging */ - return 0; } -_c_public_ void n_dhcp4_client_config_set_log_level(NDhcp4ClientConfig *config, int level) { - config->log.level = level; -} - -_c_public_ void n_dhcp4_client_config_set_log_func(NDhcp4ClientConfig *config, NDhcp4LogFunc func, void *data) { - config->log.func = func; - config->log.data = data; +/** + * n_dhcp4_client_set_log_level() - set the logging level of the client + * @client: the client to operate on + * @level: the minimum syslog logging level that is + * still logged. For example, set to LOG_NOTICE + * to receive logging events with level LOG_NOTICE + * and higher. Set to -1 to disable generating + * logging events (which is also the default). + * + * By enabling logging, you can get N_DHCP4_CLIENT_EVENT_LOG events. + * + * From the logging event you may steal the message if (and only if) "allow_steal_message" + * is true. In that case, clear the message field and free the message yourself. + * + * If a logging event cannot be logged due to out of memory, one message + * gets logged that messages are missing. Until the event with that message + * gets dropped, no further logging events will be queued. + * + * You may change the logging level at any time, but it does not affect + * logging events that are already queued. + */ +_c_public_ void n_dhcp4_client_set_log_level(NDhcp4Client *client, int level) { + client->log_queue.log_level = level; } /** @@ -313,6 +337,16 @@ NDhcp4CEventNode *n_dhcp4_c_event_node_free(NDhcp4CEventNode *node) { case N_DHCP4_CLIENT_EVENT_EXTENDED: node->event.extended.lease = n_dhcp4_client_lease_unref(node->event.extended.lease); break; + case N_DHCP4_CLIENT_EVENT_LOG: + if (_c_unlikely_(!node->event.log.allow_steal_message)) { + /* @node is the static node "nomem_node". It must not be + * freed. */ + c_list_unlink(&node->client_link); + node->is_public = false; + return NULL; + } + node->event.log.message = c_free((char *)node->event.log.message); + break; default: break; } @@ -395,8 +429,11 @@ _c_public_ int n_dhcp4_client_new(NDhcp4Client **clientp, NDhcp4ClientConfig *co ev.data.u32 = N_DHCP4_CLIENT_EPOLL_TIMER; r = epoll_ctl(client->fd_epoll, EPOLL_CTL_ADD, client->fd_timer, &ev); - if (r < 0) + if (r < 0) { + close(client->fd_timer); + client->fd_timer = -1; return -errno; + } *clientp = client; client = NULL; @@ -487,6 +524,78 @@ int n_dhcp4_client_raise(NDhcp4Client *client, NDhcp4CEventNode **nodep, unsigne } /** + * n_dhcp4_log_queue_fmt() - add a logging event. + * @client: the NDhcp4LogQueue to operate on + * @level: the syslog logging level + * @fmt: the format string for the message + * @... printf arguments for logging + * + * Appends a logging event to the event queue if logging is + * enabled and the logging level sufficiently high. + * + * Queuing a logging event might fail with out of memory. + * In that case, a static event will be queued that informs + * about lost messages. + */ +void n_dhcp4_log_queue_fmt(NDhcp4LogQueue *log_queue, + int level, + const char *fmt, + ...) { + NDhcp4CEventNode *node; + char *message; + va_list ap; + int r; + + if (level > log_queue->log_level) + return; + + /* Currently the logging queue is only implemented for + * the client. Nobody would enable logging except a + * client instance. */ + c_assert(log_queue->is_client); + + if (!c_list_is_empty (&log_queue->nomem_node.client_link)) { + /* we have the nomem_node queued after a recent out + * of memory. This disables all logging messages until + * the event gets popped. + * + * The reason is that we can only queue the nomem_node once, + * so if we now try to append another event and succeed, the + * user wouldn't know which messages got dropped. Instead, + * just drop them all!! */ + return; + } + + r = n_dhcp4_c_event_node_new(&node); + if (r < 0) + goto handle_nomem; + + va_start(ap, fmt); + r = vasprintf(&message, fmt, ap); + va_end(ap); + + if (r < 0) { + n_dhcp4_c_event_node_free(node); + goto handle_nomem; + } + + node->event = (NDhcp4ClientEvent) { + .event = N_DHCP4_CLIENT_EVENT_LOG, + .log = { + .level = level, + .message = message, + .allow_steal_message = true, + }, + }; + + c_list_link_tail(log_queue->event_list, &node->client_link); + return; + +handle_nomem: + c_list_link_tail(log_queue->event_list, &log_queue->nomem_node.client_link); +} + +/** * n_dhcp4_client_arm_timer() - update timer * @client: client to operate on * @@ -682,9 +791,10 @@ _c_public_ int n_dhcp4_client_dispatch(NDhcp4Client *client) { /* continue normally */ } else if (r) { if (r >= _N_DHCP4_E_INTERNAL) { - n_dhcp4_c_log(client->config, LOG_ERR, - "invalid internal error code %d after dispatch", - r); + n_dhcp4_log(&client->log_queue, + LOG_ERR, + "invalid internal error code %d after dispatch", + r); return N_DHCP4_E_INTERNAL; } return r; @@ -753,6 +863,8 @@ _c_public_ int n_dhcp4_client_dispatch(NDhcp4Client *client) { * the client attempted several incompatible * probes in parallel, then the most recent * ones will be cancelled asynchronously. + * * N_DHCP4_CLIENT_EVENT_LOG: A logging event if n_dhcp4_client_set_log_level() + * is enabled. * * Return: 0 on success, negative error code on failure. */ diff --git a/shared/n-dhcp4/src/n-dhcp4-private.h b/shared/n-dhcp4/src/n-dhcp4-private.h index e285d95d..90f8f0c3 100644 --- a/shared/n-dhcp4/src/n-dhcp4-private.h +++ b/shared/n-dhcp4/src/n-dhcp4-private.h @@ -24,6 +24,7 @@ typedef struct NDhcp4Outgoing NDhcp4Outgoing; typedef struct NDhcp4SConnection NDhcp4SConnection; typedef struct NDhcp4SConnectionIp NDhcp4SConnectionIp; typedef struct NDhcp4SEventNode NDhcp4SEventNode; +typedef struct NDhcp4LogQueue NDhcp4LogQueue; /* specs */ @@ -242,11 +243,6 @@ struct NDhcp4ClientConfig { size_t n_broadcast_mac; uint8_t *client_id; size_t n_client_id; - struct { - int level; - NDhcp4LogFunc func; - void *data; - } log; }; #define N_DHCP4_CLIENT_CONFIG_NULL(_x) { \ @@ -290,9 +286,42 @@ struct NDhcp4CEventNode { .probe_link = C_LIST_INIT((_x).probe_link), \ } +struct NDhcp4LogQueue { + CList *event_list; + NDhcp4CEventNode nomem_node; + int log_level; + bool is_client : 1; +}; + +#define N_DHCP4_LOG_QUEUE_NULL_DEFUNCT() { \ + .log_level = -1, \ + .is_client = false, \ + } + +#define N_DHCP4_LOG_QUEUE_NULL_CLIENT(client) { \ + .event_list = &((client).event_list), \ + .log_level = -1, \ + .is_client = true, \ + .nomem_node = { \ + .client_link = C_LIST_INIT((client).log_queue.nomem_node.client_link), \ + .probe_link = C_LIST_INIT((client).log_queue.nomem_node.probe_link), \ + .event = { \ + .event = N_DHCP4_CLIENT_EVENT_LOG, \ + .log = { \ + .level = LOG_CRIT, \ + .message = "one or more logging messages dropped due to out of memory", \ + .allow_steal_message = false, \ + }, \ + }, \ + .is_public = false, \ + }, \ + } + struct NDhcp4CConnection { NDhcp4ClientConfig *client_config; NDhcp4ClientProbeConfig *probe_config; + NDhcp4LogQueue *log_queue; + int fd_epoll; unsigned int state; /* current connection state */ @@ -324,6 +353,9 @@ struct NDhcp4Client { unsigned long n_refs; NDhcp4ClientConfig *config; CList event_list; + + NDhcp4LogQueue log_queue; + int fd_epoll; int fd_timer; @@ -339,6 +371,7 @@ struct NDhcp4Client { .event_list = C_LIST_INIT((_x).event_list), \ .fd_epoll = -1, \ .fd_timer = -1, \ + .log_queue = N_DHCP4_LOG_QUEUE_NULL_CLIENT(_x), \ } struct NDhcp4ClientProbe { @@ -570,6 +603,7 @@ NDhcp4CEventNode *n_dhcp4_c_event_node_free(NDhcp4CEventNode *node); int n_dhcp4_c_connection_init(NDhcp4CConnection *connection, NDhcp4ClientConfig *client_config, NDhcp4ClientProbeConfig *probe_config, + NDhcp4LogQueue *log_queue, int fd_epoll); void n_dhcp4_c_connection_deinit(NDhcp4CConnection *connection); @@ -698,19 +732,28 @@ static inline uint64_t n_dhcp4_gettime(clockid_t clock) { return ts.tv_sec * 1000ULL * 1000ULL * 1000ULL + ts.tv_nsec; } -#define n_dhcp4_c_log(_config, _level, ...) \ +void n_dhcp4_log_queue_fmt(NDhcp4LogQueue *log_queue, + int level, + const char *fmt, + ...) _c_printf_(3, 4); + +/** + * n_dhcp4_log() - append a logging event + * @x_log_queue: the logging event queue + * @x_level: the syslog logging level for the message. + * @...: the format string and arguments. + * + * Warning: this macro only evaluates the format arguments if the logging + * level is enabled. + */ +#define n_dhcp4_log(x_log_queue, x_level, ...) \ do { \ - const NDhcp4ClientConfig *__config = _config; \ - int __level = _level; \ + NDhcp4LogQueue *const _log_queue = (x_log_queue); \ + const int _level = (x_level); \ \ - if (__level <= __config->log.level && __config->log.func) { \ - if (1) { \ - _config->log.func(__level, \ - __config->log.data, \ - __VA_ARGS__); \ - } else { \ - /* To have the compiler check arguments */ \ - printf(__VA_ARGS__); \ - } \ + if (_level <= _log_queue->log_level) { \ + n_dhcp4_log_queue_fmt(_log_queue, \ + _level, \ + __VA_ARGS__); \ } \ } while (0) diff --git a/shared/n-dhcp4/src/n-dhcp4.h b/shared/n-dhcp4/src/n-dhcp4.h index d3747662..81452848 100644 --- a/shared/n-dhcp4/src/n-dhcp4.h +++ b/shared/n-dhcp4/src/n-dhcp4.h @@ -29,8 +29,6 @@ typedef struct NDhcp4ServerEvent NDhcp4ServerEvent; typedef struct NDhcp4ServerIp NDhcp4ServerIp; typedef struct NDhcp4ServerLease NDhcp4ServerLease; -typedef void (*NDhcp4LogFunc)(int level, void *data, const char *fmt, ...); - #define N_DHCP4_CLIENT_START_DELAY_RFC2131 (UINT64_C(9000)) enum { @@ -63,6 +61,7 @@ enum { N_DHCP4_CLIENT_EVENT_EXTENDED, N_DHCP4_CLIENT_EVENT_EXPIRED, N_DHCP4_CLIENT_EVENT_CANCELLED, + N_DHCP4_CLIENT_EVENT_LOG, _N_DHCP4_CLIENT_EVENT_N, }; @@ -88,6 +87,14 @@ struct NDhcp4ClientEvent { struct { NDhcp4ClientProbe *probe; } retracted, expired, cancelled; + struct { + /* If allow_steal_message is true, then the user may steal the message when handling + * the event. In that case, set the message field to %NULL and free it yourself + * with free(). */ + const char *message; + int level; + bool allow_steal_message; + } log; }; }; @@ -113,8 +120,6 @@ void n_dhcp4_client_config_set_request_broadcast(NDhcp4ClientConfig *config, boo void n_dhcp4_client_config_set_mac(NDhcp4ClientConfig *config, const uint8_t *mac, size_t n_mac); void n_dhcp4_client_config_set_broadcast_mac(NDhcp4ClientConfig *config, const uint8_t *mac, size_t n_mac); int n_dhcp4_client_config_set_client_id(NDhcp4ClientConfig *config, const uint8_t *id, size_t n_id); -void n_dhcp4_client_config_set_log_level(NDhcp4ClientConfig *config, int level); -void n_dhcp4_client_config_set_log_func(NDhcp4ClientConfig *config, NDhcp4LogFunc func, void *data); /* client-probe configs */ @@ -141,6 +146,8 @@ void n_dhcp4_client_get_fd(NDhcp4Client *client, int *fdp); int n_dhcp4_client_dispatch(NDhcp4Client *client); int n_dhcp4_client_pop_event(NDhcp4Client *client, NDhcp4ClientEvent **eventp); +void n_dhcp4_client_set_log_level(NDhcp4Client *client, int level); + int n_dhcp4_client_update_mtu(NDhcp4Client *client, uint16_t mtu); int n_dhcp4_client_probe(NDhcp4Client *client, diff --git a/shared/nm-glib-aux/nm-glib.h b/shared/nm-glib-aux/nm-glib.h index 7a5b8edd..2df51795 100644 --- a/shared/nm-glib-aux/nm-glib.h +++ b/shared/nm-glib-aux/nm-glib.h @@ -382,9 +382,8 @@ _nm_g_hash_table_get_keys_as_array (GHashTable *hash_table, /*****************************************************************************/ -#if !GLIB_CHECK_VERSION(2, 44, 0) static inline gpointer -g_steal_pointer (gpointer pp) +_nm_g_steal_pointer (gpointer pp) { gpointer *ptr = (gpointer *) pp; gpointer ref; @@ -394,13 +393,20 @@ g_steal_pointer (gpointer pp) return ref; } + +#if !GLIB_CHECK_VERSION(2, 44, 0) +static inline gpointer +g_steal_pointer (gpointer pp) +{ + return _nm_g_steal_pointer (pp); +} #endif #ifdef g_steal_pointer #undef g_steal_pointer #endif #define g_steal_pointer(pp) \ - ((typeof (*(pp))) g_steal_pointer (pp)) + ((typeof (*(pp))) _nm_g_steal_pointer (pp)) /*****************************************************************************/ diff --git a/shared/nm-glib-aux/nm-macros-internal.h b/shared/nm-glib-aux/nm-macros-internal.h index d5a8513e..f56ed856 100644 --- a/shared/nm-glib-aux/nm-macros-internal.h +++ b/shared/nm-glib-aux/nm-macros-internal.h @@ -630,6 +630,14 @@ NM_G_ERROR_MSG (GError *error) NM_CONSTCAST_FULL (type, (obj), _obj, GObject, ##__VA_ARGS__); \ }) +#define NM_ENSURE_NOT_NULL(ptr) \ + ({ \ + typeof (ptr) _ptr = (ptr); \ + \ + nm_assert (_ptr != NULL); \ + _ptr; \ + }) + #if _NM_CC_SUPPORT_GENERIC /* returns @value, if the type of @value matches @type. * This requires support for C11 _Generic(). If no support is @@ -929,23 +937,38 @@ nm_streq0 (const char *s1, const char *s2) #define NM_STR_HAS_PREFIX(str, prefix) \ ({ \ - const char *const _str = (str); \ + const char *const _str_has_prefix = (str); \ \ - _str && (strncmp ((str), ""prefix"", NM_STRLEN (prefix)) == 0); \ + nm_assert (strlen (prefix) == NM_STRLEN (prefix)); \ + \ + _str_has_prefix \ + && (strncmp (_str_has_prefix, ""prefix"", NM_STRLEN (prefix)) == 0); \ }) #define NM_STR_HAS_SUFFIX(str, suffix) \ ({ \ - const char *_str; \ + const char *const _str_has_suffix = (str); \ gsize _l; \ \ - ( (_str = (str)) \ - && ((_l = strlen (_str)) >= NM_STRLEN (suffix)) \ - && (memcmp (&_str[_l - NM_STRLEN (suffix)], \ + nm_assert (strlen (suffix) == NM_STRLEN (suffix)); \ + \ + ( _str_has_suffix \ + && ((_l = strlen (_str_has_suffix)) >= NM_STRLEN (suffix)) \ + && (memcmp (&_str_has_suffix[_l - NM_STRLEN (suffix)], \ ""suffix"", \ NM_STRLEN (suffix)) == 0)); \ }) +/* whether @str starts with the string literal @prefix and is followed by + * some other text. It is like NM_STR_HAS_PREFIX() && !nm_streq() together. */ +#define NM_STR_HAS_PREFIX_WITH_MORE(str, prefix) \ + ({ \ + const char *const _str_has_prefix_with_more = (str); \ + \ + NM_STR_HAS_PREFIX (_str_has_prefix_with_more, ""prefix"") \ + && _str_has_prefix_with_more[NM_STRLEN (prefix)] != '\0'; \ + }) + /*****************************************************************************/ static inline GString * @@ -966,16 +989,23 @@ nm_gstring_add_space_delimiter (GString *str) return str; } +static inline gboolean +nm_str_is_empty (const char *str) +{ + /* %NULL is also accepted, and also "empty". */ + return !str || !str[0]; +} + static inline const char * nm_str_not_empty (const char *str) { - return str && str[0] ? str : NULL; + return !nm_str_is_empty (str) ? str : NULL; } static inline char * nm_strdup_not_empty (const char *str) { - return str && str[0] ? g_strdup (str) : NULL; + return !nm_str_is_empty (str) ? g_strdup (str) : NULL; } static inline char * diff --git a/shared/nm-glib-aux/nm-shared-utils.c b/shared/nm-glib-aux/nm-shared-utils.c index 5dd099a7..39e98a31 100644 --- a/shared/nm-glib-aux/nm-shared-utils.c +++ b/shared/nm-glib-aux/nm-shared-utils.c @@ -13,10 +13,14 @@ #include <sys/syscall.h> #include <glib-unix.h> #include <net/if.h> +#include <net/ethernet.h> #include "nm-errno.h" #include "nm-str-buf.h" +G_STATIC_ASSERT (sizeof (NMUtilsNamedEntry) == sizeof (const char *)); +G_STATIC_ASSERT (G_STRUCT_OFFSET (NMUtilsNamedValue, value_ptr) == sizeof (const char *)); + /*****************************************************************************/ const void *const _NM_PTRARRAY_EMPTY[1] = { NULL }; @@ -85,6 +89,11 @@ nm_ip_addr_set_from_untrusted (int addr_family, /*****************************************************************************/ +G_STATIC_ASSERT (ETH_ALEN == sizeof (struct ether_addr)); +G_STATIC_ASSERT (ETH_ALEN == 6); + +/*****************************************************************************/ + gsize nm_utils_get_next_realloc_size (gboolean true_realloc, gsize requested) { @@ -2456,11 +2465,12 @@ _str_buf_append_c_escape_octal (NMStrBuf *strbuf, } gconstpointer -nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_free) +nm_utils_buf_utf8safe_unescape (const char *str, NMUtilsStrUtf8SafeFlags flags, gsize *out_len, gpointer *to_free) { + gboolean strip_spaces = NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_UNESCAPE_STRIP_SPACES); NMStrBuf strbuf; - gsize len; const char *s; + gsize len; g_return_val_if_fail (to_free, NULL); g_return_val_if_fail (out_len, NULL); @@ -2471,16 +2481,29 @@ nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_fr return NULL; } + if (strip_spaces) + str = nm_str_skip_leading_spaces (str); + len = strlen (str); s = memchr (str, '\\', len); if (!s) { + if ( strip_spaces + && len > 0 + && g_ascii_isspace (str[len - 1])) { + len--; + while ( len > 0 + && g_ascii_isspace (str[len - 1])) + len--; + *out_len = len; + return (*to_free = g_strndup (str, len)); + } *out_len = len; *to_free = NULL; return str; } - nm_str_buf_init (&strbuf, len, FALSE); + nm_str_buf_init (&strbuf, len + 1u, FALSE); nm_str_buf_append_len (&strbuf, str, s - str); str = s; @@ -2494,7 +2517,7 @@ nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_fr ch = (++str)[0]; if (ch == '\0') { - // error. Trailing '\\' + /* error. Trailing '\\' */ break; } @@ -2533,7 +2556,14 @@ nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_fr s = strchr (str, '\\'); if (!s) { - nm_str_buf_append (&strbuf, str); + gsize l = strlen (str); + + if (strip_spaces) { + while ( l > 0 + && g_ascii_isspace (str[l - 1])) + l--; + } + nm_str_buf_append_len (&strbuf, str, l); break; } @@ -2541,6 +2571,11 @@ nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_fr str = s; } + /* assert that no reallocation was necessary. For one, unescaping should + * never result in a longer string than the input. Also, when unescaping + * secrets, we want to ensure that we don't leak secrets in memory. */ + nm_assert (strbuf.allocated == len + 1u); + return (*to_free = nm_str_buf_finalize (&strbuf, out_len)); } @@ -2670,16 +2705,33 @@ nm_utils_buf_utf8safe_escape_bytes (GBytes *bytes, NMUtilsStrUtf8SafeFlags flags return nm_utils_buf_utf8safe_escape (p, l, flags, to_free); } +char * +nm_utils_buf_utf8safe_escape_cp (gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags) +{ + const char *s_const; + char *s; + + s_const = nm_utils_buf_utf8safe_escape (buf, buflen, flags, &s); + nm_assert (!s || s == s_const); + return s ?: g_strdup (s_const); +} + /*****************************************************************************/ const char * -nm_utils_str_utf8safe_unescape (const char *str, char **to_free) +nm_utils_str_utf8safe_unescape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free) { + const char *res; gsize len; g_return_val_if_fail (to_free, NULL); - return nm_utils_buf_utf8safe_unescape (str, &len, (gpointer *) to_free); + res = nm_utils_buf_utf8safe_unescape (str, flags, &len, (gpointer *) to_free); + + nm_assert ( (!res && len == 0) + || (strlen (res) <= len)); + + return res; } /** @@ -2737,11 +2789,11 @@ nm_utils_str_utf8safe_escape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags) } char * -nm_utils_str_utf8safe_unescape_cp (const char *str) +nm_utils_str_utf8safe_unescape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags) { char *s; - str = nm_utils_str_utf8safe_unescape (str, &s); + str = nm_utils_str_utf8safe_unescape (str, flags, &s); return s ?: g_strdup (str); } @@ -4817,3 +4869,123 @@ nm_str_buf_append_printf (NMStrBuf *strbuf, strbuf->_priv_len += (gsize) l; } + +/*****************************************************************************/ + +/** + * nm_indirect_g_free: + * @arg: a pointer to a pointer that is to be freed. + * + * This does the same as nm_clear_g_free(arg) (g_clear_pointer (arg, g_free)). + * This is for example useful when you have a GArray with pointers and a + * clear function to free them. g_array_set_clear_func()'s destroy notify + * function gets a pointer to the array location, so we have to follow + * the first pointer. + */ +void +nm_indirect_g_free (gpointer arg) +{ + gpointer *p = arg; + + nm_clear_g_free (p); +} + +/*****************************************************************************/ + +static char * +attribute_escape (const char *src, char c1, char c2) +{ + char *ret, *dest; + + dest = ret = g_malloc (strlen (src) * 2 + 1); + + while (*src) { + if (*src == c1 || *src == c2 || *src == '\\') + *dest++ = '\\'; + *dest++ = *src++; + } + *dest++ = '\0'; + + return ret; +} + +void +_nm_utils_format_variant_attributes_full (GString *str, + const NMUtilsNamedValue *values, + guint num_values, + char attr_separator, + char key_value_separator) +{ + const char *name, *value; + GVariant *variant; + char *escaped; + char buf[64]; + char sep = 0; + guint i; + + for (i = 0; i < num_values; i++) { + name = values[i].name; + variant = (GVariant *) values[i].value_ptr; + value = NULL; + + if (g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT32)) + value = nm_sprintf_buf (buf, "%u", g_variant_get_uint32 (variant)); + else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_INT32)) + value = nm_sprintf_buf (buf, "%d", (int) g_variant_get_int32 (variant)); + else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT64)) + value = nm_sprintf_buf (buf, "%"G_GUINT64_FORMAT, g_variant_get_uint64 (variant)); + else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BYTE)) + value = nm_sprintf_buf (buf, "%hhu", g_variant_get_byte (variant)); + else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BOOLEAN)) + value = g_variant_get_boolean (variant) ? "true" : "false"; + else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)) + value = g_variant_get_string (variant, NULL); + else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BYTESTRING)) { + /* FIXME: there is no guarantee that the byte array + * is valid UTF-8.*/ + value = g_variant_get_bytestring (variant); + } else + continue; + + if (sep) + g_string_append_c (str, sep); + + escaped = attribute_escape (name, attr_separator, key_value_separator); + g_string_append (str, escaped); + g_free (escaped); + + g_string_append_c (str, key_value_separator); + + escaped = attribute_escape (value, attr_separator, key_value_separator); + g_string_append (str, escaped); + g_free (escaped); + + sep = attr_separator; + } +} + +char * +_nm_utils_format_variant_attributes (GHashTable *attributes, + char attr_separator, + char key_value_separator) +{ + GString *str = NULL; + gs_free NMUtilsNamedValue *values = NULL; + guint len; + + g_return_val_if_fail (attr_separator, NULL); + g_return_val_if_fail (key_value_separator, NULL); + + if (!attributes || !g_hash_table_size (attributes)) + return NULL; + + values = nm_utils_named_values_from_str_dict (attributes, &len); + + str = g_string_new (""); + _nm_utils_format_variant_attributes_full (str, + values, + len, + attr_separator, + key_value_separator); + return g_string_free (str, FALSE); +} diff --git a/shared/nm-glib-aux/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h index d5990c2d..eeb8be5b 100644 --- a/shared/nm-glib-aux/nm-shared-utils.h +++ b/shared/nm-glib-aux/nm-shared-utils.h @@ -178,6 +178,24 @@ nm_ip4_addr_is_localhost (in_addr_t addr4) /*****************************************************************************/ +struct ether_addr; + +static inline int +nm_utils_ether_addr_cmp (const struct ether_addr *a1, const struct ether_addr *a2) +{ + nm_assert (a1); + nm_assert (a2); + return memcmp (a1, a2, 6 /*ETH_ALEN*/); +} + +static inline gboolean +nm_utils_ether_addr_equal (const struct ether_addr *a1, const struct ether_addr *a2) +{ + return nm_utils_ether_addr_cmp (a1, a2) == 0; +} + +/*****************************************************************************/ + #define NM_UTILS_INET_ADDRSTRLEN INET6_ADDRSTRLEN static inline const char * @@ -926,6 +944,21 @@ _nm_g_slice_free_fcn_define (32) /*****************************************************************************/ +/* Like g_error_matches() however: + * - as macro it is always inlined. + * - the @domain is usually a error quark getter function that cannot + * be inlined. This macro calls the getter only if there is an error (lazy). + * - accept a list of allowed codes, instead of only one. + */ +#define nm_g_error_matches(error, err_domain, ...) \ + ({ \ + const GError *const _error = (error); \ + \ + _error \ + && _error->domain == (err_domain) \ + && NM_IN_SET (_error->code, __VA_ARGS__); \ + }) + static inline void nm_g_set_error_take (GError **error, GError *error_take) { @@ -1143,6 +1176,25 @@ GParamSpec *nm_g_object_class_find_property_from_gtype (GType gtype, /*****************************************************************************/ +#define _NM_G_PARAM_SPEC_CAST(param_spec, _value_type, _c_type) \ + ({ \ + const GParamSpec *const _param_spec = (param_spec); \ + \ + nm_assert ( !_param_spec \ + || _param_spec->value_type == (_value_type)); \ + ((const _c_type *) _param_spec); \ + }) + +#define NM_G_PARAM_SPEC_CAST_BOOLEAN(param_spec) _NM_G_PARAM_SPEC_CAST (param_spec, G_TYPE_BOOLEAN, GParamSpecBoolean) +#define NM_G_PARAM_SPEC_CAST_UINT(param_spec) _NM_G_PARAM_SPEC_CAST (param_spec, G_TYPE_UINT, GParamSpecUInt) +#define NM_G_PARAM_SPEC_CAST_UINT64(param_spec) _NM_G_PARAM_SPEC_CAST (param_spec, G_TYPE_UINT64, GParamSpecUInt64) + +#define NM_G_PARAM_SPEC_GET_DEFAULT_BOOLEAN(param_spec) (NM_G_PARAM_SPEC_CAST_BOOLEAN (NM_ENSURE_NOT_NULL (param_spec))->default_value) +#define NM_G_PARAM_SPEC_GET_DEFAULT_UINT(param_spec) (NM_G_PARAM_SPEC_CAST_UINT (NM_ENSURE_NOT_NULL (param_spec))->default_value) +#define NM_G_PARAM_SPEC_GET_DEFAULT_UINT64(param_spec) (NM_G_PARAM_SPEC_CAST_UINT64 (NM_ENSURE_NOT_NULL (param_spec))->default_value) + +/*****************************************************************************/ + GType nm_g_type_find_implementing_class_for_property (GType gtype, const char *pname); @@ -1150,20 +1202,39 @@ GType nm_g_type_find_implementing_class_for_property (GType gtype, typedef enum { NM_UTILS_STR_UTF8_SAFE_FLAG_NONE = 0, + + /* This flag only has an effect during escaping. */ NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL = 0x0001, + + /* This flag only has an effect during escaping. */ NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII = 0x0002, + + /* This flag only has an effect during escaping to ensure we + * don't leak secrets in memory. Note that during unescape we + * know the maximum result size from the beginning, and no + * reallocation happens. Thus, unescape always avoids leaking + * secrets already. */ NM_UTILS_STR_UTF8_SAFE_FLAG_SECRET = 0x0004, + + /* This flag only has an effect during unescaping. It means + * that non-escaped whitespaces (g_ascii_isspace()) will be + * stripped from the front and end of the string. Note that + * this flag is only useful for gracefully accepting user input + * with spaces. With this flag, escape and unescape may no longer + * yield the original input. */ + NM_UTILS_STR_UTF8_SAFE_UNESCAPE_STRIP_SPACES = 0x0008, } NMUtilsStrUtf8SafeFlags; const char *nm_utils_buf_utf8safe_escape (gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags, char **to_free); +char *nm_utils_buf_utf8safe_escape_cp (gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags); const char *nm_utils_buf_utf8safe_escape_bytes (GBytes *bytes, NMUtilsStrUtf8SafeFlags flags, char **to_free); -gconstpointer nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_free); +gconstpointer nm_utils_buf_utf8safe_unescape (const char *str, NMUtilsStrUtf8SafeFlags flags, gsize *out_len, gpointer *to_free); const char *nm_utils_str_utf8safe_escape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free); -const char *nm_utils_str_utf8safe_unescape (const char *str, char **to_free); +const char *nm_utils_str_utf8safe_unescape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free); char *nm_utils_str_utf8safe_escape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags); -char *nm_utils_str_utf8safe_unescape_cp (const char *str); +char *nm_utils_str_utf8safe_unescape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags); char *nm_utils_str_utf8safe_escape_take (char *str, NMUtilsStrUtf8SafeFlags flags); @@ -1211,6 +1282,30 @@ nm_g_variant_is_of_type (GVariant *value, } static inline void +nm_g_variant_builder_add_sv (GVariantBuilder *builder, const char *key, GVariant *val) +{ + g_variant_builder_add (builder, "{sv}", key, val); +} + +static inline void +nm_g_variant_builder_add_sv_bytearray (GVariantBuilder *builder, const char *key, const guint8 *arr, gsize len) +{ + g_variant_builder_add (builder, "{sv}", key, g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, arr, len, 1)); +} + +static inline void +nm_g_variant_builder_add_sv_uint32 (GVariantBuilder *builder, const char *key, guint32 val) +{ + nm_g_variant_builder_add_sv (builder, key, g_variant_new_uint32 (val)); +} + +static inline void +nm_g_variant_builder_add_sv_str (GVariantBuilder *builder, const char *key, const char *str) +{ + nm_g_variant_builder_add_sv (builder, key, g_variant_new_string (str)); +} + +static inline void nm_g_source_destroy_and_unref (GSource *source) { g_source_destroy (source); @@ -1355,6 +1450,8 @@ typedef struct { }; } NMUtilsNamedValue; +#define NM_UTILS_NAMED_VALUE_INIT(n, v) { .name = (n), .value_ptr = (v) } + NMUtilsNamedValue *nm_utils_named_values_from_str_dict_with_sort (GHashTable *hash, guint *out_len, GCompareDataFunc compare_func, @@ -1436,6 +1533,22 @@ char *nm_utils_g_slist_strlist_join (const GSList *a, const char *separator); /*****************************************************************************/ static inline guint +nm_g_array_len (const GArray *arr) +{ + return arr ? arr->len : 0u; +} + +/*****************************************************************************/ + +static inline guint +nm_g_ptr_array_len (const GPtrArray *arr) +{ + return arr ? arr->len : 0u; +} + +/*****************************************************************************/ + +static inline guint nm_g_hash_table_size (GHashTable *hash) { return hash ? g_hash_table_size (hash) : 0u; @@ -1848,6 +1961,8 @@ nm_utils_strdup_reset (char **dst, const char *src) return TRUE; } +void nm_indirect_g_free (gpointer arg); + /*****************************************************************************/ /* nm_utils_get_next_realloc_size() is used to grow buffers exponentially, when @@ -1887,4 +2002,64 @@ gboolean nm_utils_ifname_valid (const char* name, NMUtilsIfaceType type, GError **error); +/*****************************************************************************/ + +static inline GArray * +nm_strvarray_ensure (GArray **p) +{ + if (!*p) { + *p = g_array_new (TRUE, FALSE, sizeof (char *)); + g_array_set_clear_func (*p, nm_indirect_g_free); + } + return *p; +} + +static inline void +nm_strvarray_add (GArray *array, const char *str) +{ + char *s; + + s = g_strdup (str); + g_array_append_val (array, s); +} + +static inline const char *const* +nm_strvarray_get_strv (GArray **arr, guint *length) +{ + if (!*arr) { + NM_SET_OUT (length, 0); + return (const char *const*) arr; + } + + NM_SET_OUT (length, (*arr)->len); + return &g_array_index (*arr, const char *, 0); +} + +static inline void +nm_strvarray_set_strv (GArray **array, const char *const*strv) +{ + gs_unref_array GArray *array_old = NULL; + + array_old = g_steal_pointer (array); + + if (!strv || !strv[0]) + return; + + nm_strvarray_ensure (array); + for (; strv[0]; strv++) + nm_strvarray_add (*array, strv[0]); +} + +/*****************************************************************************/ + +void _nm_utils_format_variant_attributes_full (GString *str, + const NMUtilsNamedValue *values, + guint num_values, + char attr_separator, + char key_value_separator); + +char *_nm_utils_format_variant_attributes (GHashTable *attributes, + char attr_separator, + char key_value_separator); + #endif /* __NM_SHARED_UTILS_H__ */ diff --git a/shared/nm-keyfile/nm-keyfile-internal.h b/shared/nm-keyfile/nm-keyfile-internal.h deleted file mode 100644 index 42003a5f..00000000 --- a/shared/nm-keyfile/nm-keyfile-internal.h +++ /dev/null @@ -1,180 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2008 Novell, Inc. - * Copyright (C) 2015 Red Hat, Inc. - */ - -#ifndef __NM_KEYFILE_INTERNAL_H__ -#define __NM_KEYFILE_INTERNAL_H__ - -#if !((NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_INTERNAL) -#error Cannot use this header. -#endif - -#include <sys/types.h> - -#include "nm-connection.h" -#include "nm-setting-8021x.h" - -#include "nm-core-internal.h" -#include "nm-meta-setting.h" - -/*****************************************************************************/ - -#define NM_KEYFILE_CERT_SCHEME_PREFIX_PATH "file://" -#define NM_KEYFILE_CERT_SCHEME_PREFIX_PKCS11 "pkcs11:" -#define NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB "data:;base64," - -char *nm_keyfile_detect_unqualified_path_scheme (const char *base_dir, - gconstpointer pdata, - gsize data_len, - gboolean consider_exists, - gboolean *out_exists); - -typedef enum { - NM_KEYFILE_READ_TYPE_WARN = 1, -} NMKeyfileReadType; - -/** - * NMKeyfileReadHandler: - * - * Hook to nm_keyfile_read(). The user might fail the reading by setting - * @error. - * - * Returns: should return TRUE, if the reading was handled. Otherwise, - * a default action will be performed that depends on the @type. - * For %NM_KEYFILE_READ_TYPE_WARN type, the default action is doing nothing. - */ -typedef gboolean (*NMKeyfileReadHandler) (GKeyFile *keyfile, - NMConnection *connection, - NMKeyfileReadType type, - void *type_data, - void *user_data, - GError **error); - -typedef enum { - NM_KEYFILE_WARN_SEVERITY_DEBUG = 1000, - NM_KEYFILE_WARN_SEVERITY_INFO = 2000, - NM_KEYFILE_WARN_SEVERITY_INFO_MISSING_FILE = 2901, - NM_KEYFILE_WARN_SEVERITY_WARN = 3000, -} NMKeyfileWarnSeverity; - -/** - * NMKeyfileReadTypeDataWarn: - * - * this struct is passed as @type_data for the @NMKeyfileReadHandler of - * type %NM_KEYFILE_READ_TYPE_WARN. - */ -typedef struct { - /* might be %NULL, if the warning is not about a group. */ - const char *group; - - /* might be %NULL, if the warning is not about a setting. */ - NMSetting *setting; - - /* might be %NULL, if the warning is not about a property. */ - const char *property_name; - - NMKeyfileWarnSeverity severity; - const char *message; -} NMKeyfileReadTypeDataWarn; - -NMConnection *nm_keyfile_read (GKeyFile *keyfile, - const char *base_dir, - NMKeyfileReadHandler handler, - void *user_data, - GError **error); - -gboolean nm_keyfile_read_ensure_id (NMConnection *connection, - const char *fallback_id); - -gboolean nm_keyfile_read_ensure_uuid (NMConnection *connection, - const char *fallback_uuid_seed); - -/*****************************************************************************/ - -typedef enum { - NM_KEYFILE_WRITE_TYPE_CERT = 1, -} NMKeyfileWriteType; - -/** - * NMKeyfileWriteHandler: - * - * This is a hook to tweak the serialization. - * - * Handler for certain properties or events that are not entirely contained - * within the keyfile or that might be serialized differently. The @type and - * @type_data arguments tell which kind of argument we have at hand. - * - * Currently only the type %NM_KEYFILE_WRITE_TYPE_CERT is supported, which provides - * @type_data as %NMKeyfileWriteTypeDataCert. However, this handler should be generic enough - * to support other types as well. - * - * This don't have to be only "properties". For example, nm_keyfile_read() uses - * a similar handler to push warnings to the caller. - * - * If the handler raises an error, it should set the @error value. This causes - * the an overall failure. - * - * Returns: whether the issue was handled. If the type was unhandled, - * a default action will be performed. This might be raise an error, - * do some fallback parsing, or do nothing. - */ -typedef gboolean (*NMKeyfileWriteHandler) (NMConnection *connection, - GKeyFile *keyfile, - NMKeyfileWriteType type, - void *type_data, - void *user_data, - GError **error); - -/** - * NMKeyfileWriteTypeDataCert: - * - * this struct is passed as @type_data for the @NMKeyfileWriteHandler of - * type %NM_KEYFILE_WRITE_TYPE_CERT. - */ -typedef struct { - const NMSetting8021xSchemeVtable *vtable; - NMSetting8021x *setting; -} NMKeyfileWriteTypeDataCert; - -GKeyFile *nm_keyfile_write (NMConnection *connection, - NMKeyfileWriteHandler handler, - void *user_data, - GError **error); - -/*****************************************************************************/ - -char *nm_keyfile_plugin_kf_get_string (GKeyFile *kf, const char *group, const char *key, GError **error); -void nm_keyfile_plugin_kf_set_string (GKeyFile *kf, const char *group, const char *key, const char *value); - -int nm_key_file_get_boolean (GKeyFile *kf, const char *group, const char *key, int default_value); - -void _nm_keyfile_copy (GKeyFile *dst, GKeyFile *src); -gboolean _nm_keyfile_a_contains_all_in_b (GKeyFile *kf_a, GKeyFile *kf_b); -gboolean _nm_keyfile_equals (GKeyFile *kf_a, GKeyFile *kf_b, gboolean consider_order); -gboolean _nm_keyfile_has_values (GKeyFile *keyfile); - -/*****************************************************************************/ - -#define NM_KEYFILE_GROUP_NMMETA ".nmmeta" -#define NM_KEYFILE_KEY_NMMETA_NM_GENERATED "nm-generated" -#define NM_KEYFILE_KEY_NMMETA_VOLATILE "volatile" -#define NM_KEYFILE_KEY_NMMETA_SHADOWED_STORAGE "shadowed-storage" -#define NM_KEYFILE_KEY_NMMETA_SHADOWED_OWNED "shadowed-owned" - -#define NM_KEYFILE_PATH_NAME_LIB NMLIBDIR "/system-connections" -#define NM_KEYFILE_PATH_NAME_ETC_DEFAULT NMCONFDIR "/system-connections" -#define NM_KEYFILE_PATH_NAME_RUN NMRUNDIR "/system-connections" - -#define NM_KEYFILE_PATH_SUFFIX_NMCONNECTION ".nmconnection" - -#define NM_KEYFILE_PATH_SUFFIX_NMMETA ".nmmeta" - -#define NM_KEYFILE_PATH_NMMETA_SYMLINK_NULL "/dev/null" - -gboolean nm_keyfile_utils_ignore_filename (const char *filename, gboolean require_extension); - -char *nm_keyfile_utils_create_filename (const char *filename, gboolean with_extension); - -#endif /* __NM_KEYFILE_INTERNAL_H__ */ diff --git a/shared/nm-keyfile/nm-keyfile-utils.c b/shared/nm-keyfile/nm-keyfile-utils.c deleted file mode 100644 index 041b5eab..00000000 --- a/shared/nm-keyfile/nm-keyfile-utils.c +++ /dev/null @@ -1,635 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2010 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-keyfile-utils.h" - -#include <stdlib.h> - -#include "nm-keyfile-internal.h" -#include "nm-setting-wired.h" -#include "nm-setting-wireless.h" -#include "nm-setting-wireless-security.h" - -/*****************************************************************************/ - -/** - * nm_key_file_get_boolean: - * @kf: the #GKeyFile - * @group: the group - * @key: the key - * @default_value: the default value if the value is set or not parsable as a boolean. - * - * Replacement for g_key_file_get_boolean() (which uses g_key_file_parse_value_as_boolean()). - * g_key_file_get_boolean() seems odd to me, because it accepts trailing ASCII whitespace, - * but not leading. - * This uses _nm_utils_ascii_str_to_bool(), which accepts trailing and leading whitespace, - * case-insensitive words, and also strings like "on" and "off". - * _nm_utils_ascii_str_to_bool() is our way to parse booleans from string, and we should - * use that one consistently. - * - * Also, it doesn't have g_key_file_get_boolean()'s odd API to require an error argument - * to detect parsing failures. - * - * Returns: either %TRUE or %FALSE if the key exists and is parsable as a boolean. - * Otherwise, @default_value. Sets errno to ENODATA, EINVAL or 0, depending on whether - * the key exists, whether the value is invalid, or success. - */ -int -nm_key_file_get_boolean (GKeyFile *kf, const char *group, const char *key, int default_value) -{ - int v; - gs_free char *value = NULL; - - value = g_key_file_get_value (kf, group, key, NULL); - - if (!value) { - errno = ENODATA; - return default_value; - } - v = _nm_utils_ascii_str_to_bool (value, -1); - if (v != -1) { - errno = 0; - return v; - } - errno = EINVAL; - return default_value; -} - -/*****************************************************************************/ - -typedef struct { - const char *setting; - const char *alias; -} SettingAlias; - -static const SettingAlias alias_list[] = { - { NM_SETTING_WIRED_SETTING_NAME, "ethernet" }, - { NM_SETTING_WIRELESS_SETTING_NAME, "wifi" }, - { NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, "wifi-security" }, -}; - -const char * -nm_keyfile_plugin_get_alias_for_setting_name (const char *setting_name) -{ - guint i; - - g_return_val_if_fail (setting_name != NULL, NULL); - - for (i = 0; i < G_N_ELEMENTS (alias_list); i++) { - if (nm_streq (setting_name, alias_list[i].setting)) - return alias_list[i].alias; - } - return NULL; -} - -const char * -nm_keyfile_plugin_get_setting_name_for_alias (const char *alias) -{ - guint i; - - g_return_val_if_fail (alias != NULL, NULL); - - for (i = 0; i < G_N_ELEMENTS (alias_list); i++) { - if (nm_streq (alias, alias_list[i].alias)) - return alias_list[i].setting; - } - return NULL; -} - -/*****************************************************************************/ - -/* List helpers */ -#define DEFINE_KF_LIST_WRAPPER(stype, get_ctype, set_ctype) \ -get_ctype \ -nm_keyfile_plugin_kf_get_##stype##_list (GKeyFile *kf, \ - const char *group, \ - const char *key, \ - gsize *out_length, \ - GError **error) \ -{ \ - get_ctype list; \ - const char *alias; \ - GError *local = NULL; \ - gsize l; \ - \ - list = g_key_file_get_##stype##_list (kf, group, key, &l, &local); \ - if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) { \ - alias = nm_keyfile_plugin_get_alias_for_setting_name (group); \ - if (alias) { \ - g_clear_error (&local); \ - list = g_key_file_get_##stype##_list (kf, alias, key, &l, &local); \ - } \ - } \ - if (local) \ - g_propagate_error (error, local); \ - if (!list) \ - l = 0; \ - NM_SET_OUT (out_length, l); \ - return list; \ -} \ - \ -void \ -nm_keyfile_plugin_kf_set_##stype##_list (GKeyFile *kf, \ - const char *group, \ - const char *key, \ - set_ctype list[], \ - gsize length) \ -{ \ - const char *alias; \ - \ - alias = nm_keyfile_plugin_get_alias_for_setting_name (group); \ - g_key_file_set_##stype##_list (kf, alias ?: group, key, list, length); \ -} - -DEFINE_KF_LIST_WRAPPER(integer, int*, int); -DEFINE_KF_LIST_WRAPPER(string, char **, const char* const); - -void -nm_keyfile_plugin_kf_set_integer_list_uint8 (GKeyFile *kf, - const char *group, - const char *key, - const guint8 *data, - gsize length) -{ - gsize i; - gsize l = length * 4 + 2; - gs_free char *value = g_malloc (l); - char *s = value; - - g_return_if_fail (kf); - g_return_if_fail (!length || data); - g_return_if_fail (group && group[0]); - g_return_if_fail (key && key[0]); - - value[0] = '\0'; - for (i = 0; i < length; i++) - nm_utils_strbuf_append (&s, &l, "%d;", (int) data[i]); - nm_assert (l > 0); - nm_keyfile_plugin_kf_set_value (kf, group, key, value); -} - -/* Single value helpers */ -#define DEFINE_KF_WRAPPER(stype, get_ctype, set_ctype) \ -get_ctype \ -nm_keyfile_plugin_kf_get_##stype (GKeyFile *kf, \ - const char *group, \ - const char *key, \ - GError **error) \ -{ \ - get_ctype val; \ - const char *alias; \ - GError *local = NULL; \ - \ - val = g_key_file_get_##stype (kf, group, key, &local); \ - if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) { \ - alias = nm_keyfile_plugin_get_alias_for_setting_name (group); \ - if (alias) { \ - g_clear_error (&local); \ - val = g_key_file_get_##stype (kf, alias, key, &local); \ - } \ - } \ - if (local) \ - g_propagate_error (error, local); \ - return val; \ -} \ - \ -void \ -nm_keyfile_plugin_kf_set_##stype (GKeyFile *kf, \ - const char *group, \ - const char *key, \ - set_ctype value) \ -{ \ - const char *alias; \ - \ - alias = nm_keyfile_plugin_get_alias_for_setting_name (group); \ - g_key_file_set_##stype (kf, alias ?: group, key, value); \ -} - -DEFINE_KF_WRAPPER(string, char*, const char*); -DEFINE_KF_WRAPPER(boolean, gboolean, gboolean); -DEFINE_KF_WRAPPER(value, char*, const char*); - -gint64 -nm_keyfile_plugin_kf_get_int64 (GKeyFile *kf, - const char *group, - const char *key, - guint base, - gint64 min, - gint64 max, - gint64 fallback, - GError **error) -{ - gs_free char *s = NULL; - int errsv; - gint64 v; - - s = nm_keyfile_plugin_kf_get_value (kf, group, key, error); - if (!s) { - errno = ENODATA; - return fallback; - } - - v = _nm_utils_ascii_str_to_int64 (s, base, min, max, fallback); - errsv = errno; - if ( errsv != 0 - && error) { - g_set_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE, - _("value is not an integer in range [%lld, %lld]"), - (long long) min, (long long) max); - errno = errsv; - } - return v; -} - -char ** -nm_keyfile_plugin_kf_get_keys (GKeyFile *kf, - const char *group, - gsize *out_length, - GError **error) -{ - char **keys; - const char *alias; - GError *local = NULL; - gsize l; - - keys = g_key_file_get_keys (kf, group, &l, &local); - if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) { - alias = nm_keyfile_plugin_get_alias_for_setting_name (group); - if (alias) { - g_clear_error (&local); - keys = g_key_file_get_keys (kf, alias, &l, error ? &local : NULL); - } - } - nm_assert ((!local) != (!keys)); - if (!keys) - l = 0; - nm_assert (l == NM_PTRARRAY_LEN (keys)); - NM_SET_OUT (out_length, l); - if (local) - g_propagate_error (error, local); - return keys; -} - -gboolean -nm_keyfile_plugin_kf_has_key (GKeyFile *kf, - const char *group, - const char *key, - GError **error) -{ - gboolean has; - const char *alias; - GError *local = NULL; - - has = g_key_file_has_key (kf, group, key, &local); - if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) { - alias = nm_keyfile_plugin_get_alias_for_setting_name (group); - if (alias) { - g_clear_error (&local); - has = g_key_file_has_key (kf, alias, key, &local); - } - } - if (local) - g_propagate_error (error, local); - return has; -} - -/*****************************************************************************/ - -void -_nm_keyfile_copy (GKeyFile *dst, GKeyFile *src) -{ - gs_strfreev char **groups = NULL; - guint g, k; - - groups = g_key_file_get_groups (src, NULL); - for (g = 0; groups && groups[g]; g++) { - const char *group = groups[g]; - gs_strfreev char **keys = NULL; - - keys = g_key_file_get_keys (src, group, NULL, NULL); - if (!keys) - continue; - - for (k = 0; keys[k]; k++) { - const char *key = keys[k]; - gs_free char *value = NULL; - - value = g_key_file_get_value (src, group, key, NULL); - if (value) - g_key_file_set_value (dst, group, key, value); - else - g_key_file_remove_key (dst, group, key, NULL); - } - } -} - -/*****************************************************************************/ - -gboolean -_nm_keyfile_a_contains_all_in_b (GKeyFile *kf_a, GKeyFile *kf_b) -{ - gs_strfreev char **groups = NULL; - guint i, j; - - if (kf_a == kf_b) - return TRUE; - if (!kf_a || !kf_b) - return FALSE; - - groups = g_key_file_get_groups (kf_a, NULL); - for (i = 0; groups && groups[i]; i++) { - gs_strfreev char **keys = NULL; - - keys = g_key_file_get_keys (kf_a, groups[i], NULL, NULL); - if (!keys) - continue; - - for (j = 0; keys[j]; j++) { - gs_free char *key_a = g_key_file_get_value (kf_a, groups[i], keys[j], NULL); - gs_free char *key_b = g_key_file_get_value (kf_b, groups[i], keys[j], NULL); - - if (g_strcmp0 (key_a, key_b) != 0) - return FALSE; - } - } - return TRUE; -} - -static gboolean -_nm_keyfile_equals_ordered (GKeyFile *kf_a, GKeyFile *kf_b) -{ - gs_strfreev char **groups = NULL; - gs_strfreev char **groups_b = NULL; - guint i, j; - - if (kf_a == kf_b) - return TRUE; - if (!kf_a || !kf_b) - return FALSE; - - groups = g_key_file_get_groups (kf_a, NULL); - groups_b = g_key_file_get_groups (kf_b, NULL); - if (!groups && !groups_b) - return TRUE; - if (!groups || !groups_b) - return FALSE; - for (i = 0; groups[i] && groups_b[i] && !strcmp (groups[i], groups_b[i]); i++) - ; - if (groups[i] || groups_b[i]) - return FALSE; - - for (i = 0; groups[i]; i++) { - gs_strfreev char **keys = NULL; - gs_strfreev char **keys_b = NULL; - - keys = g_key_file_get_keys (kf_a, groups[i], NULL, NULL); - keys_b = g_key_file_get_keys (kf_b, groups[i], NULL, NULL); - - if ((!keys) != (!keys_b)) - return FALSE; - if (!keys) - continue; - - for (j = 0; keys[j] && keys_b[j] && !strcmp (keys[j], keys_b[j]); j++) - ; - if (keys[j] || keys_b[j]) - return FALSE; - - for (j = 0; keys[j]; j++) { - gs_free char *key_a = g_key_file_get_value (kf_a, groups[i], keys[j], NULL); - gs_free char *key_b = g_key_file_get_value (kf_b, groups[i], keys[j], NULL); - - if (g_strcmp0 (key_a, key_b) != 0) - return FALSE; - } - } - return TRUE; -} - -gboolean -_nm_keyfile_equals (GKeyFile *kf_a, GKeyFile *kf_b, gboolean consider_order) -{ - if (!consider_order) { - return _nm_keyfile_a_contains_all_in_b (kf_a, kf_b) - && _nm_keyfile_a_contains_all_in_b (kf_b, kf_a); - } else { - return _nm_keyfile_equals_ordered (kf_a, kf_b); - } -} - -gboolean -_nm_keyfile_has_values (GKeyFile *keyfile) -{ - gs_strfreev char **groups = NULL; - - g_return_val_if_fail (keyfile, FALSE); - - groups = g_key_file_get_groups (keyfile, NULL); - return groups && groups[0]; -} - -/*****************************************************************************/ - -static const char * -_keyfile_key_encode (const char *name, - char **out_to_free) -{ - gsize len, i; - GString *str; - - nm_assert (name); - nm_assert (out_to_free && !*out_to_free); - - /* See g_key_file_is_key_name(). - * - * GKeyFile allows all UTF-8 characters (even non-well formed sequences), - * except: - * - no empty keys - * - no leading/trailing ' ' - * - no '=', '[', ']' - * - * We do something more strict here. All non-ASCII characters, all non-printable - * characters, and all invalid characters are escaped with "\\XX". - * - * We don't escape \\, unless it is followed by two hex digits. - */ - - if (!name[0]) { - /* empty keys are are backslash encoded. Note that usually - * \\00 is not a valid encode, the only exception is the empty - * word. */ - return "\\00"; - } - - /* find the first character that needs escaping. */ - i = 0; - if (name[0] != ' ') { - for (;; i++) { - const guchar ch = (guchar) name[i]; - - if (ch == '\0') - return name; - - if ( ch < 0x20 - || ch >= 127 - || NM_IN_SET (ch, '=', '[', ']') - || ( ch == '\\' - && g_ascii_isxdigit (name[i + 1]) - && g_ascii_isxdigit (name[i + 2])) - || ( ch == ' ' - && name[i + 1] == '\0')) - break; - } - } else if (name[1] == '\0') - return "\\20"; - - len = i + strlen (&name[i]); - nm_assert (len == strlen (name)); - str = g_string_sized_new (len + 15); - - if (name[0] == ' ') { - nm_assert (i == 0); - g_string_append (str, "\\20"); - i = 1; - } else - g_string_append_len (str, name, i); - - for (;; i++) { - const guchar ch = (guchar) name[i]; - - if (ch == '\0') - break; - - if ( ch < 0x20 - || ch >= 127 - || NM_IN_SET (ch, '=', '[', ']') - || ( ch == '\\' - && g_ascii_isxdigit (name[i + 1]) - && g_ascii_isxdigit (name[i + 2])) - || ( ch == ' ' - && name[i + 1] == '\0')) - g_string_append_printf (str, "\\%02X", ch); - else - g_string_append_c (str, (char) ch); - } - - return (*out_to_free = g_string_free (str, FALSE)); -} - -static const char * -_keyfile_key_decode (const char *key, - char **out_to_free) -{ - gsize i, len; - GString *str; - - nm_assert (key); - nm_assert (out_to_free && !*out_to_free); - - if (!key[0]) - return ""; - - for (i = 0; TRUE; i++) { - const char ch = key[i]; - - if (ch == '\0') - return key; - if ( ch == '\\' - && g_ascii_isxdigit (key[i + 1]) - && g_ascii_isxdigit (key[i + 2])) - break; - } - - len = i + strlen (&key[i]); - - if ( len == 3 - && nm_streq (key, "\\00")) - return ""; - - nm_assert (len == strlen (key)); - str = g_string_sized_new (len + 3); - - g_string_append_len (str, key, i); - for (;;) { - const char ch = key[i]; - char ch1, ch2; - unsigned v; - - if (ch == '\0') - break; - - if ( ch == '\\' - && g_ascii_isxdigit ((ch1 = key[i + 1])) - && g_ascii_isxdigit ((ch2 = key[i + 2]))) { - v = (g_ascii_xdigit_value (ch1) << 4) + g_ascii_xdigit_value (ch2); - if (v != 0) { - g_string_append_c (str, (char) v); - i += 3; - continue; - } - } - g_string_append_c (str, ch); - i++; - } - - return (*out_to_free = g_string_free (str, FALSE)); -} - -/*****************************************************************************/ - -const char * -nm_keyfile_key_encode (const char *name, - char **out_to_free) -{ - const char *key; - - key = _keyfile_key_encode (name, out_to_free); -#if NM_MORE_ASSERTS > 5 - nm_assert (key); - nm_assert (!*out_to_free || key == *out_to_free); - nm_assert (!*out_to_free || !nm_streq0 (name, key)); - { - gs_free char *to_free2 = NULL; - const char *name2; - - name2 = _keyfile_key_decode (key, &to_free2); - /* name2, the result of encode()+decode() is identical to name. - * That is because - * - encode() is a injective function. - * - decode() is a surjective function, however for output - * values of encode() is behaves injective too. */ - nm_assert (nm_streq0 (name2, name)); - } -#endif - return key; -} - -const char * -nm_keyfile_key_decode (const char *key, - char **out_to_free) -{ - const char *name; - - name = _keyfile_key_decode (key, out_to_free); -#if NM_MORE_ASSERTS > 5 - nm_assert (name); - nm_assert (!*out_to_free || name == *out_to_free); - { - gs_free char *to_free2 = NULL; - const char *key2; - - key2 = _keyfile_key_encode (name, &to_free2); - /* key2, the result of decode+encode may not be idential - * to the original key. That is, decode() is a surjective - * function mapping different keys to the same name. - * However, decode() behaves injective for input that - * are valid output of encode(). */ - nm_assert (key2); - } -#endif - return name; -} diff --git a/shared/nm-keyfile/nm-keyfile-utils.h b/shared/nm-keyfile/nm-keyfile-utils.h deleted file mode 100644 index a351262e..00000000 --- a/shared/nm-keyfile/nm-keyfile-utils.h +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2010 - 2015 Red Hat, Inc. - */ - -#ifndef __NM_KEYFILE_UTILS_H__ -#define __NM_KEYFILE_UTILS_H__ - -#if !((NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_INTERNAL) -#error Cannot use this header. -#endif - -#define NM_KEYFILE_GROUP_VPN_SECRETS "vpn-secrets" -#define NM_KEYFILE_GROUPPREFIX_WIREGUARD_PEER "wireguard-peer." - -const char *nm_keyfile_plugin_get_alias_for_setting_name (const char *setting_name); - -const char *nm_keyfile_plugin_get_setting_name_for_alias (const char *alias); - -/*****************************************************************************/ - -/* List helpers */ -#define DEFINE_KF_LIST_WRAPPER_PROTO(stype, get_ctype, set_ctype) \ -get_ctype nm_keyfile_plugin_kf_get_##stype##_list (GKeyFile *kf, \ - const char *group, \ - const char *key, \ - gsize *out_length, \ - GError **error); \ -\ -void nm_keyfile_plugin_kf_set_##stype##_list (GKeyFile *kf, \ - const char *group, \ - const char *key, \ - set_ctype list[], \ - gsize length); -DEFINE_KF_LIST_WRAPPER_PROTO(integer, int*, int) -DEFINE_KF_LIST_WRAPPER_PROTO(string, char**, const char* const) - -void nm_keyfile_plugin_kf_set_integer_list_uint8 (GKeyFile *kf, - const char *group, - const char *key, - const guint8 *list, - gsize length); - -/* Single-value helpers */ -#define DEFINE_KF_WRAPPER_PROTO(stype, get_ctype, set_ctype) \ -get_ctype nm_keyfile_plugin_kf_get_##stype (GKeyFile *kf, \ - const char *group, \ - const char *key, \ - GError **error); \ -\ -void nm_keyfile_plugin_kf_set_##stype (GKeyFile *kf, \ - const char *group, \ - const char *key, \ - set_ctype value); -DEFINE_KF_WRAPPER_PROTO(string, char*, const char*) -DEFINE_KF_WRAPPER_PROTO(boolean, gboolean, gboolean) -DEFINE_KF_WRAPPER_PROTO(value, char*, const char*) - -/* Misc */ -gint64 nm_keyfile_plugin_kf_get_int64 (GKeyFile *kf, - const char *group, - const char *key, - guint base, - gint64 min, - gint64 max, - gint64 fallback, - GError **error); - -char ** nm_keyfile_plugin_kf_get_keys (GKeyFile *kf, - const char *group, - gsize *out_length, - GError **error); - -gboolean nm_keyfile_plugin_kf_has_key (GKeyFile *kf, - const char *group, - const char *key, - GError **error); - -const char *nm_keyfile_key_encode (const char *name, - char **out_to_free); - -const char *nm_keyfile_key_decode (const char *key, - char **out_to_free); - -#endif /* __NM_KEYFILE_UTILS_H__ */ diff --git a/shared/nm-keyfile/nm-keyfile.c b/shared/nm-keyfile/nm-keyfile.c deleted file mode 100644 index 00a61d03..00000000 --- a/shared/nm-keyfile/nm-keyfile.c +++ /dev/null @@ -1,4068 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2008 - 2009 Novell, Inc. - * Copyright (C) 2008 - 2017 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-keyfile-internal.h" - -#include <stdlib.h> -#include <stdio.h> -#include <sys/stat.h> -#include <unistd.h> -#include <sys/types.h> -#include <arpa/inet.h> -#include <linux/pkt_sched.h> - -#include "nm-glib-aux/nm-secret-utils.h" -#include "systemd/nm-sd-utils-shared.h" -#include "nm-libnm-core-intern/nm-common-macros.h" -#include "nm-core-internal.h" -#include "nm-keyfile-utils.h" - -#include "nm-setting-user.h" - -/*****************************************************************************/ - -typedef struct _ParseInfoProperty ParseInfoProperty; - -typedef struct { - NMConnection *connection; - GKeyFile *keyfile; - const char *base_dir; - NMKeyfileReadHandler handler; - void *user_data; - GError *error; - const char *group; - NMSetting *setting; -} KeyfileReaderInfo; - -typedef struct { - NMConnection *connection; - GKeyFile *keyfile; - GError *error; - NMKeyfileWriteHandler handler; - void *user_data; -} KeyfileWriterInfo; - -/*****************************************************************************/ - -static void -_handle_warn (KeyfileReaderInfo *info, - const char *property_name, - NMKeyfileWarnSeverity severity, - char *message) -{ - NMKeyfileReadTypeDataWarn type_data = { - .group = info->group, - .setting = info->setting, - .property_name = property_name, - .severity = severity, - .message = message, - }; - - info->handler (info->keyfile, - info->connection, - NM_KEYFILE_READ_TYPE_WARN, - &type_data, - info->user_data, - &info->error); - g_free (message); -} -#define handle_warn(arg_info, arg_property_name, arg_severity, ...) \ - ({ \ - KeyfileReaderInfo *_info = (arg_info); \ - \ - if (_info->handler) { \ - _handle_warn (_info, (arg_property_name), (arg_severity), \ - g_strdup_printf (__VA_ARGS__)); \ - } \ - _info->error == NULL; \ - }) - -/*****************************************************************************/ - -static gboolean -_secret_flags_persist_secret (NMSettingSecretFlags flags) -{ - return flags == NM_SETTING_SECRET_FLAG_NONE; -} - -/*****************************************************************************/ -/* Some setting properties also contain setting names, such as - * NMSettingConnection's 'type' property (which specifies the base type of the - * connection, e.g. ethernet or wifi) or 'slave-type' (specifies type of slave - * connection, e.g. bond or bridge). This function handles translating those - * properties' values to the real setting name if they are an alias. - */ -static void -setting_alias_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) -{ - const char *setting_name = nm_setting_get_name (setting); - const char *key_setting_name; - gs_free char *s = NULL; - - s = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL); - if (!s) - return; - - key_setting_name = nm_keyfile_plugin_get_setting_name_for_alias (s); - g_object_set (G_OBJECT (setting), - key, - key_setting_name ?: s, - NULL); -} - -static void -sriov_vfs_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) -{ - const char *setting_name = nm_setting_get_name (setting); - gs_unref_ptrarray GPtrArray *vfs = NULL; - gs_strfreev char **keys = NULL; - gsize n_keys = 0; - int i; - - keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, setting_name, &n_keys, NULL); - if (n_keys == 0) - return; - - vfs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_sriov_vf_unref); - - for (i = 0; i < n_keys; i++) { - gs_free char *value = NULL; - NMSriovVF *vf; - const char *rest; - - if (!g_str_has_prefix (keys[i], "vf.")) - continue; - - rest = &keys[i][3]; - - if (!NM_STRCHAR_ALL (rest, ch, g_ascii_isdigit (ch))) - continue; - - value = nm_keyfile_plugin_kf_get_string (info->keyfile, - setting_name, - keys[i], - NULL); - - vf = _nm_utils_sriov_vf_from_strparts (rest, value, TRUE, NULL); - if (vf) - g_ptr_array_add (vfs, vf); - } - - g_object_set (G_OBJECT (setting), - key, vfs, - NULL); -} - -static void -read_array_of_uint (GKeyFile *file, - NMSetting *setting, - const char *key) -{ - gs_unref_array GArray *array = NULL; - gsize length; - gsize i; - gs_free int *tmp = NULL; - gs_free_error GError *error = NULL; - - tmp = nm_keyfile_plugin_kf_get_integer_list (file, nm_setting_get_name (setting), key, &length, &error); - if (error) - return; - - array = g_array_sized_new (FALSE, FALSE, sizeof (guint), length); - - for (i = 0; i < length; i++) { - if (tmp[i] < 0) - return; - g_array_append_val (array, tmp[i]); - } - - g_object_set (setting, key, array, NULL); -} - -static gboolean -get_one_int (KeyfileReaderInfo *info, const char *property_name, const char *str, guint32 max_val, guint32 *out) -{ - gint64 tmp; - - g_return_val_if_fail (!info == !property_name, FALSE); - - if (!str || !str[0]) { - if (property_name) - handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring missing number")); - return FALSE; - } - - tmp = _nm_utils_ascii_str_to_int64 (str, 10, 0, max_val, -1); - if (tmp == -1) { - if (property_name) { - handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid number '%s'"), - str); - } - return FALSE; - } - - *out = (guint32) tmp; - return TRUE; -} - -static gpointer -build_address (KeyfileReaderInfo *info, int family, const char *address_str, guint32 plen, const char *property_name) -{ - NMIPAddress *addr; - GError *error = NULL; - - g_return_val_if_fail (address_str, NULL); - - addr = nm_ip_address_new (family, address_str, plen, &error); - if (!addr) { - handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid %s address: %s"), - family == AF_INET ? "IPv4" : "IPv6", error->message); - g_error_free (error); - } - - return addr; -} - -static gpointer -build_route (KeyfileReaderInfo *info, - const char *property_name, - int family, - const char *dest_str, - guint32 plen, - const char *gateway_str, - const char *metric_str) -{ - NMIPRoute *route; - guint32 u32; - gint64 metric = -1; - GError *error = NULL; - - g_return_val_if_fail (dest_str, NULL); - - /* Next hop */ - if (gateway_str && gateway_str[0]) { - if (!nm_utils_ipaddr_is_valid (family, gateway_str)) { - /* Try workaround for routes written by broken keyfile writer. - * Due to bug bgo#719851, an older version of writer would have - * written "a:b:c:d::/plen,metric" if the gateway was ::, instead - * of "a:b:c:d::/plen,,metric" or "a:b:c:d::/plen,::,metric" - * Try workaround by interpreting gateway_str as metric to accept such - * invalid routes. This broken syntax should not be not officially - * supported. - **/ - if ( family == AF_INET6 - && !metric_str - && get_one_int (NULL, NULL, gateway_str, G_MAXUINT32, &u32)) { - metric = u32; - gateway_str = NULL; - } else { - if (!info->error) { - handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid gateway '%s' for %s route"), - gateway_str, family == AF_INET ? "IPv4" : "IPv6"); - } - return NULL; - } - } - } else - gateway_str = NULL; - - /* parse metric, default to -1 */ - if (metric_str) { - if (!get_one_int (info, property_name, metric_str, G_MAXUINT32, &u32)) - return NULL; - metric = u32; - } - - route = nm_ip_route_new (family, - dest_str, - plen, - gateway_str, - metric, - &error); - if (!route) { - handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid %s route: %s"), - family == AF_INET ? "IPv4" : "IPv6", - error->message); - g_error_free (error); - } - - return route; -} - -/* On success, returns pointer to the zero-terminated field (original @current). - * The @current * pointer target is set to point to the rest of the input - * or %NULL if there is no more input. Sets error to %NULL for convenience. - * - * On failure, returns %NULL (unspecified). The @current pointer target is - * resets to its original value to allow skipping fields. The @error target - * is set to the character that breaks the parsing or %NULL if @current was %NULL. - * - * When @current target is %NULL, gracefully fail returning %NULL while - * leaving the @current target %NULL end setting @error to %NULL; - */ -static const char * -read_field (char **current, const char **out_err_str, const char *characters, const char *delimiters) -{ - const char *start; - - nm_assert (current); - nm_assert (out_err_str); - nm_assert (characters); - nm_assert (delimiters); - - *out_err_str = NULL; - - if (!*current) { - /* graceful failure, leave '*current' NULL */ - return NULL; - } - - /* fail on empty input */ - if (!**current) - return NULL; - - /* remember beginning of input */ - start = *current; - - while (**current && strchr (characters, **current)) - (*current)++; - if (**current) - if (strchr (delimiters, **current)) { - /* success, more data available */ - *(*current)++ = '\0'; - return start; - } else { - /* error, bad character */ - *out_err_str = *current; - *current = (char *) start; - return NULL; - } - else { - /* success, end of input */ - *current = NULL; - return start; - } -} - -/*****************************************************************************/ - -#define NM_DBUS_SERVICE_OPENCONNECT "org.freedesktop.NetworkManager.openconnect" -#define NM_OPENCONNECT_KEY_GATEWAY "gateway" -#define NM_OPENCONNECT_KEY_COOKIE "cookie" -#define NM_OPENCONNECT_KEY_GWCERT "gwcert" -#define NM_OPENCONNECT_KEY_XMLCONFIG "xmlconfig" -#define NM_OPENCONNECT_KEY_LASTHOST "lasthost" -#define NM_OPENCONNECT_KEY_AUTOCONNECT "autoconnect" -#define NM_OPENCONNECT_KEY_CERTSIGS "certsigs" - -static void -openconnect_fix_secret_flags (NMSetting *setting) -{ - NMSettingVpn *s_vpn; - NMSettingSecretFlags flags; - - /* Huge hack. There were some openconnect changes that needed to happen - * pretty late, too late to get into distros. Migration has already - * happened for many people, and their secret flags are wrong. But we - * don't want to requrie re-migration, so we have to fix it up here. Ugh. - */ - - if (!NM_IS_SETTING_VPN (setting)) - return; - - s_vpn = NM_SETTING_VPN (setting); - - if (!nm_streq0 (nm_setting_vpn_get_service_type (s_vpn), NM_DBUS_SERVICE_OPENCONNECT)) - return; - - /* These are different for every login session, and should not be stored */ - flags = NM_SETTING_SECRET_FLAG_NOT_SAVED; - nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_GATEWAY, flags, NULL); - nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_COOKIE, flags, NULL); - nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_GWCERT, flags, NULL); - - /* These are purely internal data for the auth-dialog, and should be stored */ - flags = 0; - nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_XMLCONFIG, flags, NULL); - nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_LASTHOST, flags, NULL); - nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_AUTOCONNECT, flags, NULL); - nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_CERTSIGS, flags, NULL); -} - -/*****************************************************************************/ - -#define IP_ADDRESS_CHARS "0123456789abcdefABCDEF:.%" -#define DIGITS "0123456789" -#define DELIMITERS "/;," - -/* The following IPv4 and IPv6 address formats are supported: - * - * address (DEPRECATED) - * address/plen - * address/gateway (DEPRECATED) - * address/plen,gateway - * - * The following IPv4 and IPv6 route formats are supported: - * - * address/plen (NETWORK dev DEVICE) - * address/plen,gateway (NETWORK via GATEWAY dev DEVICE) - * address/plen,,metric (NETWORK dev DEVICE metric METRIC) - * address/plen,gateway,metric (NETWORK via GATEWAY dev DEVICE metric METRIC) - * - * For backward, forward and sideward compatibility, slash (/), - * semicolon (;) and comma (,) are interchangeable. The choice of - * separator in the above examples is therefore not significant. - * - * Leaving out the prefix length is discouraged and DEPRECATED. The - * default value of IPv6 prefix length was 64 and has not been - * changed. The default for IPv4 is now 24, which is the closest - * IPv4 equivalent. These defaults may just as well be changed to - * match the iproute2 defaults (32 for IPv4 and 128 for IPv6). - */ -static gpointer -read_one_ip_address_or_route (KeyfileReaderInfo *info, - const char *property_name, - const char *setting_name, - const char *key_name, - gboolean ipv6, - gboolean route, - char **out_gateway, - NMSetting *setting) -{ - guint plen; - gpointer result; - const char *address_str; - const char *plen_str; - const char *gateway_str; - const char *metric_str; - const char *err_str = NULL; - char *current; - gs_free char *value = NULL; - gs_free char *value_orig = NULL; - -#define VALUE_ORIG() (value_orig ?: (value_orig = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key_name, NULL))) - - value = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key_name, NULL); - if (!value) - return NULL; - - current = value; - - /* get address field */ - address_str = read_field (¤t, &err_str, IP_ADDRESS_CHARS, DELIMITERS); - if (err_str) { - handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN, - _("unexpected character '%c' for address %s: '%s' (position %td)"), - *err_str, key_name, VALUE_ORIG (), err_str - current); - return NULL; - } - /* get prefix length field (skippable) */ - plen_str = read_field (¤t, &err_str, DIGITS, DELIMITERS); - /* get gateway field */ - gateway_str = read_field (¤t, &err_str, IP_ADDRESS_CHARS, DELIMITERS); - if (err_str) { - handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN, - _("unexpected character '%c' for %s: '%s' (position %td)"), - *err_str, key_name, VALUE_ORIG (), err_str - current); - return NULL; - } - /* for routes, get metric */ - if (route) { - metric_str = read_field (¤t, &err_str, DIGITS, DELIMITERS); - if (err_str) { - handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN, - _("unexpected character '%c' in prefix length for %s: '%s' (position %td)"), - *err_str, key_name, VALUE_ORIG (), err_str - current); - return NULL; - } - } else - metric_str = NULL; - if (current) { - /* there is still some data */ - if (*current) { - /* another field follows */ - handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN, - _("garbage at the end of value %s: '%s'"), - key_name, VALUE_ORIG ()); - return NULL; - } else { - /* semicolon at the end of input */ - if (!handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_INFO, - _("deprecated semicolon at the end of value %s: '%s'"), - key_name, VALUE_ORIG ())) - return NULL; - } - } - -#define DEFAULT_PREFIX(for_route, for_ipv6) ( (for_route) ? ( (for_ipv6) ? 128 : 24 ) : ( (for_ipv6) ? 64 : 24 ) ) - - /* parse plen, fallback to defaults */ - if (plen_str) { - if (!get_one_int (info, property_name, plen_str, ipv6 ? 128 : 32, &plen)) { - plen = DEFAULT_PREFIX (route, ipv6); - if ( info->error - || !handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid prefix length for %s '%s', defaulting to %d"), - key_name, VALUE_ORIG (), plen)) - return NULL; - } - } else { - plen = DEFAULT_PREFIX (route, ipv6); - if (!handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN, - _("missing prefix length for %s '%s', defaulting to %d"), - key_name, VALUE_ORIG (), plen)) - return NULL; - } - - /* build the appropriate data structure for NetworkManager settings */ - if (route) { - result = build_route (info, - property_name, - ipv6 ? AF_INET6 : AF_INET, - address_str, - plen, - gateway_str, - metric_str); - } else { - result = build_address (info, - ipv6 ? AF_INET6 : AF_INET, - address_str, - plen, - property_name); - if (!result) - return NULL; - if (gateway_str) - NM_SET_OUT (out_gateway, g_strdup (gateway_str)); - } - -#undef VALUE_ORIG - - return result; -} - -static void -fill_route_attributes (GKeyFile *kf, NMIPRoute *route, const char *setting, const char *key, int family) -{ - gs_free char *value = NULL; - gs_unref_hashtable GHashTable *hash = NULL; - GHashTableIter iter; - char *name; - GVariant *variant; - - value = nm_keyfile_plugin_kf_get_string (kf, setting, key, NULL); - if (!value || !value[0]) - return; - - hash = nm_utils_parse_variant_attributes (value, ',', '=', TRUE, - nm_ip_route_get_variant_attribute_spec (), - NULL); - if (hash) { - g_hash_table_iter_init (&iter, hash); - while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &variant)) { - if (nm_ip_route_attribute_validate (name, variant, family, NULL, NULL)) - nm_ip_route_set_attribute (route, name, g_variant_ref (variant)); - } - } -} - -typedef struct { - const char *s_key; - gint32 key_idx; - gint8 key_type; -} BuildListData; - -typedef enum { - BUILD_LIST_TYPE_ADDRESSES, - BUILD_LIST_TYPE_ROUTES, - BUILD_LIST_TYPE_ROUTING_RULES, -} BuildListType; - -static int -_build_list_data_cmp (gconstpointer p_a, gconstpointer p_b, gpointer user_data) -{ - const BuildListData *a = p_a; - const BuildListData *b = p_b; - - NM_CMP_FIELD (a, b, key_idx); - NM_CMP_FIELD (a, b, key_type); - NM_CMP_FIELD_STR (a, b, s_key); - return 0; -} - -static gboolean -_build_list_data_is_shadowed (const BuildListData *build_list, - gsize build_list_len, - gsize idx) -{ - /* the keyfile contains duplicate keys, which are both returned - * by g_key_file_get_keys() (WHY??). - * - * Skip the earlier one. */ - return idx + 1 < build_list_len - && build_list[idx].key_idx == build_list[idx + 1].key_idx - && build_list[idx].key_type == build_list[idx + 1].key_type - && nm_streq (build_list[idx].s_key, build_list[idx + 1].s_key); -} - -static gboolean -_build_list_match_key_w_name_impl (const char *key, - const char *base_name, - gsize base_name_l, - gint32 *out_key_idx) -{ - gint64 v; - - /* some very strict parsing. */ - - /* the key must start with base_name. */ - if (strncmp (key, base_name, base_name_l) != 0) - return FALSE; - - key += base_name_l; - if (key[0] == '\0') { - /* if key is identical to base_name, that's good. */ - NM_SET_OUT (out_key_idx, -1); - return TRUE; - } - - /* if base_name is followed by a zero, then it must be - * only a zero, nothing else. */ - if (key[0] == '0') { - if (key[1] != '\0') - return FALSE; - NM_SET_OUT (out_key_idx, 0); - return TRUE; - } - - /* otherwise, it can only be followed by a non-zero decimal. */ - if (!(key[0] >= '1' && key[0] <= '9')) - return FALSE; - /* and all remaining chars must be decimals too. */ - if (!NM_STRCHAR_ALL (&key[1], ch, g_ascii_isdigit (ch))) - return FALSE; - - /* and it must be convertible to a (positive) int. */ - v = _nm_utils_ascii_str_to_int64 (key, 10, 0, G_MAXINT32, -1); - if (v < 0) - return FALSE; - - /* good */ - NM_SET_OUT (out_key_idx, v); - return TRUE; -} - -#define _build_list_match_key_w_name(key, base_name, out_key_idx) \ - _build_list_match_key_w_name_impl (key, base_name, NM_STRLEN (base_name), out_key_idx) - -static BuildListData * -_build_list_create (GKeyFile *keyfile, - const char *group_name, - BuildListType build_list_type, - gsize *out_build_list_len, - char ***out_keys_strv) -{ - gs_strfreev char **keys = NULL; - gsize i_keys, n_keys; - gs_free BuildListData *build_list = NULL; - gsize build_list_len = 0; - - nm_assert (out_build_list_len && *out_build_list_len == 0); - nm_assert (out_keys_strv && !*out_keys_strv); - - keys = nm_keyfile_plugin_kf_get_keys (keyfile, group_name, &n_keys, NULL); - if (n_keys == 0) - return NULL; - - for (i_keys = 0; i_keys < n_keys; i_keys++) { - const char *s_key = keys[i_keys]; - gint32 key_idx; - gint8 key_type = 0; - - switch (build_list_type) { - case BUILD_LIST_TYPE_ROUTES: - if (_build_list_match_key_w_name (s_key, "route", &key_idx)) - key_type = 0; - else if (_build_list_match_key_w_name (s_key, "routes", &key_idx)) - key_type = 1; - else - continue; - break; - case BUILD_LIST_TYPE_ADDRESSES: - if (_build_list_match_key_w_name (s_key, "address", &key_idx)) - key_type = 0; - else if (_build_list_match_key_w_name (s_key, "addresses", &key_idx)) - key_type = 1; - else - continue; - break; - case BUILD_LIST_TYPE_ROUTING_RULES: - if (_build_list_match_key_w_name (s_key, "routing-rule", &key_idx)) - key_type = 0; - else - continue; - break; - default: - nm_assert_not_reached (); - break; - } - - if (G_UNLIKELY (!build_list)) - build_list = g_new (BuildListData, n_keys - i_keys); - - build_list[build_list_len++] = (BuildListData) { - .s_key = s_key, - .key_idx = key_idx, - .key_type = key_type, - }; - } - - if (build_list_len == 0) - return NULL; - - if (build_list_len > 1) { - g_qsort_with_data (build_list, - build_list_len, - sizeof (BuildListData), - _build_list_data_cmp, - NULL); - } - - *out_build_list_len = build_list_len; - *out_keys_strv = g_steal_pointer (&keys); - return g_steal_pointer (&build_list); -} - -static void -ip_address_or_route_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *setting_key) -{ - const char *setting_name = nm_setting_get_name (setting); - gboolean is_ipv6 = nm_streq (setting_name, "ipv6"); - gboolean is_routes = nm_streq (setting_key, "routes"); - gs_free char *gateway = NULL; - gs_unref_ptrarray GPtrArray *list = NULL; - gs_strfreev char **keys = NULL; - gs_free BuildListData *build_list = NULL; - gsize i_build_list, build_list_len = 0; - - build_list = _build_list_create (info->keyfile, - setting_name, - is_routes - ? BUILD_LIST_TYPE_ROUTES - : BUILD_LIST_TYPE_ADDRESSES, - &build_list_len, - &keys); - if (!build_list) - return; - - list = g_ptr_array_new_with_free_func (is_routes - ? (GDestroyNotify) nm_ip_route_unref - : (GDestroyNotify) nm_ip_address_unref); - - for (i_build_list = 0; i_build_list < build_list_len; i_build_list++) { - const char *s_key; - gpointer item; - - if (_build_list_data_is_shadowed (build_list, build_list_len, i_build_list)) - continue; - - s_key = build_list[i_build_list].s_key; - item = read_one_ip_address_or_route (info, - setting_key, - setting_name, - s_key, - is_ipv6, - is_routes, - gateway ? NULL : &gateway, - setting); - if (item && is_routes) { - char options_key[128]; - - nm_sprintf_buf (options_key, "%s_options", s_key); - fill_route_attributes (info->keyfile, - item, - setting_name, - options_key, - is_ipv6 ? AF_INET6 : AF_INET); - } - - if (info->error) - return; - - if (item) - g_ptr_array_add (list, item); - } - - if (list->len >= 1) - g_object_set (setting, setting_key, list, NULL); - - if (gateway) - g_object_set (setting, "gateway", gateway, NULL); -} - -static void -ip_routing_rule_parser_full (KeyfileReaderInfo *info, - const NMMetaSettingInfo *setting_info, - const NMSettInfoProperty *property_info, - const ParseInfoProperty *pip, - NMSetting *setting) -{ - const char *setting_name = nm_setting_get_name (setting); - gboolean is_ipv6 = nm_streq (setting_name, "ipv6"); - gs_strfreev char **keys = NULL; - gs_free BuildListData *build_list = NULL; - gsize i_build_list, build_list_len = 0; - - build_list = _build_list_create (info->keyfile, - setting_name, - BUILD_LIST_TYPE_ROUTING_RULES, - &build_list_len, - &keys); - if (!build_list) - return; - - for (i_build_list = 0; i_build_list < build_list_len; i_build_list++) { - nm_auto_unref_ip_routing_rule NMIPRoutingRule *rule = NULL; - gs_free char *value = NULL; - gs_free_error GError *local = NULL; - - if (_build_list_data_is_shadowed (build_list, build_list_len, i_build_list)) - continue; - - value = nm_keyfile_plugin_kf_get_string (info->keyfile, - setting_name, - build_list[i_build_list].s_key, - NULL); - if (!value) - continue; - - rule = nm_ip_routing_rule_from_string (value, - ( NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE - | ( is_ipv6 - ? NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET6 - : NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET)), - NULL, - &local); - if (!rule) { - handle_warn (info, property_info->name, NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid value for \"%s\": %s"), - build_list[i_build_list].s_key, - local->message); - if (info->error) - return; - continue; - } - - nm_setting_ip_config_add_routing_rule (NM_SETTING_IP_CONFIG (setting), rule); - } -} - -static void -ip_dns_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) -{ - int addr_family; - gs_strfreev char **list = NULL; - gsize i, n, length; - - nm_assert (NM_IS_SETTING_IP4_CONFIG (setting) || NM_IS_SETTING_IP6_CONFIG (setting)); - - list = nm_keyfile_plugin_kf_get_string_list (info->keyfile, - nm_setting_get_name (setting), - key, - &length, - NULL); - nm_assert (length == NM_PTRARRAY_LEN (list)); - if (length == 0) - return; - - addr_family = NM_IS_SETTING_IP4_CONFIG (setting) ? AF_INET : AF_INET6; - - n = 0; - for (i = 0; i < length; i++) { - NMIPAddr addr; - - if (inet_pton (addr_family, list[i], &addr) <= 0) { - if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid DNS server IPv%c address '%s'"), - nm_utils_addr_family_to_char (addr_family), - list[i])) { - do { - nm_clear_g_free (&list[i]); - } while (++i < length); - return; - } - nm_clear_g_free (&list[i]); - continue; - } - - if (n != i) - list[n] = g_steal_pointer (&list[i]); - n++; - } - - g_object_set (setting, key, list, NULL); -} - -static void -ip6_addr_gen_mode_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) -{ - NMSettingIP6ConfigAddrGenMode addr_gen_mode; - const char *setting_name = nm_setting_get_name (setting); - gs_free char *s = NULL; - - s = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL); - if (s) { - if (!nm_utils_enum_from_str (nm_setting_ip6_config_addr_gen_mode_get_type (), s, - (int *) &addr_gen_mode, NULL)) { - handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid option '%s', use one of [%s]"), - s, "eui64,stable-privacy"); - return; - } - } else - addr_gen_mode = NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64; - - g_object_set (G_OBJECT (setting), key, (int) addr_gen_mode, NULL); -} - -static void -mac_address_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key, gsize addr_len, gboolean cloned_mac_addr) -{ - const char *setting_name = nm_setting_get_name (setting); - char addr_str[NM_UTILS_HWADDR_LEN_MAX * 3]; - guint8 addr_bin[NM_UTILS_HWADDR_LEN_MAX]; - gs_free char *tmp_string = NULL; - gs_free int *int_list = NULL; - const char *mac_str; - gsize int_list_len; - gsize i; - - nm_assert (addr_len > 0); - nm_assert (addr_len <= NM_UTILS_HWADDR_LEN_MAX); - - tmp_string = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL); - - if ( cloned_mac_addr - && NM_CLONED_MAC_IS_SPECIAL (tmp_string)) { - mac_str = tmp_string; - goto out; - } - - if ( tmp_string - && nm_utils_hwaddr_aton (tmp_string, addr_bin, addr_len)) - goto good_addr_bin; - - /* Old format; list of ints */ - int_list = nm_keyfile_plugin_kf_get_integer_list (info->keyfile, setting_name, key, &int_list_len, NULL); - if (int_list_len == addr_len) { - for (i = 0; i < addr_len; i++) { - const int val = int_list[i]; - - if (val < 0 || val > 255) - break; - addr_bin[i] = (guint8) val; - } - if (i == addr_len) - goto good_addr_bin; - } - - handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid MAC address")); - return; - -good_addr_bin: - nm_utils_bin2hexstr_full (addr_bin, addr_len, ':', TRUE, addr_str); - mac_str = addr_str; - -out: - g_object_set (setting, key, mac_str, NULL); -} - -static void -mac_address_parser_ETHER (KeyfileReaderInfo *info, NMSetting *setting, const char *key) -{ - mac_address_parser (info, setting, key, ETH_ALEN, FALSE); -} - -static void -mac_address_parser_ETHER_cloned (KeyfileReaderInfo *info, NMSetting *setting, const char *key) -{ - mac_address_parser (info, setting, key, ETH_ALEN, TRUE); -} - -static void -mac_address_parser_INFINIBAND (KeyfileReaderInfo *info, NMSetting *setting, const char *key) -{ - mac_address_parser (info, setting, key, INFINIBAND_ALEN, FALSE); -} - -static void -read_hash_of_string (GKeyFile *file, NMSetting *setting, const char *key) -{ - gs_strfreev char **keys = NULL; - const char *const*iter; - const char *setting_name = nm_setting_get_name (setting); - gboolean is_vpn; - gsize n_keys; - - nm_assert ( (NM_IS_SETTING_VPN (setting) && nm_streq (key, NM_SETTING_VPN_DATA)) - || (NM_IS_SETTING_VPN (setting) && nm_streq (key, NM_SETTING_VPN_SECRETS)) - || (NM_IS_SETTING_BOND (setting) && nm_streq (key, NM_SETTING_BOND_OPTIONS)) - || (NM_IS_SETTING_USER (setting) && nm_streq (key, NM_SETTING_USER_DATA))); - - keys = nm_keyfile_plugin_kf_get_keys (file, setting_name, &n_keys, NULL); - if (n_keys == 0) - return; - - if ( (is_vpn = NM_IS_SETTING_VPN (setting)) - || NM_IS_SETTING_BOND (setting)) { - for (iter = (const char *const*) keys; *iter; iter++) { - gs_free char *to_free = NULL; - gs_free char *value = NULL; - const char *name; - - value = nm_keyfile_plugin_kf_get_string (file, setting_name, *iter, NULL); - if (!value) - continue; - - name = nm_keyfile_key_decode (*iter, &to_free); - - if (is_vpn) { - /* Add any item that's not a class property to the data hash */ - if (!g_object_class_find_property (G_OBJECT_GET_CLASS (setting), name)) - nm_setting_vpn_add_data_item (NM_SETTING_VPN (setting), name, value); - } else { - if (!nm_streq (name, "interface-name")) - nm_setting_bond_add_option (NM_SETTING_BOND (setting), name, value); - } - } - openconnect_fix_secret_flags (setting); - return; - } - - if (NM_IS_SETTING_USER (setting)) { - gs_unref_hashtable GHashTable *data = NULL; - - data = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free); - for (iter = (const char *const*) keys; *iter; iter++) { - gs_free char *to_free = NULL; - char *value = NULL; - const char *name; - - value = nm_keyfile_plugin_kf_get_string (file, setting_name, *iter, NULL); - if (!value) - continue; - name = nm_keyfile_key_decode (*iter, &to_free); - g_hash_table_insert (data, - g_steal_pointer (&to_free) ?: g_strdup (name), - value); - } - g_object_set (setting, NM_SETTING_USER_DATA, data, NULL); - return; - } - - nm_assert_not_reached (); -} - -static gsize -unescape_semicolons (char *str) -{ - gsize i, j; - - for (i = 0, j = 0; str[i]; ) { - if (str[i] == '\\' && str[i+1] == ';') - i++; - str[j++] = str[i++];; - } - nm_explicit_bzero (&str[j], i - j); - return j; -} - -static GBytes * -get_bytes (KeyfileReaderInfo *info, - const char *setting_name, - const char *key, - gboolean zero_terminate, - gboolean unescape_semicolon) -{ - nm_auto_free_secret char *tmp_string = NULL; - gboolean may_be_int_list = TRUE; - gsize length; - GBytes *result; - - /* New format: just a string - * Old format: integer list; e.g. 11;25;38; - */ - tmp_string = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL); - if (!tmp_string) - return NULL; - - /* if the string is empty, we return an empty GBytes array. - * Note that for NM_SETTING_802_1X_PASSWORD_RAW both %NULL and - * an empty GBytes are valid, and shall be destinguished. */ - if (!tmp_string[0]) { - /* note that even if @zero_terminate is TRUE, we return an empty - * byte-array. The reason is that zero_terminate is there to terminate - * *valid* strings. It's not there to terminated invalid (empty) strings. - */ - return g_bytes_new_static ("", 0); - } - - for (length = 0; tmp_string[length]; length++) { - const char ch = tmp_string[length]; - - if ( !g_ascii_isspace (ch) - && !g_ascii_isdigit (ch) - && ch != ';') { - may_be_int_list = FALSE; - length += strlen (&tmp_string[length]); - break; - } - } - - /* Try to parse the string as a integer list. */ - if (may_be_int_list && length > 0) { - nm_auto_free_secret_buf NMSecretBuf *bin = NULL; - const char *const s = tmp_string; - gsize i, d; - - bin = nm_secret_buf_new (length / 2 + 3); - -#define DIGIT(c) ((c) - '0') - i = 0; - d = 0; - while (TRUE) { - int n; - - /* leading whitespace */ - while (g_ascii_isspace (s[i])) - i++; - if (s[i] == '\0') - break; - /* then expect 1 to 3 digits */ - if (!g_ascii_isdigit (s[i])) { - d = 0; - break; - } - n = DIGIT (s[i]); - i++; - if (g_ascii_isdigit (s[i])) { - n = 10 * n + DIGIT (s[i]); - i++; - if (g_ascii_isdigit (s[i])) { - n = 10 * n + DIGIT (s[i]); - i++; - } - } - if (n > 255) { - d = 0; - break; - } - - nm_assert (d < bin->len); - bin->bin[d++] = n; - - /* allow whitespace after the digit. */ - while (g_ascii_isspace (s[i])) - i++; - /* need a semicolon as separator. */ - if (s[i] != ';') { - d = 0; - break; - } - i++; - } -#undef DIGIT - - /* Old format; list of ints. We already did a strict validation of the - * string format before. We expect that this conversion cannot fail. */ - if (d > 0) { - /* note that @zero_terminate does not add a terminating '\0' to - * binary data as an integer list. If the bytes are expressed as - * an integer list, all potential NUL characters are supposed to - * be included there explicitly. - * - * However, in the spirit of defensive programming, we do append a - * NUL character to the buffer, although this character is hidden - * and only a mitigation for bugs. */ - - if (d + 10 < bin->len) { - /* hm, too much unused memory. Copy the memory to a suitable - * sized buffer. */ - return nm_secret_copy_to_gbytes (bin->bin, d); - } - - nm_assert (d < bin->len); - bin->bin[d] = '\0'; - return nm_secret_buf_to_gbytes_take (g_steal_pointer (&bin), d); - } - } - - /* Handle as a simple string (ie, new format) */ - if (unescape_semicolon) - length = unescape_semicolons (tmp_string); - if (zero_terminate) - length++; - if (length == 0) - return NULL; - - result = g_bytes_new_with_free_func (tmp_string, - length, - (GDestroyNotify) nm_free_secret, - tmp_string); - tmp_string = NULL; - return result; -} - -static void -ssid_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) -{ - const char *setting_name = nm_setting_get_name (setting); - GBytes *bytes; - - bytes = get_bytes (info, setting_name, key, FALSE, TRUE); - if (bytes) { - g_object_set (setting, key, bytes, NULL); - g_bytes_unref (bytes); - } else if (!info->error) { - handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid SSID")); - } -} - -static void -password_raw_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) -{ - const char *setting_name = nm_setting_get_name (setting); - GBytes *bytes; - - bytes = get_bytes (info, setting_name, key, FALSE, TRUE); - if (bytes) { - g_object_set (setting, key, bytes, NULL); - g_bytes_unref (bytes); - } else if (!info->error) { - handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid raw password")); - } -} - -static char * -get_cert_path (const char *base_dir, const guint8 *cert_path, gsize cert_path_len) -{ - const char *base; - char *p = NULL, *path, *tmp; - - g_return_val_if_fail (base_dir != NULL, NULL); - g_return_val_if_fail (cert_path != NULL, NULL); - - path = g_strndup ((char *) cert_path, cert_path_len); - - if (path[0] == '/') - return path; - - base = path; - p = strrchr (path, '/'); - if (p) - base = p + 1; - - tmp = g_build_path ("/", base_dir, base, NULL); - g_free (path); - return tmp; -} - -static const char *certext[] = { ".pem", ".cert", ".crt", ".cer", ".p12", ".der", ".key" }; - -static gboolean -has_cert_ext (const char *path) -{ - int i; - - for (i = 0; i < G_N_ELEMENTS (certext); i++) { - if (g_str_has_suffix (path, certext[i])) - return TRUE; - } - return FALSE; -} - -char * -nm_keyfile_detect_unqualified_path_scheme (const char *base_dir, - gconstpointer pdata, - gsize data_len, - gboolean consider_exists, - gboolean *out_exists) -{ - const char *data = pdata; - gboolean exists = FALSE; - gsize validate_len; - gsize path_len, pathuri_len; - gs_free char *path = NULL; - gs_free char *pathuri = NULL; - - g_return_val_if_fail (base_dir && base_dir[0] == '/', NULL); - - if (!pdata) - return NULL; - if (data_len == -1) - data_len = strlen (data); - if (data_len > 500 || data_len < 1) - return NULL; - - /* If there's a trailing zero tell g_utf8_validate() to validate until the zero */ - if (data[data_len - 1] == '\0') { - /* setting it to -1, would mean we accept data to contain NUL characters before the - * end. Don't accept any NUL in [0 .. data_len-1[ . */ - validate_len = data_len - 1; - } else - validate_len = data_len; - if ( validate_len == 0 - || g_utf8_validate ((const char *) data, validate_len, NULL) == FALSE) - return NULL; - - /* Might be a bare path without the file:// prefix; in that case - * if it's an absolute path, use that, otherwise treat it as a - * relative path to the current directory. - */ - - path = get_cert_path (base_dir, (const guint8 *) data, data_len); - - /* FIXME(keyfile-parse-in-memory): it is wrong that keyfile reader makes decisions based on - * the file systems content. The serialization/parsing should be entirely in-memory. */ - if ( !memchr (data, '/', data_len) - && !has_cert_ext (path)) { - if (!consider_exists) - return NULL; - exists = g_file_test (path, G_FILE_TEST_EXISTS); - if (!exists) - return NULL; - } else if (out_exists) - exists = g_file_test (path, G_FILE_TEST_EXISTS); - - /* Construct the proper value as required for the PATH scheme. - * - * When returning TRUE, we must also be sure that @data_len does not look like - * the deprecated format of list of integers. With this implementation that is the - * case, as long as @consider_exists is FALSE. */ - path_len = strlen (path); - pathuri_len = (NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH) + 1) + path_len; - pathuri = g_new (char, pathuri_len); - memcpy (pathuri, NM_KEYFILE_CERT_SCHEME_PREFIX_PATH, NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH)); - memcpy (&pathuri[NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH)], path, path_len + 1); - if (nm_setting_802_1x_check_cert_scheme (pathuri, pathuri_len, NULL) != NM_SETTING_802_1X_CK_SCHEME_PATH) - return NULL; - - NM_SET_OUT (out_exists, exists); - return g_steal_pointer (&pathuri); -} - -#define HAS_SCHEME_PREFIX(bin, bin_len, scheme) \ - ({ \ - const char *const _bin = (bin); \ - const gsize _bin_len = (bin_len); \ - \ - nm_assert (_bin && _bin_len > 0); \ - \ - ( _bin_len > NM_STRLEN (scheme) + 1 \ - && _bin[_bin_len - 1] == '\0' \ - && memcmp (_bin, scheme, NM_STRLEN (scheme)) == 0); \ - }) - -static void -cert_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) -{ - const char *setting_name = nm_setting_get_name (setting); - gs_unref_bytes GBytes *bytes = NULL; - const char *bin = NULL; - gsize bin_len = 0; - char *path; - gboolean path_exists; - - bytes = get_bytes (info, setting_name, key, TRUE, FALSE); - if (bytes) - bin = g_bytes_get_data (bytes, &bin_len); - if (bin_len == 0) { - if (!info->error) { - handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid key/cert value")); - } - return; - } - - if (HAS_SCHEME_PREFIX (bin, bin_len, NM_KEYFILE_CERT_SCHEME_PREFIX_PATH)) { - const char *path2 = &bin[NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH)]; - gs_free char *path2_free = NULL; - - if (nm_setting_802_1x_check_cert_scheme (bin, bin_len, NULL) != NM_SETTING_802_1X_CK_SCHEME_PATH) { - handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid key/cert value path \"%s\""), bin); - return; - } - - g_object_set (setting, key, bytes, NULL); - - if (path2[0] != '/') { - /* we want to read absolute paths because we use keyfile as exchange - * between different processes which might not have the same cwd. */ - path2_free = get_cert_path (info->base_dir, (const guint8 *) path2, - bin_len - NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH) - 1); - path2 = path2_free; - } - - /* FIXME(keyfile-parse-in-memory): keyfile reader must not access the file system and - * (in a first step) only operate in memory-only. If the presence of files should be checked, - * then by invoking a callback (and possibly keyfile settings plugin would - * collect the file names to be checked and check them later). */ - if (!g_file_test (path2, G_FILE_TEST_EXISTS)) { - handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_INFO_MISSING_FILE, - _("certificate or key file '%s' does not exist"), - path2); - } - return; - } - - if (HAS_SCHEME_PREFIX (bin, bin_len, NM_KEYFILE_CERT_SCHEME_PREFIX_PKCS11)) { - if (nm_setting_802_1x_check_cert_scheme (bin, bin_len, NULL) != NM_SETTING_802_1X_CK_SCHEME_PKCS11) { - handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid PKCS#11 URI \"%s\""), bin); - return; - } - - g_object_set (setting, key, bytes, NULL); - return; - } - - if (HAS_SCHEME_PREFIX (bin, bin_len, NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB)) { - const char *cdata = bin + NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB); - gsize cdata_len = bin_len - NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB) - 1; - gs_free guchar *bin_decoded = NULL; - gsize bin_decoded_len = 0; - gsize i; - gboolean valid_base64; - gs_unref_bytes GBytes *val = NULL; - - /* Let's be strict here. We expect valid base64, no funny stuff!! - * We didn't write such invalid data ourselfes and refuse to read it as blob. */ - if ((valid_base64 = (cdata_len % 4 == 0))) { - for (i = 0; i < cdata_len; i++) { - char c = cdata[i]; - - if (!( (c >= 'a' && c <= 'z') - || (c >= 'A' && c <= 'Z') - || (c >= '0' && c <= '9') - || (c == '+' || c == '/'))) { - if (c != '=' || i < cdata_len - 2) - valid_base64 = FALSE; - else { - for (; i < cdata_len; i++) { - if (cdata[i] != '=') - valid_base64 = FALSE; - } - } - break; - } - } - } - if (valid_base64) - bin_decoded = g_base64_decode (cdata, &bin_decoded_len); - - if (bin_decoded_len == 0) { - handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid key/cert value data:;base64, is not base64")); - return; - } - - if (nm_setting_802_1x_check_cert_scheme (bin_decoded, bin_decoded_len, NULL) != NM_SETTING_802_1X_CK_SCHEME_BLOB) { - /* The blob probably starts with "file://". Setting the cert data will confuse NMSetting8021x. - * In fact this is a limitation of NMSetting8021x which does not support setting blobs that start - * with file://. Just warn and return TRUE to signal that we ~handled~ the setting. */ - handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid key/cert value data:;base64,file://")); - return; - } - - val = g_bytes_new_take (g_steal_pointer (&bin_decoded), bin_decoded_len); - g_object_set (setting, key, val, NULL); - return; - } - - /* If not, it might be a plain path */ - path = nm_keyfile_detect_unqualified_path_scheme (info->base_dir, bin, bin_len, TRUE, &path_exists); - if (path) { - gs_unref_bytes GBytes *val = NULL; - - /* Construct the proper value as required for the PATH scheme */ - val = g_bytes_new_take (path, strlen (path) + 1); - g_object_set (setting, key, val, NULL); - - /* Warn if the certificate didn't exist */ - if (!path_exists) { - handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_INFO_MISSING_FILE, - _("certificate or key file '%s' does not exist"), - path); - } - return; - } - - if (nm_setting_802_1x_check_cert_scheme (bin, bin_len, NULL) != NM_SETTING_802_1X_CK_SCHEME_BLOB) { - /* The blob probably starts with "file://" but contains invalid characters for a path. - * Setting the cert data will confuse NMSetting8021x. - * In fact, NMSetting8021x does not support setting such binary data, so just warn and - * continue. */ - handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid key/cert value is not a valid blob")); - return; - } - - g_object_set (setting, key, bytes, NULL); -} - -static int -_parity_from_char (int ch) -{ -#if NM_MORE_ASSERTS > 5 - { - static char check = 0; - - if (check == 0) { - nm_auto_unref_gtypeclass GEnumClass *klass = g_type_class_ref (NM_TYPE_SETTING_SERIAL_PARITY); - guint i; - - check = 1; - - /* In older versions, parity was G_TYPE_CHAR/gint8, and the character - * value was stored as integer. - * For example parity=69 equals parity=E, meaning NM_SETTING_SERIAL_PARITY_EVEN. - * - * That means, certain values are reserved. Assert that these numbers - * are not reused when we extend NMSettingSerialParity enum. - * Actually, since NM_SETTING_SERIAL_PARITY is g_param_spec_enum(), - * we anyway cannot extend the enum without breaking API... - * - * [1] commit "a91e60902e libnm-core: make NMSettingSerial:parity an enum" - * [2] https://cgit.freedesktop.org/NetworkManager/NetworkManager/commit/?id=a91e60902eabae1de93d61323dae6ac894b5d40f - */ - g_assert (G_IS_ENUM_CLASS (klass)); - for (i = 0; i < klass->n_values; i++) { - const GEnumValue *v = &klass->values[i]; - int num = v->value; - - g_assert (_parity_from_char (num) == -1); - g_assert (!NM_IN_SET (num, 'e', 'E', 'o', 'O', 'n', 'N')); - } - } - } -#endif - - switch (ch) { - case 'E': - case 'e': - return NM_SETTING_SERIAL_PARITY_EVEN; - case 'O': - case 'o': - return NM_SETTING_SERIAL_PARITY_ODD; - case 'N': - case 'n': - return NM_SETTING_SERIAL_PARITY_NONE; - } - - return -1; -} - -static void -parity_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) -{ - const char *setting_name = nm_setting_get_name (setting); - gs_free_error GError *err = NULL; - int parity; - gs_free char *tmp_str = NULL; - gint64 i64; - - /* Keyfile traditionally stored this as the ASCII value for 'E', 'o', or 'n'. - * We now accept either that or the (case-insensitive) character itself (but - * still always write it the old way, for backward compatibility). - */ - tmp_str = nm_keyfile_plugin_kf_get_value (info->keyfile, setting_name, key, &err); - if (err) - goto out_err; - - if ( tmp_str - && tmp_str[0] != '\0' - && tmp_str[1] == '\0') { - /* the ASCII characters like 'E' are taken directly... */ - parity = _parity_from_char (tmp_str[0]); - if (parity >= 0) - goto parity_good; - } - - i64 = _nm_utils_ascii_str_to_int64 (tmp_str, 0, G_MININT, G_MAXINT, G_MININT64); - if ( i64 != G_MININT64 - && errno == 0) { - - if ((parity = _parity_from_char (i64)) >= 0) { - /* another oddity: the string is a valid number. However, if the numeric values - * is one of the supported ASCII codes, accept it (like 69 for 'E'). - */ - goto parity_good; - } - - /* Finally, take the numeric value as is. */ - parity = i64; - goto parity_good; - } - - handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid parity value '%s'"), - tmp_str ?: ""); - return; - -parity_good: - nm_g_object_set_property_enum (G_OBJECT (setting), key, NM_TYPE_SETTING_SERIAL_PARITY, parity, &err); - -out_err: - if (!err) - return; - if ( err->domain == G_KEY_FILE_ERROR - && NM_IN_SET (err->code, G_KEY_FILE_ERROR_GROUP_NOT_FOUND, - G_KEY_FILE_ERROR_KEY_NOT_FOUND)) { - /* ignore such errors. The key is not present. */ - return; - } - handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid setting: %s"), err->message); -} - -static void -team_config_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) -{ - const char *setting_name = nm_setting_get_name (setting); - gs_free char *conf = NULL; - gs_free_error GError *error = NULL; - - conf = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL); - - g_object_set (G_OBJECT (setting), key, conf, NULL); - - if ( conf - && !nm_setting_verify (setting, NULL, &error)) { - handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid team configuration: %s"), - error->message); - g_object_set (G_OBJECT (setting), key, NULL, NULL); - } -} - -static void -bridge_vlan_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) -{ - gs_unref_ptrarray GPtrArray *vlans = NULL; - gs_free char *value = NULL; - gs_free const char **strv = NULL; - const char *const *iter; - GError *local = NULL; - NMBridgeVlan *vlan; - - value = nm_keyfile_plugin_kf_get_string (info->keyfile, - nm_setting_get_name (setting), - key, - NULL); - if (!value || !value[0]) - return; - - vlans = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref); - - strv = nm_utils_escaped_tokens_split (value, ","); - if (strv) { - for (iter = strv; *iter; iter++) { - vlan = nm_bridge_vlan_from_str (*iter, &local); - if (!vlan) { - handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - "invalid bridge VLAN: %s", local->message); - g_clear_error (&local); - continue; - } - g_ptr_array_add (vlans, vlan); - } - } - - if (vlans->len > 0) - g_object_set (setting, key, vlans, NULL); -} - -static void -qdisc_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) -{ - const char *setting_name = nm_setting_get_name (setting); - gs_unref_ptrarray GPtrArray *qdiscs = NULL; - gs_strfreev char **keys = NULL; - gsize n_keys = 0; - int i; - - keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, setting_name, &n_keys, NULL); - if (n_keys == 0) - return; - - qdiscs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_tc_qdisc_unref); - - for (i = 0; i < n_keys; i++) { - NMTCQdisc *qdisc; - const char *qdisc_parent; - gs_free char *qdisc_rest = NULL; - gs_free char *qdisc_str = NULL; - gs_free_error GError *err = NULL; - - if (!g_str_has_prefix (keys[i], "qdisc.")) - continue; - - qdisc_parent = keys[i] + sizeof ("qdisc.") - 1; - qdisc_rest = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, keys[i], NULL); - qdisc_str = g_strdup_printf ("%s%s %s", - _nm_utils_parse_tc_handle (qdisc_parent, NULL) != TC_H_UNSPEC ? "parent " : "", - qdisc_parent, - qdisc_rest); - - qdisc = nm_utils_tc_qdisc_from_str (qdisc_str, &err); - if (!qdisc) { - handle_warn (info, keys[i], NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid qdisc: %s"), - err->message); - } else { - g_ptr_array_add (qdiscs, qdisc); - } - } - - if (qdiscs->len >= 1) - g_object_set (setting, key, qdiscs, NULL); -} - -static void -tfilter_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) -{ - const char *setting_name = nm_setting_get_name (setting); - gs_unref_ptrarray GPtrArray *tfilters = NULL; - gs_strfreev char **keys = NULL; - gsize n_keys = 0; - int i; - - keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, setting_name, &n_keys, NULL); - if (n_keys == 0) - return; - - tfilters = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_tc_tfilter_unref); - - for (i = 0; i < n_keys; i++) { - NMTCTfilter *tfilter; - const char *tfilter_parent; - gs_free char *tfilter_rest = NULL; - gs_free char *tfilter_str = NULL; - gs_free_error GError *err = NULL; - - if (!g_str_has_prefix (keys[i], "tfilter.")) - continue; - - tfilter_parent = keys[i] + sizeof ("tfilter.") - 1; - tfilter_rest = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, keys[i], NULL); - tfilter_str = g_strdup_printf ("%s%s %s", - _nm_utils_parse_tc_handle (tfilter_parent, NULL) != TC_H_UNSPEC ? "parent " : "", - tfilter_parent, - tfilter_rest); - - tfilter = nm_utils_tc_tfilter_from_str (tfilter_str, &err); - if (!tfilter) { - handle_warn (info, keys[i], NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid tfilter: %s"), - err->message); - } else { - g_ptr_array_add (tfilters, tfilter); - } - } - - if (tfilters->len >= 1) - g_object_set (setting, key, tfilters, NULL); -} - -/*****************************************************************************/ - -/* Some setting properties also contain setting names, such as - * NMSettingConnection's 'type' property (which specifies the base type of the - * connection, eg ethernet or wifi) or the 802-11-wireless setting's - * 'security' property which specifies whether or not the AP requires - * encryption. This function handles translating those properties' values - * from the real setting name to the more-readable alias. - */ -static void -setting_alias_writer (KeyfileWriterInfo *info, - NMSetting *setting, - const char *key, - const GValue *value) -{ - const char *str, *alias; - - str = g_value_get_string (value); - alias = nm_keyfile_plugin_get_alias_for_setting_name (str); - nm_keyfile_plugin_kf_set_string (info->keyfile, - nm_setting_get_name (setting), - key, - alias ?: str); -} - -static void -sriov_vfs_writer (KeyfileWriterInfo *info, - NMSetting *setting, - const char *key, - const GValue *value) -{ - GPtrArray *vfs; - guint i; - - vfs = g_value_get_boxed (value); - if (!vfs) - return; - - for (i = 0; i < vfs->len; i++) { - const NMSriovVF *vf = vfs->pdata[i]; - gs_free char *kf_value = NULL; - char kf_key[32]; - - kf_value = nm_utils_sriov_vf_to_str (vf, TRUE, NULL); - if (!kf_value) - continue; - - nm_sprintf_buf (kf_key, "vf.%u", nm_sriov_vf_get_index (vf)); - - nm_keyfile_plugin_kf_set_string (info->keyfile, - nm_setting_get_name (setting), - kf_key, - kf_value); - } -} - -static void -write_array_of_uint (GKeyFile *file, - NMSetting *setting, - const char *key, - const GValue *value) -{ - GArray *array; - guint i; - gs_free int *tmp_array = NULL; - - array = (GArray *) g_value_get_boxed (value); - if (!array || !array->len) - return; - - g_return_if_fail (g_array_get_element_size (array) == sizeof (guint)); - - tmp_array = g_new (int, array->len); - for (i = 0; i < array->len; i++) { - guint v = g_array_index (array, guint, i); - - if (v > G_MAXINT) - g_return_if_reached (); - tmp_array[i] = (int) v; - } - - nm_keyfile_plugin_kf_set_integer_list (file, nm_setting_get_name (setting), key, tmp_array, array->len); -} - -static void -dns_writer (KeyfileWriterInfo *info, - NMSetting *setting, - const char *key, - const GValue *value) -{ - char **list; - - list = g_value_get_boxed (value); - if (list && list[0]) { - nm_keyfile_plugin_kf_set_string_list (info->keyfile, nm_setting_get_name (setting), key, - (const char **) list, g_strv_length (list)); - } -} - -static void -ip6_addr_gen_mode_writer (KeyfileWriterInfo *info, - NMSetting *setting, - const char *key, - const GValue *value) -{ - NMSettingIP6ConfigAddrGenMode addr_gen_mode; - gs_free char *str = NULL; - - addr_gen_mode = (NMSettingIP6ConfigAddrGenMode) g_value_get_int (value); - str = nm_utils_enum_to_str (nm_setting_ip6_config_addr_gen_mode_get_type (), - addr_gen_mode); - nm_keyfile_plugin_kf_set_string (info->keyfile, - nm_setting_get_name (setting), - key, - str); -} - -static void -write_ip_values (GKeyFile *file, - const char *setting_name, - GPtrArray *array, - const char *gateway, - gboolean is_route) -{ - nm_auto_free_gstring GString *output = NULL; - int addr_family; - guint i; - const char *addr; - const char *gw; - guint32 plen; - char key_name[64]; - char *key_name_idx; - - if (!array->len) - return; - - addr_family = nm_streq (setting_name, NM_SETTING_IP4_CONFIG_SETTING_NAME) - ? AF_INET - : AF_INET6; - - strcpy (key_name, is_route ? "route" : "address"); - key_name_idx = key_name + strlen (key_name); - - output = g_string_sized_new (2*INET_ADDRSTRLEN + 10); - for (i = 0; i < array->len; i++) { - gint64 metric = -1; - - if (is_route) { - NMIPRoute *route = array->pdata[i]; - - addr = nm_ip_route_get_dest (route); - plen = nm_ip_route_get_prefix (route); - gw = nm_ip_route_get_next_hop (route); - metric = nm_ip_route_get_metric (route); - } else { - NMIPAddress *address = array->pdata[i]; - - addr = nm_ip_address_get_address (address); - plen = nm_ip_address_get_prefix (address); - gw = (i == 0) - ? gateway - : NULL; - } - - g_string_set_size (output, 0); - g_string_append_printf (output, "%s/%u", addr, plen); - if ( metric != -1 - || gw) { - /* Older versions of the plugin do not support the form - * "a.b.c.d/plen,,metric", so, we always have to write the - * gateway, even if there isn't one. - * The current version supports reading of the above form. - */ - if (!gw) { - if (addr_family == AF_INET) - gw = "0.0.0.0"; - else - gw = "::"; - } - - g_string_append_printf (output, ",%s", gw); - if ( is_route - && metric != -1) - g_string_append_printf (output, ",%lu", (unsigned long) metric); - } - - sprintf (key_name_idx, "%u", i + 1); - nm_keyfile_plugin_kf_set_string (file, setting_name, key_name, output->str); - - if (is_route) { - gs_free char *attributes = NULL; - - attributes = nm_utils_format_variant_attributes (_nm_ip_route_get_attributes (array->pdata[i]), - ',', '='); - if (attributes) { - g_strlcat (key_name, "_options", sizeof (key_name)); - nm_keyfile_plugin_kf_set_string (file, setting_name, key_name, attributes); - } - } - } -} - -static void -addr_writer (KeyfileWriterInfo *info, - NMSetting *setting, - const char *key, - const GValue *value) -{ - GPtrArray *array; - const char *setting_name = nm_setting_get_name (setting); - const char *gateway = nm_setting_ip_config_get_gateway (NM_SETTING_IP_CONFIG (setting)); - - array = (GPtrArray *) g_value_get_boxed (value); - if (array && array->len) - write_ip_values (info->keyfile, setting_name, array, gateway, FALSE); -} - -static void -route_writer (KeyfileWriterInfo *info, - NMSetting *setting, - const char *key, - const GValue *value) -{ - GPtrArray *array; - const char *setting_name = nm_setting_get_name (setting); - - array = (GPtrArray *) g_value_get_boxed (value); - if (array && array->len) - write_ip_values (info->keyfile, setting_name, array, NULL, TRUE); -} - -static void -bridge_vlan_writer (KeyfileWriterInfo *info, - NMSetting *setting, - const char *key, - const GValue *value) -{ - NMBridgeVlan *vlan; - GPtrArray *vlans; - GString *string; - guint i; - - vlans = (GPtrArray *) g_value_get_boxed (value); - if (!vlans || !vlans->len) - return; - - string = g_string_new (""); - for (i = 0; i < vlans->len; i++) { - gs_free char *vlan_str = NULL; - - vlan = vlans->pdata[i]; - vlan_str = nm_bridge_vlan_to_str (vlan, NULL); - if (!vlan_str) - continue; - if (string->len > 0) - g_string_append (string, ","); - nm_utils_escaped_tokens_escape_gstr_assert (vlan_str, ",", string); - } - - nm_keyfile_plugin_kf_set_string (info->keyfile, - nm_setting_get_name (setting), - "vlans", - string->str); - - g_string_free (string, TRUE); -} - - -#define ETHERNET_S390_OPTIONS_GROUP_NAME "ethernet-s390-options" - -static void -wired_s390_options_parser_full (KeyfileReaderInfo *info, - const NMMetaSettingInfo *setting_info, - const NMSettInfoProperty *property_info, - const ParseInfoProperty *pip, - NMSetting *setting) -{ - NMSettingWired *s_wired = NM_SETTING_WIRED (setting); - gs_strfreev char **keys = NULL; - gsize n_keys; - gsize i; - - keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, ETHERNET_S390_OPTIONS_GROUP_NAME, &n_keys, NULL); - for (i = 0; i < n_keys; i++) { - gs_free char *value = NULL; - gs_free char *key_to_free = NULL; - - value = nm_keyfile_plugin_kf_get_string (info->keyfile, - ETHERNET_S390_OPTIONS_GROUP_NAME, - keys[i], - NULL); - if (!value) - continue; - - nm_setting_wired_add_s390_option (s_wired, - nm_keyfile_key_decode (keys[i], - &key_to_free), - value); - } -} - -static void -wired_s390_options_writer_full (KeyfileWriterInfo *info, - const NMMetaSettingInfo *setting_info, - const NMSettInfoProperty *property_info, - const ParseInfoProperty *pip, - NMSetting *setting) -{ - NMSettingWired *s_wired = NM_SETTING_WIRED (setting); - guint i, n; - - n = nm_setting_wired_get_num_s390_options (s_wired); - for (i = 0; i < n; i++) { - const char *opt_key; - const char *opt_val; - gs_free char *key_to_free = NULL; - - nm_setting_wired_get_s390_option (s_wired, i, &opt_key, &opt_val); - nm_keyfile_plugin_kf_set_string (info->keyfile, - ETHERNET_S390_OPTIONS_GROUP_NAME, - nm_keyfile_key_encode (opt_key, &key_to_free), - opt_val); - } -} - -static void -ip_routing_rule_writer_full (KeyfileWriterInfo *info, - const NMMetaSettingInfo *setting_info, - const NMSettInfoProperty *property_info, - const ParseInfoProperty *pip, - NMSetting *setting) -{ - const char *setting_name = nm_setting_get_name (setting); - NMSettingIPConfig *s_ip = NM_SETTING_IP_CONFIG (setting); - guint i, j, n; - char key_name_full[100] = "routing-rule"; - char *key_name_num = &key_name_full[NM_STRLEN ("routing-rule")]; - - n = nm_setting_ip_config_get_num_routing_rules (s_ip); - j = 0; - for (i = 0; i < n; i++) { - NMIPRoutingRule *rule = nm_setting_ip_config_get_routing_rule (s_ip, i); - gs_free char *str = NULL; - - str = nm_ip_routing_rule_to_string (rule, - NM_IP_ROUTING_RULE_AS_STRING_FLAGS_NONE, - NULL, - NULL); - if (!str) - continue; - - sprintf (key_name_num, "%u", ++j); - nm_keyfile_plugin_kf_set_string (info->keyfile, - setting_name, - key_name_full, - str); - } -} - -static void -qdisc_writer (KeyfileWriterInfo *info, - NMSetting *setting, - const char *key, - const GValue *value) -{ - gsize i; - GPtrArray *array; - - array = (GPtrArray *) g_value_get_boxed (value); - if (!array || !array->len) - return; - - for (i = 0; i < array->len; i++) { - NMTCQdisc *qdisc = array->pdata[i]; - GString *key_name = g_string_sized_new (16); - GString *value_str = g_string_sized_new (60); - - g_string_append (key_name, "qdisc."); - _nm_utils_string_append_tc_parent (key_name, NULL, - nm_tc_qdisc_get_parent (qdisc)); - _nm_utils_string_append_tc_qdisc_rest (value_str, qdisc); - - nm_keyfile_plugin_kf_set_string (info->keyfile, - NM_SETTING_TC_CONFIG_SETTING_NAME, - key_name->str, - value_str->str); - - g_string_free (key_name, TRUE); - g_string_free (value_str, TRUE); - } -} - -static void -tfilter_writer (KeyfileWriterInfo *info, - NMSetting *setting, - const char *key, - const GValue *value) -{ - gsize i; - GPtrArray *array; - - array = (GPtrArray *) g_value_get_boxed (value); - if (!array || !array->len) - return; - - for (i = 0; i < array->len; i++) { - NMTCTfilter *tfilter = array->pdata[i]; - GString *key_name = g_string_sized_new (16); - GString *value_str = g_string_sized_new (60); - - g_string_append (key_name, "tfilter."); - _nm_utils_string_append_tc_parent (key_name, NULL, - nm_tc_tfilter_get_parent (tfilter)); - _nm_utils_string_append_tc_tfilter_rest (value_str, tfilter, NULL); - - nm_keyfile_plugin_kf_set_string (info->keyfile, - NM_SETTING_TC_CONFIG_SETTING_NAME, - key_name->str, - value_str->str); - - g_string_free (key_name, TRUE); - g_string_free (value_str, TRUE); - } -} - -static void -write_hash_of_string (GKeyFile *file, - NMSetting *setting, - const char *key, - const GValue *value) -{ - GHashTable *hash; - const char *group_name = nm_setting_get_name (setting); - gboolean vpn_secrets = FALSE; - gs_free const char **keys = NULL; - guint i, l; - - nm_assert ( (NM_IS_SETTING_VPN (setting) && nm_streq (key, NM_SETTING_VPN_DATA)) - || (NM_IS_SETTING_VPN (setting) && nm_streq (key, NM_SETTING_VPN_SECRETS)) - || (NM_IS_SETTING_BOND (setting) && nm_streq (key, NM_SETTING_BOND_OPTIONS)) - || (NM_IS_SETTING_USER (setting) && nm_streq (key, NM_SETTING_USER_DATA))); - - /* Write VPN secrets out to a different group to keep them separate */ - if ( NM_IS_SETTING_VPN (setting) - && nm_streq (key, NM_SETTING_VPN_SECRETS)) { - group_name = NM_KEYFILE_GROUP_VPN_SECRETS; - vpn_secrets = TRUE; - } - - hash = g_value_get_boxed (value); - - keys = nm_utils_strdict_get_keys (hash, TRUE, &l); - for (i = 0; i < l; i++) { - gs_free char *to_free = NULL; - const char *property, *data; - - property = keys[i]; - - /* Handle VPN secrets specially; they are nested in the property's hash; - * we don't want to write them if the secret is not saved, not required, - * or owned by a user's secret agent. - */ - if (vpn_secrets) { - NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE; - - if (!nm_setting_get_secret_flags (setting, property, &secret_flags, NULL)) - nm_assert_not_reached (); - if (!_secret_flags_persist_secret (secret_flags)) - continue; - } - - data = g_hash_table_lookup (hash, property); - nm_keyfile_plugin_kf_set_string (file, group_name, - nm_keyfile_key_encode (property, &to_free), - data); - } -} - -static void -ssid_writer (KeyfileWriterInfo *info, - NMSetting *setting, - const char *key, - const GValue *value) -{ - GBytes *bytes; - const guint8 *ssid_data; - gsize ssid_len; - const char *setting_name = nm_setting_get_name (setting); - gboolean new_format = TRUE; - gsize semicolons = 0; - gsize i; - - g_return_if_fail (G_VALUE_HOLDS (value, G_TYPE_BYTES)); - - bytes = g_value_get_boxed (value); - if (!bytes) - return; - ssid_data = g_bytes_get_data (bytes, &ssid_len); - if (!ssid_data || !ssid_len) { - nm_keyfile_plugin_kf_set_string (info->keyfile, setting_name, key, ""); - return; - } - - /* Check whether each byte is printable. If not, we have to use an - * integer list, otherwise we can just use a string. - */ - for (i = 0; i < ssid_len; i++) { - const char c = ssid_data[i]; - - if (!g_ascii_isprint (c)) { - new_format = FALSE; - break; - } - if (c == ';') - semicolons++; - } - - if (new_format) { - gs_free char *ssid = NULL; - - if (semicolons == 0) - ssid = g_strndup ((char *) ssid_data, ssid_len); - else { - /* Escape semicolons with backslashes to make strings - * containing ';', such as '16;17;' unambiguous */ - gsize j = 0; - - ssid = g_malloc (ssid_len + semicolons + 1); - for (i = 0; i < ssid_len; i++) { - if (ssid_data[i] == ';') - ssid[j++] = '\\'; - ssid[j++] = ssid_data[i]; - } - ssid[j] = '\0'; - } - nm_keyfile_plugin_kf_set_string (info->keyfile, setting_name, key, ssid); - } else - nm_keyfile_plugin_kf_set_integer_list_uint8 (info->keyfile, setting_name, key, ssid_data, ssid_len); -} - -static void -password_raw_writer (KeyfileWriterInfo *info, - NMSetting *setting, - const char *key, - const GValue *value) -{ - const char *setting_name = nm_setting_get_name (setting); - GBytes *array; - gsize len; - const guint8 *data; - - g_return_if_fail (G_VALUE_HOLDS (value, G_TYPE_BYTES)); - - array = (GBytes *) g_value_get_boxed (value); - if (!array) - return; - data = g_bytes_get_data (array, &len); - if (!data) - len = 0; - nm_keyfile_plugin_kf_set_integer_list_uint8 (info->keyfile, setting_name, key, data, len); -} - -/*****************************************************************************/ - -static void -cert_writer_default (NMConnection *connection, - GKeyFile *file, - NMKeyfileWriteTypeDataCert *cert_data) -{ - const char *setting_name = nm_setting_get_name (NM_SETTING (cert_data->setting)); - NMSetting8021xCKScheme scheme; - - scheme = cert_data->vtable->scheme_func (cert_data->setting); - if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) { - gs_free char *path_free = NULL; - gs_free char *base_dir = NULL; - gs_free char *tmp = NULL; - const char *path; - - path = cert_data->vtable->path_func (cert_data->setting); - g_assert (path); - - /* If the path is relative, make it an absolute path. - * Relative paths make a keyfile not easily usable in another - * context. */ - if (path[0] && path[0] != '/') { - base_dir = g_get_current_dir (); - path_free = g_strconcat (base_dir, "/", path, NULL); - path = path_free; - } else - base_dir = g_path_get_dirname (path); - - /* path cannot start with "file://" or "data:;base64,", because it is an absolute path. - * Still, make sure that a prefix-less path will be recognized. This can happen - * for example if the path is longer then 500 chars. */ - tmp = nm_keyfile_detect_unqualified_path_scheme (base_dir, path, -1, FALSE, NULL); - if (tmp) - nm_clear_g_free (&tmp); - else { - tmp = g_strconcat (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH, path, NULL); - path = tmp; - } - - /* Path contains at least a '/', hence it cannot be recognized as the old - * binary format consisting of a list of integers. */ - - nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->vtable->setting_key, path); - } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { - GBytes *blob; - const guint8 *blob_data; - gsize blob_len; - gs_free char *blob_base64 = NULL; - gs_free char *val = NULL; - - blob = cert_data->vtable->blob_func (cert_data->setting); - g_assert (blob); - blob_data = g_bytes_get_data (blob, &blob_len); - - blob_base64 = g_base64_encode (blob_data, blob_len); - val = g_strconcat (NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB, blob_base64, NULL); - - nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->vtable->setting_key, val); - } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) { - nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->vtable->setting_key, - cert_data->vtable->uri_func (cert_data->setting)); - } else { - /* scheme_func() returns UNKNOWN in all other cases. The only valid case - * where a scheme is allowed to be UNKNOWN, is unsetting the value. In this - * case, we don't expect the writer to be called, because the default value - * will not be serialized. - * The only other reason for the scheme to be UNKNOWN is an invalid cert. - * But our connection verifies, so that cannot happen either. */ - g_return_if_reached (); - } -} - -static void -cert_writer (KeyfileWriterInfo *info, - NMSetting *setting, - const char *key, - const GValue *value) -{ - const NMSetting8021xSchemeVtable *objtype = NULL; - guint i; - NMKeyfileWriteTypeDataCert type_data = { 0 }; - - for (i = 0; nm_setting_8021x_scheme_vtable[i].setting_key; i++) { - if (nm_streq0 (nm_setting_8021x_scheme_vtable[i].setting_key, key)) { - objtype = &nm_setting_8021x_scheme_vtable[i]; - break; - } - } - if (!objtype) - g_return_if_reached (); - - type_data.setting = NM_SETTING_802_1X (setting); - type_data.vtable = objtype; - - if (info->handler) { - if (info->handler (info->connection, - info->keyfile, - NM_KEYFILE_WRITE_TYPE_CERT, - &type_data, - info->user_data, - &info->error)) - return; - if (info->error) - return; - } - - cert_writer_default (info->connection, info->keyfile, &type_data); -} - -/*****************************************************************************/ - -struct _ParseInfoProperty { - const char *property_name; - union { - void (*parser) (KeyfileReaderInfo *info, - NMSetting *setting, - const char *key); - void (*parser_full) (KeyfileReaderInfo *info, - const NMMetaSettingInfo *setting_info, - const NMSettInfoProperty *property_info, - const ParseInfoProperty *pip, - NMSetting *setting); - }; - union { - void (*writer) (KeyfileWriterInfo *info, - NMSetting *setting, - const char *key, - const GValue *value); - void (*writer_full) (KeyfileWriterInfo *info, - const NMMetaSettingInfo *setting_info, - const NMSettInfoProperty *property_info, - const ParseInfoProperty *pip, - NMSetting *setting); - }; - bool parser_skip; - bool parser_no_check_key:1; - bool writer_skip:1; - bool has_writer_full:1; - bool has_parser_full:1; - - /* usually, we skip to write values that have their - * default value. By setting this flag to TRUE, also - * default values are written. */ - bool writer_persist_default:1; -}; - -#define PARSE_INFO_PROPERTY(_property_name, ...) \ - (&((const ParseInfoProperty) { \ - .property_name = _property_name, \ - __VA_ARGS__ \ - })) - -#define PARSE_INFO_PROPERTIES(...) \ - .properties = ((const ParseInfoProperty*const[]) { \ - __VA_ARGS__ \ - NULL, \ - }) - -typedef struct { - const ParseInfoProperty*const*properties; -} ParseInfoSetting; - -#define PARSE_INFO_SETTING(setting_type, ...) \ - [setting_type] = (&((const ParseInfoSetting) { \ - __VA_ARGS__ \ - })) - -static const ParseInfoSetting *const parse_infos[_NM_META_SETTING_TYPE_NUM] = { - PARSE_INFO_SETTING (NM_META_SETTING_TYPE_WIRELESS, - PARSE_INFO_PROPERTIES ( - PARSE_INFO_PROPERTY (NM_SETTING_WIRELESS_BSSID, - .parser = mac_address_parser_ETHER, - ), - PARSE_INFO_PROPERTY (NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, - .parser = mac_address_parser_ETHER_cloned, - ), - PARSE_INFO_PROPERTY (NM_SETTING_WIRELESS_MAC_ADDRESS, - .parser = mac_address_parser_ETHER, - ), - PARSE_INFO_PROPERTY (NM_SETTING_WIRELESS_SSID, - .parser = ssid_parser, - .writer = ssid_writer, - ), - ), - ), - PARSE_INFO_SETTING (NM_META_SETTING_TYPE_802_1X, - PARSE_INFO_PROPERTIES ( - PARSE_INFO_PROPERTY (NM_SETTING_802_1X_CA_CERT, - .parser = cert_parser, - .writer = cert_writer, - ), - PARSE_INFO_PROPERTY (NM_SETTING_802_1X_CLIENT_CERT, - .parser = cert_parser, - .writer = cert_writer, - ), - PARSE_INFO_PROPERTY (NM_SETTING_802_1X_PASSWORD_RAW, - .parser = password_raw_parser, - .writer = password_raw_writer, - ), - PARSE_INFO_PROPERTY (NM_SETTING_802_1X_PHASE2_CA_CERT, - .parser = cert_parser, - .writer = cert_writer, - ), - PARSE_INFO_PROPERTY (NM_SETTING_802_1X_PHASE2_CLIENT_CERT, - .parser = cert_parser, - .writer = cert_writer, - ), - PARSE_INFO_PROPERTY (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, - .parser = cert_parser, - .writer = cert_writer, - ), - PARSE_INFO_PROPERTY (NM_SETTING_802_1X_PRIVATE_KEY, - .parser = cert_parser, - .writer = cert_writer, - ), - ), - ), - PARSE_INFO_SETTING (NM_META_SETTING_TYPE_WIRED, - PARSE_INFO_PROPERTIES ( - PARSE_INFO_PROPERTY (NM_SETTING_WIRED_CLONED_MAC_ADDRESS, - .parser = mac_address_parser_ETHER_cloned, - ), - PARSE_INFO_PROPERTY (NM_SETTING_WIRED_MAC_ADDRESS, - .parser = mac_address_parser_ETHER, - ), - PARSE_INFO_PROPERTY (NM_SETTING_WIRED_S390_OPTIONS, - .parser_no_check_key = TRUE, - .parser_full = wired_s390_options_parser_full, - .writer_full = wired_s390_options_writer_full, - .has_parser_full = TRUE, - .has_writer_full = TRUE, - ), - ), - ), - PARSE_INFO_SETTING (NM_META_SETTING_TYPE_BLUETOOTH, - PARSE_INFO_PROPERTIES ( - PARSE_INFO_PROPERTY (NM_SETTING_BLUETOOTH_BDADDR, - .parser = mac_address_parser_ETHER, - ), - ), - ), - PARSE_INFO_SETTING (NM_META_SETTING_TYPE_BOND, - PARSE_INFO_PROPERTIES ( - PARSE_INFO_PROPERTY (NM_SETTING_BOND_OPTIONS, - .parser_no_check_key = TRUE, - ), - ), - ), - PARSE_INFO_SETTING (NM_META_SETTING_TYPE_BRIDGE, - PARSE_INFO_PROPERTIES ( - PARSE_INFO_PROPERTY (NM_SETTING_BRIDGE_MAC_ADDRESS, - .parser = mac_address_parser_ETHER, - ), - PARSE_INFO_PROPERTY (NM_SETTING_BRIDGE_VLANS, - .parser_no_check_key = TRUE, - .parser = bridge_vlan_parser, - .writer = bridge_vlan_writer, - ), - ), - ), - PARSE_INFO_SETTING (NM_META_SETTING_TYPE_BRIDGE_PORT, - PARSE_INFO_PROPERTIES ( - PARSE_INFO_PROPERTY (NM_SETTING_BRIDGE_PORT_VLANS, - .parser_no_check_key = TRUE, - .parser = bridge_vlan_parser, - .writer = bridge_vlan_writer, - ), - ), - ), - PARSE_INFO_SETTING (NM_META_SETTING_TYPE_CONNECTION, - PARSE_INFO_PROPERTIES ( - PARSE_INFO_PROPERTY (NM_SETTING_CONNECTION_READ_ONLY, - .parser_skip = TRUE, - .writer_skip = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_CONNECTION_TYPE, - .parser = setting_alias_parser, - .writer = setting_alias_writer, - ), - ), - ), - PARSE_INFO_SETTING (NM_META_SETTING_TYPE_INFINIBAND, - PARSE_INFO_PROPERTIES ( - PARSE_INFO_PROPERTY (NM_SETTING_INFINIBAND_MAC_ADDRESS, - .parser = mac_address_parser_INFINIBAND, - ), - ), - ), - PARSE_INFO_SETTING (NM_META_SETTING_TYPE_IP4_CONFIG, - PARSE_INFO_PROPERTIES ( - PARSE_INFO_PROPERTY (NM_SETTING_IP_CONFIG_ADDRESSES, - .parser_no_check_key = TRUE, - .parser = ip_address_or_route_parser, - .writer = addr_writer, - ), - PARSE_INFO_PROPERTY (NM_SETTING_IP_CONFIG_DNS, - .parser_no_check_key = TRUE, - .parser = ip_dns_parser, - .writer = dns_writer, - ), - PARSE_INFO_PROPERTY (NM_SETTING_IP_CONFIG_GATEWAY, - .writer_skip = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_IP_CONFIG_ROUTES, - .parser_no_check_key = TRUE, - .parser = ip_address_or_route_parser, - .writer = route_writer, - ), - PARSE_INFO_PROPERTY (NM_SETTING_IP_CONFIG_ROUTING_RULES, - .parser_no_check_key = TRUE, - .parser_full = ip_routing_rule_parser_full, - .writer_full = ip_routing_rule_writer_full, - .has_parser_full = TRUE, - .has_writer_full = TRUE, - ), - ), - ), - PARSE_INFO_SETTING (NM_META_SETTING_TYPE_IP6_CONFIG, - PARSE_INFO_PROPERTIES ( - PARSE_INFO_PROPERTY (NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE, - .parser_no_check_key = TRUE, - .parser = ip6_addr_gen_mode_parser, - .writer = ip6_addr_gen_mode_writer, - .writer_persist_default = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_IP_CONFIG_ADDRESSES, - .parser_no_check_key = TRUE, - .parser = ip_address_or_route_parser, - .writer = addr_writer, - ), - PARSE_INFO_PROPERTY (NM_SETTING_IP_CONFIG_DNS, - .parser_no_check_key = TRUE, - .parser = ip_dns_parser, - .writer = dns_writer, - ), - PARSE_INFO_PROPERTY (NM_SETTING_IP_CONFIG_GATEWAY, - .writer_skip = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_IP_CONFIG_ROUTES, - .parser_no_check_key = TRUE, - .parser = ip_address_or_route_parser, - .writer = route_writer, - ), - PARSE_INFO_PROPERTY (NM_SETTING_IP_CONFIG_ROUTING_RULES, - .parser_no_check_key = TRUE, - .parser_full = ip_routing_rule_parser_full, - .writer_full = ip_routing_rule_writer_full, - .has_parser_full = TRUE, - .has_writer_full = TRUE, - ), - ), - ), - PARSE_INFO_SETTING (NM_META_SETTING_TYPE_SERIAL, - PARSE_INFO_PROPERTIES ( - PARSE_INFO_PROPERTY (NM_SETTING_SERIAL_PARITY, - .parser = parity_parser, - ), - ), - ), - PARSE_INFO_SETTING (NM_META_SETTING_TYPE_SRIOV, - PARSE_INFO_PROPERTIES ( - PARSE_INFO_PROPERTY (NM_SETTING_SRIOV_VFS, - .parser_no_check_key = TRUE, - .parser = sriov_vfs_parser, - .writer = sriov_vfs_writer, - ), - ), - ), - PARSE_INFO_SETTING (NM_META_SETTING_TYPE_TC_CONFIG, - PARSE_INFO_PROPERTIES ( - PARSE_INFO_PROPERTY (NM_SETTING_TC_CONFIG_QDISCS, - .parser_no_check_key = TRUE, - .parser = qdisc_parser, - .writer = qdisc_writer, - ), - PARSE_INFO_PROPERTY (NM_SETTING_TC_CONFIG_TFILTERS, - .parser_no_check_key = TRUE, - .parser = tfilter_parser, - .writer = tfilter_writer, - ), - ), - ), - PARSE_INFO_SETTING (NM_META_SETTING_TYPE_TEAM, - PARSE_INFO_PROPERTIES ( - PARSE_INFO_PROPERTY (NM_SETTING_TEAM_CONFIG, - .parser = team_config_parser, - ), - PARSE_INFO_PROPERTY (NM_SETTING_TEAM_LINK_WATCHERS, - .parser_skip = TRUE, - .writer_skip = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_TEAM_MCAST_REJOIN_COUNT, - .parser_skip = TRUE, - .writer_skip = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_TEAM_MCAST_REJOIN_INTERVAL, - .parser_skip = TRUE, - .writer_skip = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_TEAM_NOTIFY_PEERS_COUNT, - .parser_skip = TRUE, - .writer_skip = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_TEAM_NOTIFY_PEERS_INTERVAL, - .parser_skip = TRUE, - .writer_skip = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_TEAM_RUNNER, - .parser_skip = TRUE, - .writer_skip = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_TEAM_RUNNER_ACTIVE, - .parser_skip = TRUE, - .writer_skip = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY, - .parser_skip = TRUE, - .writer_skip = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_TEAM_RUNNER_FAST_RATE, - .parser_skip = TRUE, - .writer_skip = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_TEAM_RUNNER_HWADDR_POLICY, - .parser_skip = TRUE, - .writer_skip = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_TEAM_RUNNER_MIN_PORTS, - .parser_skip = TRUE, - .writer_skip = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_TEAM_RUNNER_SYS_PRIO, - .parser_skip = TRUE, - .writer_skip = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_TEAM_RUNNER_TX_BALANCER, - .parser_skip = TRUE, - .writer_skip = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL, - .parser_skip = TRUE, - .writer_skip = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_TEAM_RUNNER_TX_HASH, - .parser_skip = TRUE, - .writer_skip = TRUE, - ), - ), - ), - PARSE_INFO_SETTING (NM_META_SETTING_TYPE_TEAM_PORT, - PARSE_INFO_PROPERTIES ( - PARSE_INFO_PROPERTY (NM_SETTING_TEAM_CONFIG, - .parser = team_config_parser, - ), - PARSE_INFO_PROPERTY (NM_SETTING_TEAM_PORT_LACP_KEY, - .parser_skip = TRUE, - .writer_skip = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_TEAM_PORT_LACP_PRIO, - .parser_skip = TRUE, - .writer_skip = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_TEAM_PORT_LINK_WATCHERS, - .parser_skip = TRUE, - .writer_skip = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_TEAM_PORT_PRIO, - .parser_skip = TRUE, - .writer_skip = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_TEAM_PORT_QUEUE_ID, - .parser_skip = TRUE, - .writer_skip = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_TEAM_PORT_STICKY, - .parser_skip = TRUE, - .writer_skip = TRUE, - ), - ), - ), - PARSE_INFO_SETTING (NM_META_SETTING_TYPE_USER, - PARSE_INFO_PROPERTIES ( - PARSE_INFO_PROPERTY (NM_SETTING_USER_DATA, - .parser_no_check_key = TRUE, - ), - ), - ), - PARSE_INFO_SETTING (NM_META_SETTING_TYPE_VLAN, - PARSE_INFO_PROPERTIES ( - PARSE_INFO_PROPERTY (NM_SETTING_VLAN_FLAGS, - .writer_persist_default = TRUE, - ), - ), - ), - PARSE_INFO_SETTING (NM_META_SETTING_TYPE_VPN, - PARSE_INFO_PROPERTIES ( - PARSE_INFO_PROPERTY (NM_SETTING_VPN_DATA, - .parser_no_check_key = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_VPN_PERSISTENT, - .parser_no_check_key = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_VPN_SECRETS, - .parser_no_check_key = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_VPN_SERVICE_TYPE, - .parser_no_check_key = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_VPN_TIMEOUT, - .parser_no_check_key = TRUE, - ), - PARSE_INFO_PROPERTY (NM_SETTING_VPN_USER_NAME, - .parser_no_check_key = TRUE, - ), - ), - ), - PARSE_INFO_SETTING (NM_META_SETTING_TYPE_WIMAX, - PARSE_INFO_PROPERTIES ( - PARSE_INFO_PROPERTY (NM_SETTING_WIMAX_MAC_ADDRESS, - .parser = mac_address_parser_ETHER, - ), - ), - ), -}; - -static void -_parse_info_find (NMSetting *setting, - const char *property_name, - const NMMetaSettingInfo **out_setting_info, - const ParseInfoSetting **out_parse_info_setting, - const ParseInfoProperty **out_parse_info_property) -{ - const NMMetaSettingInfo *setting_info; - const ParseInfoSetting *pis; - const ParseInfoProperty *pip; - -#if NM_MORE_ASSERTS > 10 - { - guint i, j; - static int asserted = FALSE; - - if (!asserted) { - for (i = 0; i < G_N_ELEMENTS (parse_infos); i++) { - pis = parse_infos[i]; - - if (!pis) - continue; - if (!pis->properties) - continue; - - g_assert (pis->properties[0]); - for (j = 0; pis->properties[j]; j++) { - const ParseInfoProperty *pip0; - const ParseInfoProperty *pipj = pis->properties[j]; - - g_assert (pipj->property_name); - if ( j > 0 - && (pip0 = pis->properties[j - 1]) - && strcmp (pip0->property_name, pipj->property_name) >= 0) { - g_error ("Wrong order at index #%d.%d: \"%s.%s\" before \"%s.%s\"", - i, j - 1, - nm_meta_setting_infos[i].setting_name, pip0->property_name, - nm_meta_setting_infos[i].setting_name, pipj->property_name); - } - } - } - asserted = TRUE; - } - } -#endif - - if ( !NM_IS_SETTING (setting) - || !(setting_info = NM_SETTING_GET_CLASS (setting)->setting_info)) { - /* handle invalid setting objects gracefully. */ - NM_SET_OUT (out_setting_info, NULL); - NM_SET_OUT (out_parse_info_setting, NULL); - NM_SET_OUT (out_parse_info_property, NULL); - return; - } - - nm_assert (setting_info->setting_name); - nm_assert (_NM_INT_NOT_NEGATIVE (setting_info->meta_type)); - nm_assert (setting_info->meta_type < G_N_ELEMENTS (parse_infos)); - - pis = parse_infos[setting_info->meta_type]; - - pip = NULL; - if ( pis - && property_name) { - gssize idx; - - G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (ParseInfoProperty, property_name) == 0); - - idx = nm_utils_ptrarray_find_binary_search ((gconstpointer *) pis->properties, - NM_PTRARRAY_LEN (pis->properties), - &property_name, - nm_strcmp_p_with_data, - NULL, - NULL, - NULL); - if (idx >= 0) - pip = pis->properties[idx]; - } - - NM_SET_OUT (out_setting_info, setting_info); - NM_SET_OUT (out_parse_info_setting, pis); - NM_SET_OUT (out_parse_info_property, pip); -} - -/*****************************************************************************/ - -static void -read_one_setting_value (KeyfileReaderInfo *info, - NMSetting *setting, - const NMSettInfoProperty *property_info) -{ - GKeyFile *keyfile = info->keyfile; - gs_free_error GError *err = NULL; - const NMMetaSettingInfo *setting_info; - const ParseInfoProperty *pip; - gs_free char *tmp_str = NULL; - const char *key; - GType type; - guint64 u64; - gint64 i64; - - nm_assert (!info->error); - nm_assert ( !property_info->param_spec - || nm_streq (property_info->param_spec->name, property_info->name)); - - key = property_info->name; - - _parse_info_find (setting, key, &setting_info, NULL, &pip); - - nm_assert (setting_info); - - if (!pip) { - if (nm_streq (key, NM_SETTING_NAME)) - return; - if (!property_info->param_spec) - return; - if ((property_info->param_spec->flags & (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)) != G_PARAM_WRITABLE) - return; - } else { - if (pip->parser_skip) - return; - if (pip->has_parser_full) { - pip->parser_full (info, setting_info, property_info, pip, setting); - return; - } - } - - nm_assert (property_info->param_spec); - nm_assert ((property_info->param_spec->flags & (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)) == G_PARAM_WRITABLE); - - /* Check for the exact key in the GKeyFile if required. Most setting - * properties map 1:1 to a key in the GKeyFile, but for those properties - * like IP addresses and routes where more than one value is actually - * encoded by the setting property, this won't be true. - */ - if ( (!pip || !pip->parser_no_check_key) - && !nm_keyfile_plugin_kf_has_key (keyfile, setting_info->setting_name, key, &err)) { - /* Key doesn't exist or an error occurred, thus nothing to do. */ - if (err) { - if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("error loading setting value: %s"), - err->message)) - return; - } - return; - } - - if ( pip - && pip->parser) { - pip->parser (info, setting, key); - return; - } - - type = G_PARAM_SPEC_VALUE_TYPE (property_info->param_spec); - - if (type == G_TYPE_STRING) { - gs_free char *str_val = NULL; - - str_val = nm_keyfile_plugin_kf_get_string (keyfile, setting_info->setting_name, key, &err); - if (!err) - nm_g_object_set_property_string_take (G_OBJECT (setting), key, g_steal_pointer (&str_val), &err); - } else if (type == G_TYPE_UINT) { - tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_info->setting_name, key, &err); - if (!err) { - u64 = _nm_utils_ascii_str_to_uint64 (tmp_str, 0, 0, G_MAXUINT, G_MAXUINT64); - if ( u64 == G_MAXUINT64 - && errno != 0) { - g_set_error_literal (&err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE, - _("value cannot be interpreted as integer")); - } else - nm_g_object_set_property_uint (G_OBJECT (setting), key, u64, &err); - } - } else if (type == G_TYPE_INT) { - tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_info->setting_name, key, &err); - if (!err) { - i64 = _nm_utils_ascii_str_to_int64 (tmp_str, 0, G_MININT, G_MAXINT, G_MININT64); - if ( i64 == G_MININT64 - && errno != 0) { - g_set_error_literal (&err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE, - _("value cannot be interpreted as integer")); - } else - nm_g_object_set_property_int (G_OBJECT (setting), key, i64, &err); - } - } else if (type == G_TYPE_BOOLEAN) { - gboolean bool_val; - - bool_val = nm_keyfile_plugin_kf_get_boolean (keyfile, setting_info->setting_name, key, &err); - if (!err) - nm_g_object_set_property_boolean (G_OBJECT (setting), key, bool_val, &err); - } else if (type == G_TYPE_CHAR) { - tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_info->setting_name, key, &err); - if (!err) { - /* As documented by glib, G_TYPE_CHAR is really a (signed!) gint8. */ - i64 = _nm_utils_ascii_str_to_int64 (tmp_str, 0, G_MININT8, G_MAXINT8, G_MININT64); - if ( i64 == G_MININT64 - && errno != 0) { - g_set_error_literal (&err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE, - _("value cannot be interpreted as integer")); - } else - nm_g_object_set_property_char (G_OBJECT (setting), key, i64, &err); - } - } else if (type == G_TYPE_UINT64) { - tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_info->setting_name, key, &err); - if (!err) { - u64 = _nm_utils_ascii_str_to_uint64 (tmp_str, 0, 0, G_MAXUINT64, G_MAXUINT64); - if ( u64 == G_MAXUINT64 - && errno != 0) { - g_set_error_literal (&err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE, - _("value cannot be interpreted as integer")); - } else - nm_g_object_set_property_uint64 (G_OBJECT (setting), key, u64, &err); - } - } else if (type == G_TYPE_INT64) { - tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_info->setting_name, key, &err); - if (!err) { - i64 = _nm_utils_ascii_str_to_int64 (tmp_str, 0, G_MININT64, G_MAXINT64, G_MAXINT64); - if ( i64 == G_MAXINT64 - && errno != 0) { - g_set_error_literal (&err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE, - _("value cannot be interpreted as integer")); - } else - nm_g_object_set_property_int64 (G_OBJECT (setting), key, i64, &err); - } - } else if (type == G_TYPE_BYTES) { - gs_free int *tmp = NULL; - GByteArray *array; - GBytes *bytes; - gsize length; - int i; - gboolean already_warned = FALSE; - - tmp = nm_keyfile_plugin_kf_get_integer_list (keyfile, setting_info->setting_name, key, &length, NULL); - - array = g_byte_array_sized_new (length); - for (i = 0; i < length; i++) { - const int val = tmp[i]; - unsigned char v = (unsigned char) (val & 0xFF); - - if (val < 0 || val > 255) { - if ( !already_warned - && !handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid byte element '%d' (not between 0 and 255 inclusive)"), - val)) { - g_byte_array_unref (array); - return; - } - already_warned = TRUE; - } else - g_byte_array_append (array, (const unsigned char *) &v, sizeof (v)); - } - - bytes = g_byte_array_free_to_bytes (array); - g_object_set (setting, key, bytes, NULL); - g_bytes_unref (bytes); - } else if (type == G_TYPE_STRV) { - gs_strfreev char **sa = NULL; - gsize length; - - sa = nm_keyfile_plugin_kf_get_string_list (keyfile, setting_info->setting_name, key, &length, NULL); - g_object_set (setting, key, sa, NULL); - } else if (type == G_TYPE_HASH_TABLE) { - read_hash_of_string (keyfile, setting, key); - } else if (type == G_TYPE_ARRAY) { - read_array_of_uint (keyfile, setting, key); - } else if (G_TYPE_IS_FLAGS (type)) { - tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_info->setting_name, key, &err); - if (!err) { - u64 = _nm_utils_ascii_str_to_uint64 (tmp_str, 0, 0, G_MAXUINT, G_MAXUINT64); - if ( u64 == G_MAXUINT64 - && errno != 0) { - g_set_error_literal (&err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE, - _("value cannot be interpreted as integer")); - } else - nm_g_object_set_property_flags (G_OBJECT (setting), key, type, u64, &err); - } - } else if (G_TYPE_IS_ENUM (type)) { - tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_info->setting_name, key, &err); - if (!err) { - i64 = _nm_utils_ascii_str_to_int64 (tmp_str, 0, G_MININT, G_MAXINT, G_MAXINT64); - if ( i64 == G_MAXINT64 - && errno != 0) { - g_set_error_literal (&err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE, - _("value cannot be interpreted as integer")); - } else - nm_g_object_set_property_enum (G_OBJECT (setting), key, type, i64, &err); - } - } else - g_return_if_reached (); - - if (err) { - if ( err->domain == G_KEY_FILE_ERROR - && NM_IN_SET (err->code, G_KEY_FILE_ERROR_GROUP_NOT_FOUND, - G_KEY_FILE_ERROR_KEY_NOT_FOUND)) { - /* ignore such errors. The key is not present. */ - } else { - handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid setting: %s"), err->message); - } - } -} - -static void -_read_setting (KeyfileReaderInfo *info) -{ - const NMSettInfoSetting *sett_info; - gs_unref_object NMSetting *setting = NULL; - const char *alias; - GType type; - guint i; - - alias = nm_keyfile_plugin_get_setting_name_for_alias (info->group); - if (!alias) - alias = info->group; - - type = nm_setting_lookup_type (alias); - if (!type) { - handle_warn (info, NULL, NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid setting name '%s'"), info->group); - return; - } - - setting = g_object_new (type, NULL); - - info->setting = setting; - - sett_info = _nm_setting_class_get_sett_info (NM_SETTING_GET_CLASS (setting)); - - if (sett_info->detail.gendata_info) { - gs_free char **keys = NULL; - gsize k, n_keys; - - keys = g_key_file_get_keys (info->keyfile, info->group, &n_keys, NULL); - if (!keys) - n_keys = 0; - if (n_keys > 0) { - GHashTable *h = _nm_setting_gendata_hash (setting, TRUE); - - nm_utils_strv_sort (keys, n_keys); - for (k = 0; k < n_keys; k++) { - gs_free char *key = keys[k]; - gs_free_error GError *local = NULL; - const GVariantType *variant_type; - GVariant *variant; - - /* a GKeyFile can return duplicate keys, there is just no API to make sense - * of them. Skip them. */ - if ( k + 1 < n_keys - && nm_streq (key, keys[k + 1])) - continue; - - /* currently, the API is very simple. The setting class just returns - * the desired variant type, and keyfile reader will try to parse - * it accordingly. Note, that this does currently not allow, that - * a particular key can contain different variant types, nor is it - * very flexible in general. - * - * We add flexibility when we need it. Keep it simple for now. */ - variant_type = sett_info->detail.gendata_info->get_variant_type (sett_info, - key, - &local); - if (!variant_type) { - if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid key '%s.%s'"), - info->group, key)) - break; - continue; - } - - if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_BOOLEAN)) { - gboolean v; - - v = g_key_file_get_boolean (info->keyfile, - info->group, - key, - &local); - if (local) { - if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("key '%s.%s' is not boolean"), - info->group, key)) - break; - continue; - } - variant = g_variant_new_boolean (v); - } else { - nm_assert_not_reached (); - continue; - } - - g_hash_table_insert (h, - g_steal_pointer (&key), - g_variant_take_ref (variant)); - } - for (; k < n_keys; k++) - g_free (keys[k]); - } - } - - for (i = 0; i < sett_info->property_infos_len; i++) { - read_one_setting_value (info, - setting, - &sett_info->property_infos[i]); - if (info->error) - goto out; - } - -out: - info->setting = NULL; - if (!info->error) - nm_connection_add_setting (info->connection, g_steal_pointer (&setting)); -} - -static void -_read_setting_wireguard_peer (KeyfileReaderInfo *info) -{ - gs_unref_object NMSettingWireGuard *s_wg_new = NULL; - nm_auto_unref_wgpeer NMWireGuardPeer *peer = NULL; - gs_free_error GError *error = NULL; - NMSettingWireGuard *s_wg; - gs_free char *str = NULL; - const char *cstr = NULL; - const char *key; - gint64 i64; - gs_strfreev char **sa = NULL; - gsize n_sa; - - peer = nm_wireguard_peer_new (); - - nm_assert (g_str_has_prefix (info->group, NM_KEYFILE_GROUPPREFIX_WIREGUARD_PEER)); - cstr = &info->group[NM_STRLEN (NM_KEYFILE_GROUPPREFIX_WIREGUARD_PEER)]; - if ( !nm_utils_base64secret_normalize (cstr, NM_WIREGUARD_PUBLIC_KEY_LEN, &str) - || !nm_streq0 (str, cstr)) { - /* the group name must be identical to the normalized(!) key, so that it - * is uniquely identified. */ - handle_warn (info, NULL, NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid peer public key in section '%s'"), - info->group); - return; - } - nm_wireguard_peer_set_public_key (peer, cstr, TRUE); - nm_clear_g_free (&str); - - key = NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY; - str = nm_keyfile_plugin_kf_get_string (info->keyfile, info->group, key, NULL); - if (str) { - if (!nm_wireguard_peer_set_preshared_key (peer, str, FALSE)) { - if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("key '%s.%s' is not a valid 256 bit key in base64 encoding"), - info->group, key)) - return; - } - nm_clear_g_free (&str); - } - - key = NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY_FLAGS; - i64 = nm_keyfile_plugin_kf_get_int64 (info->keyfile, info->group, key, 0, 0, NM_SETTING_SECRET_FLAG_ALL, -1, NULL); - if (errno != ENODATA) { - if ( i64 == -1 - || !_nm_setting_secret_flags_valid (i64)) { - if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("key '%s.%s' is not a valid secret flag"), - info->group, key)) - return; - } else - nm_wireguard_peer_set_preshared_key_flags (peer, i64); - } - - key = NM_WIREGUARD_PEER_ATTR_PERSISTENT_KEEPALIVE; - i64 = nm_keyfile_plugin_kf_get_int64 (info->keyfile, info->group, key, 0, 0, G_MAXUINT32, -1, NULL); - if (errno != ENODATA) { - if (i64 == -1) { - if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("key '%s.%s' is not a integer in range 0 to 2^32"), - info->group, key)) - return; - } else - nm_wireguard_peer_set_persistent_keepalive (peer, i64); - } - - key = NM_WIREGUARD_PEER_ATTR_ENDPOINT; - str = nm_keyfile_plugin_kf_get_string (info->keyfile, info->group, key, NULL); - if (str && str[0]) { - if (!nm_wireguard_peer_set_endpoint (peer, str, FALSE)) { - if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("key '%s.%s' is not a valid endpoint"), - info->group, key)) - return; - } - } - nm_clear_g_free (&str); - - key = NM_WIREGUARD_PEER_ATTR_ALLOWED_IPS; - sa = nm_keyfile_plugin_kf_get_string_list (info->keyfile, info->group, key, &n_sa, NULL); - if (n_sa > 0) { - gboolean has_error = FALSE; - gsize i; - - for (i = 0; i < n_sa; i++) { - if (!nm_utils_parse_inaddr_prefix_bin (AF_UNSPEC, sa[i], NULL, NULL, NULL)) { - has_error = TRUE; - continue; - } - nm_wireguard_peer_append_allowed_ip (peer, sa[i], TRUE); - } - if (has_error) { - if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("key '%s.%s' has invalid allowed-ips"), - info->group, key)) - return; - } - } - nm_clear_pointer (&sa, g_strfreev); - - if (info->error) - return; - - if (!nm_wireguard_peer_is_valid (peer, TRUE, TRUE, &error)) { - handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("peer '%s' is invalid: %s"), - info->group, error->message); - return; - } - - s_wg = NM_SETTING_WIREGUARD (nm_connection_get_setting (info->connection, NM_TYPE_SETTING_WIREGUARD)); - if (!s_wg) { - s_wg_new = NM_SETTING_WIREGUARD (nm_setting_wireguard_new ()); - s_wg = s_wg_new; - } - - nm_setting_wireguard_append_peer (s_wg, peer); - - if (s_wg_new) { - nm_connection_add_setting (info->connection, - NM_SETTING (g_steal_pointer (&s_wg_new))); - } -} - -static void -_read_setting_vpn_secrets (KeyfileReaderInfo *info) -{ - gs_strfreev char **keys = NULL; - gsize i, n_keys; - NMSettingVpn *s_vpn; - - s_vpn = nm_connection_get_setting_vpn (info->connection); - if (!s_vpn) { - /* if we don't also have a [vpn] section (which must be parsed earlier), - * we don't do anything. */ - nm_assert (!g_key_file_has_group (info->keyfile, "vpn")); - return; - } - - keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, NM_KEYFILE_GROUP_VPN_SECRETS, &n_keys, NULL); - for (i = 0; i < n_keys; i++) { - gs_free char *secret = NULL; - - secret = nm_keyfile_plugin_kf_get_string (info->keyfile, NM_KEYFILE_GROUP_VPN_SECRETS, keys[i], NULL); - if (secret) - nm_setting_vpn_add_secret (s_vpn, keys[i], secret); - } -} - -gboolean -nm_keyfile_read_ensure_id (NMConnection *connection, - const char *fallback_id) -{ - NMSettingConnection *s_con; - - g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); - g_return_val_if_fail (fallback_id, FALSE); - - s_con = nm_connection_get_setting_connection (connection); - g_return_val_if_fail (NM_IS_SETTING_CONNECTION (s_con), FALSE); - - if (nm_setting_connection_get_id (s_con)) - return FALSE; - - g_object_set (s_con, NM_SETTING_CONNECTION_ID, fallback_id, NULL); - return TRUE; -} - -gboolean -nm_keyfile_read_ensure_uuid (NMConnection *connection, - const char *fallback_uuid_seed) -{ - NMSettingConnection *s_con; - gs_free char *hashed_uuid = NULL; - - g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); - g_return_val_if_fail (fallback_uuid_seed, FALSE); - - s_con = nm_connection_get_setting_connection (connection); - g_return_val_if_fail (NM_IS_SETTING_CONNECTION (s_con), FALSE); - - if (nm_setting_connection_get_uuid (s_con)) - return FALSE; - - hashed_uuid = _nm_utils_uuid_generate_from_strings ("keyfile", fallback_uuid_seed, NULL); - g_object_set (s_con, NM_SETTING_CONNECTION_UUID, hashed_uuid, NULL); - return TRUE; -} - -/** - * nm_keyfile_read: - * @keyfile: the keyfile from which to create the connection - * @base_dir: when reading certificates from files with relative name, - * the relative path is made absolute using @base_dir. This must - * be an absolute path. - * @handler: read handler - * @user_data: user data for read handler - * @error: error - * - * Tries to create a NMConnection from a keyfile. The resulting keyfile is - * not normalized and might not even verify. - * - * Returns: (transfer full): on success, returns the created connection. - */ -NMConnection * -nm_keyfile_read (GKeyFile *keyfile, - const char *base_dir, - NMKeyfileReadHandler handler, - void *user_data, - GError **error) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingConnection *s_con; - gs_strfreev char **groups = NULL; - gsize n_groups; - gsize i; - gboolean vpn_secrets = FALSE; - KeyfileReaderInfo info; - - g_return_val_if_fail (keyfile, NULL); - g_return_val_if_fail (!error || !*error, NULL); - g_return_val_if_fail (base_dir && base_dir[0] == '/', NULL); - - connection = nm_simple_connection_new (); - - info = (KeyfileReaderInfo) { - .connection = connection, - .keyfile = keyfile, - .base_dir = base_dir, - .handler = handler, - .user_data = user_data, - }; - - groups = g_key_file_get_groups (keyfile, &n_groups); - if (!groups) - n_groups = 0; - - for (i = 0; i < n_groups; i++) { - - info.group = groups[i]; - - if (nm_streq (groups[i], NM_KEYFILE_GROUP_VPN_SECRETS)) { - /* Only read out secrets when needed */ - vpn_secrets = TRUE; - } else if (NM_STR_HAS_PREFIX (groups[i], NM_KEYFILE_GROUPPREFIX_WIREGUARD_PEER)) - _read_setting_wireguard_peer (&info); - else if (NM_IN_STRSET (groups[i], NM_KEYFILE_GROUP_NMMETA, - ETHERNET_S390_OPTIONS_GROUP_NAME)) { - /* pass */ - } else - _read_setting (&info); - - info.group = NULL; - - if (info.error) - goto out_with_info_error; - } - - s_con = nm_connection_get_setting_connection (connection); - if (!s_con) { - s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_con)); - } - - /* Make sure that we have 'interface-name' even if it was specified in the - * "wrong" (ie, deprecated) group. - */ - if ( !nm_setting_connection_get_interface_name (s_con) - && nm_setting_connection_get_connection_type (s_con)) { - gs_free char *interface_name = NULL; - - interface_name = g_key_file_get_string (keyfile, - nm_setting_connection_get_connection_type (s_con), - "interface-name", - NULL); - if (interface_name) - g_object_set (s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, interface_name, NULL); - } - - if (vpn_secrets) { - info.group = NM_KEYFILE_GROUP_VPN_SECRETS; - _read_setting_vpn_secrets (&info); - info.group = NULL;; - if (info.error) - goto out_with_info_error; - } - - return g_steal_pointer (&connection); - -out_with_info_error: - g_propagate_error (error, info.error); - return NULL; -} - -/*****************************************************************************/ - -static void -write_setting_value (KeyfileWriterInfo *info, - NMSetting *setting, - const NMSettInfoProperty *property_info) -{ - const NMMetaSettingInfo *setting_info; - const ParseInfoProperty *pip; - const char *key; - char numstr[64]; - GValue value; - GType type; - - nm_assert (!info->error); - nm_assert ( !property_info->param_spec - || nm_streq (property_info->param_spec->name, property_info->name)); - - key = property_info->name; - - _parse_info_find (setting, key, &setting_info, NULL, &pip); - - if (!pip) { - if (!setting_info) { - /* the setting type is unknown. That is highly unexpected - * (and as this is currently only called from NetworkManager - * daemon, not possible). - * - * Still, handle it gracefully, because later keyfile writer will become - * public API of libnm, where @setting is (untrusted) user input. - * - * Gracefully here just means: ignore the setting. */ - return; - } - if (!property_info->param_spec) - return; - if (nm_streq (key, NM_SETTING_NAME)) - return; - } else { - if (pip->has_writer_full) { - pip->writer_full (info, setting_info, property_info, pip, setting); - return; - } - if (pip->writer_skip) - return; - } - - nm_assert (property_info->param_spec); - - /* Don't write secrets that are owned by user secret agents or aren't - * supposed to be saved. VPN secrets are handled specially though since - * the secret flags there are in a third-level hash in the 'secrets' - * property. - */ - if ( (property_info->param_spec->flags & NM_SETTING_PARAM_SECRET) - && !NM_IS_SETTING_VPN (setting)) { - NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE; - - if (!nm_setting_get_secret_flags (setting, key, &secret_flags, NULL)) - g_return_if_reached (); - if (!_secret_flags_persist_secret (secret_flags)) - return; - } - - value = (GValue) { 0 }; - - g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (property_info->param_spec)); - g_object_get_property (G_OBJECT (setting), property_info->param_spec->name, &value); - - if ( (!pip || !pip->writer_persist_default) - && g_param_value_defaults (property_info->param_spec, &value)) { - nm_assert (!g_key_file_has_key (info->keyfile, setting_info->setting_name, key, NULL)); - goto out_unset_value; - } - - if ( pip - && pip->writer) { - pip->writer (info, setting, key, &value); - goto out_unset_value; - } - - type = G_VALUE_TYPE (&value); - if (type == G_TYPE_STRING) { - const char *str; - - str = g_value_get_string (&value); - if (str) - nm_keyfile_plugin_kf_set_string (info->keyfile, setting_info->setting_name, key, str); - } else if (type == G_TYPE_UINT) { - nm_sprintf_buf (numstr, "%u", g_value_get_uint (&value)); - nm_keyfile_plugin_kf_set_value (info->keyfile, setting_info->setting_name, key, numstr); - } else if (type == G_TYPE_INT) { - nm_sprintf_buf (numstr, "%d", g_value_get_int (&value)); - nm_keyfile_plugin_kf_set_value (info->keyfile, setting_info->setting_name, key, numstr); - } else if (type == G_TYPE_UINT64) { - nm_sprintf_buf (numstr, "%" G_GUINT64_FORMAT, g_value_get_uint64 (&value)); - nm_keyfile_plugin_kf_set_value (info->keyfile, setting_info->setting_name, key, numstr); - } else if (type == G_TYPE_INT64) { - nm_sprintf_buf (numstr, "%" G_GINT64_FORMAT, g_value_get_int64 (&value)); - nm_keyfile_plugin_kf_set_value (info->keyfile, setting_info->setting_name, key, numstr); - } else if (type == G_TYPE_BOOLEAN) { - nm_keyfile_plugin_kf_set_value (info->keyfile, setting_info->setting_name, key, - g_value_get_boolean (&value) - ? "true" - : "false"); - } else if (type == G_TYPE_CHAR) { - nm_sprintf_buf (numstr, "%d", (int) g_value_get_schar (&value)); - nm_keyfile_plugin_kf_set_value (info->keyfile, setting_info->setting_name, key, numstr); - } else if (type == G_TYPE_BYTES) { - GBytes *bytes; - const guint8 *data; - gsize len = 0; - - bytes = g_value_get_boxed (&value); - data = bytes ? g_bytes_get_data (bytes, &len) : NULL; - - if (data != NULL && len > 0) - nm_keyfile_plugin_kf_set_integer_list_uint8 (info->keyfile, setting_info->setting_name, key, data, len); - } else if (type == G_TYPE_STRV) { - char **array; - - array = (char **) g_value_get_boxed (&value); - nm_keyfile_plugin_kf_set_string_list (info->keyfile, setting_info->setting_name, key, (const char **const) array, g_strv_length (array)); - } else if (type == G_TYPE_HASH_TABLE) { - write_hash_of_string (info->keyfile, setting, key, &value); - } else if (type == G_TYPE_ARRAY) { - write_array_of_uint (info->keyfile, setting, key, &value); - } else if (G_VALUE_HOLDS_FLAGS (&value)) { - nm_sprintf_buf (numstr, "%u", g_value_get_flags (&value)); - nm_keyfile_plugin_kf_set_value (info->keyfile, setting_info->setting_name, key, numstr); - } else if (G_VALUE_HOLDS_ENUM (&value)) { - nm_sprintf_buf (numstr, "%d", g_value_get_enum (&value)); - nm_keyfile_plugin_kf_set_value (info->keyfile, setting_info->setting_name, key, numstr); - } else - g_return_if_reached (); - -out_unset_value: - g_value_unset (&value); -} - -static void -_write_setting_wireguard (NMSetting *setting, KeyfileWriterInfo *info) -{ - NMSettingWireGuard *s_wg; - guint i_peer, n_peers; - - s_wg = NM_SETTING_WIREGUARD (setting); - - n_peers = nm_setting_wireguard_get_peers_len (s_wg); - for (i_peer = 0; i_peer < n_peers; i_peer++) { - NMWireGuardPeer *peer = nm_setting_wireguard_get_peer (s_wg, i_peer); - const char *public_key; - char group[NM_STRLEN (NM_KEYFILE_GROUPPREFIX_WIREGUARD_PEER) + 200]; - NMSettingSecretFlags secret_flags; - gboolean any_key = FALSE; - guint i_aip, n_aip; - const char *cstr; - guint32 u32; - - public_key = nm_wireguard_peer_get_public_key (peer); - if ( !public_key - || !public_key[0] - || !NM_STRCHAR_ALL (public_key, ch, nm_sd_utils_unbase64char (ch, TRUE) >= 0)) { - /* invalid peer. Skip it */ - continue; - } - - if (g_snprintf (group, - sizeof (group), - "%s%s", - NM_KEYFILE_GROUPPREFIX_WIREGUARD_PEER, - nm_wireguard_peer_get_public_key (peer)) >= sizeof (group)) { - /* Too long. Not a valid public key. Skip the peer. */ - continue; - } - - cstr = nm_wireguard_peer_get_endpoint (peer); - if (cstr) { - g_key_file_set_string (info->keyfile, group, NM_WIREGUARD_PEER_ATTR_ENDPOINT, cstr); - any_key = TRUE; - } - - secret_flags = nm_wireguard_peer_get_preshared_key_flags (peer); - if (_secret_flags_persist_secret (secret_flags)) { - cstr = nm_wireguard_peer_get_preshared_key (peer); - if (cstr) { - g_key_file_set_string (info->keyfile, group, NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY, cstr); - any_key = TRUE; - } - } - - /* usually, we don't persist the secret-flags 0 (because they are the default). - * For WireGuard peers, the default secret-flags for preshared-key are 4 (not-required). - * So, in this case behave differently: a missing preshared-key-flag setting means - * "not-required". */ - if (secret_flags != NM_SETTING_SECRET_FLAG_NOT_REQUIRED) { - g_key_file_set_int64 (info->keyfile, group, NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY_FLAGS, secret_flags); - any_key = TRUE; - } - - u32 = nm_wireguard_peer_get_persistent_keepalive (peer); - if (u32) { - g_key_file_set_uint64 (info->keyfile, group, NM_WIREGUARD_PEER_ATTR_PERSISTENT_KEEPALIVE, u32); - any_key = TRUE; - } - - n_aip = nm_wireguard_peer_get_allowed_ips_len (peer); - if (n_aip > 0) { - gs_free const char **strv = NULL; - - strv = g_new (const char *, ((gsize) n_aip) + 1); - for (i_aip = 0; i_aip < n_aip; i_aip++) - strv[i_aip] = nm_wireguard_peer_get_allowed_ip (peer, i_aip, NULL); - strv[n_aip] = NULL; - g_key_file_set_string_list (info->keyfile, group, NM_WIREGUARD_PEER_ATTR_ALLOWED_IPS, - strv, n_aip); - any_key = TRUE; - } - - if (!any_key) { - /* we cannot omit all keys. At an empty endpoint. */ - g_key_file_set_string (info->keyfile, group, NM_WIREGUARD_PEER_ATTR_ENDPOINT, ""); - } - } -} - -GKeyFile * -nm_keyfile_write (NMConnection *connection, - NMKeyfileWriteHandler handler, - void *user_data, - GError **error) -{ - gs_unref_keyfile GKeyFile *keyfile = NULL; - KeyfileWriterInfo info; - gs_free NMSetting **settings = NULL; - guint i, j, n_settings = 0; - - g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); - g_return_val_if_fail (!error || !*error, NULL); - - if (!nm_connection_verify (connection, error)) - return NULL; - - keyfile = g_key_file_new (); - - info = (KeyfileWriterInfo) { - .connection = connection, - .keyfile = keyfile, - .error = NULL, - .handler = handler, - .user_data = user_data, - }; - - settings = nm_connection_get_settings (connection, &n_settings); - for (i = 0; i < n_settings; i++) { - const NMSettInfoSetting *sett_info; - NMSetting *setting = settings[i]; - const char *setting_name; - const char *setting_alias; - - sett_info = _nm_setting_class_get_sett_info (NM_SETTING_GET_CLASS (setting)); - - setting_name = sett_info->setting_class->setting_info->setting_name; - - if (sett_info->detail.gendata_info) { - guint k, n_keys; - const char *const*keys; - - nm_assert (!nm_keyfile_plugin_get_alias_for_setting_name (sett_info->setting_class->setting_info->setting_name)); - - n_keys = _nm_setting_gendata_get_all (setting, &keys, NULL); - - if (n_keys > 0) { - GHashTable *h = _nm_setting_gendata_hash (setting, FALSE); - - for (k = 0; k < n_keys; k++) { - const char *key = keys[k]; - GVariant *v; - - v = g_hash_table_lookup (h, key); - - if (g_variant_is_of_type (v, G_VARIANT_TYPE_BOOLEAN)) { - g_key_file_set_boolean (info.keyfile, - setting_name, - key, - g_variant_get_boolean (v)); - } else { - /* BUG: The variant type is not implemented. Since the connection - * verifies, this can only mean we either wrongly didn't reject - * the connection as invalid, or we didn't properly implement the - * variant type. */ - nm_assert_not_reached (); - continue; - } - } - } - } - - for (j = 0; j < sett_info->property_infos_len; j++) { - const NMSettInfoProperty *property_info = _nm_sett_info_property_info_get_sorted (sett_info, j); - - write_setting_value (&info, setting, property_info); - if (info.error) - goto out_with_info_error; - } - - setting_alias = nm_keyfile_plugin_get_alias_for_setting_name (setting_name); - if ( ( setting_alias - && g_key_file_has_group (info.keyfile, setting_alias)) - || g_key_file_has_group (info.keyfile, setting_name)) { - /* we have a section for the setting. Nothing to do. */ - } else { - /* ensure the group is present. There is no API for that, so add and remove - * a dummy key. */ - g_key_file_set_value (info.keyfile, setting_alias ?: setting_name, ".X", "1"); - g_key_file_remove_key (info.keyfile, setting_alias ?: setting_name, ".X", NULL); - } - - if (NM_IS_SETTING_WIREGUARD (setting)) { - _write_setting_wireguard (setting, &info); - if (info.error) - goto out_with_info_error; - } - - nm_assert (!info.error); - } - - nm_assert (!info.error); - - return g_steal_pointer (&keyfile); - -out_with_info_error: - g_propagate_error (error, info.error); - return NULL; -} - -/*****************************************************************************/ - -static const char temp_letters[] = -"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - -/* - * Check '.[a-zA-Z0-9]{6}' file suffix used for temporary files by g_file_set_contents() (mkstemp()). - */ -static gboolean -check_mkstemp_suffix (const char *path) -{ - const char *ptr; - - nm_assert (path); - - /* Matches *.[a-zA-Z0-9]{6} suffix of mkstemp()'s temporary files */ - ptr = strrchr (path, '.'); - if ( ptr - && strspn (&ptr[1], temp_letters) == 6 - && ptr[7] == '\0') - return TRUE; - return FALSE; -} - -static gboolean -_check_suffix_impl (const char *base, const char *tag, gsize tag_len) -{ - gsize len; - - nm_assert (base); - nm_assert (tag); - nm_assert (strlen (tag) == tag_len); - - len = strlen (base); - if ( len > tag_len - && !g_ascii_strcasecmp (base + len - tag_len, tag)) - return TRUE; - return FALSE; -} -#define check_suffix(base, tag) _check_suffix_impl ((base), ""tag"", NM_STRLEN (tag)) - -#define SWP_TAG ".swp" -#define SWPX_TAG ".swpx" -#define PEM_TAG ".pem" -#define DER_TAG ".der" - -gboolean -nm_keyfile_utils_ignore_filename (const char *filename, gboolean require_extension) -{ - const char *base; - gsize l; - - /* ignore_filename() must mirror nm_keyfile_utils_create_filename() */ - - g_return_val_if_fail (filename, TRUE); - - base = strrchr (filename, '/'); - if (base) - base++; - else - base = filename; - - if (!base[0]) { - /* this check above with strrchr() also rejects "/some/path/with/trailing/slash/", - * but that is fine, because such a path would name a directory, and we are not - * interested in directories. */ - return TRUE; - } - - if (base[0] == '.') { - /* don't allow hidden files */ - return TRUE; - } - - l = strlen (base); - - if (require_extension) { - if ( l <= NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION) - || !NM_STR_HAS_SUFFIX (base, NM_KEYFILE_PATH_SUFFIX_NMCONNECTION)) - return TRUE; - return FALSE; - } - - /* Ignore backup files */ - if (base[l - 1] == '~') - return TRUE; - - /* Ignore temporary files - * - * This check is also important to ignore .nmload files (see - * %NM_KEYFILE_PATH_SUFFIX_NMMETA). */ - if (check_mkstemp_suffix (base)) - return TRUE; - - /* Ignore 802.1x certificates and keys */ - if ( check_suffix (base, PEM_TAG) - || check_suffix (base, DER_TAG)) - return TRUE; - - return FALSE; -} - -char * -nm_keyfile_utils_create_filename (const char *name, - gboolean with_extension) -{ - GString *str; - const char *f = name; - /* keyfile used to escape with '*', do not change that behavior. - * - * But for newly added escapings, use '_' instead. - * Also, @with_extension is new-style. */ - const char ESCAPE_CHAR = with_extension ? '_' : '*'; - const char ESCAPE_CHAR2 = '_'; - - g_return_val_if_fail (name && name[0], NULL); - - str = g_string_sized_new (60); - - /* Convert '/' to ESCAPE_CHAR */ - for (f = name; f[0]; f++) { - if (f[0] == '/') - g_string_append_c (str, ESCAPE_CHAR); - else - g_string_append_c (str, f[0]); - } - - /* nm_keyfile_utils_create_filename() must avoid anything that ignore_filename() would reject. - * We can escape here more aggressivly then what we would read back. */ - if (str->str[0] == '.') - str->str[0] = ESCAPE_CHAR2; - if (str->str[str->len - 1] == '~') - str->str[str->len - 1] = ESCAPE_CHAR2; - if ( check_mkstemp_suffix (str->str) - || check_suffix (str->str, PEM_TAG) - || check_suffix (str->str, DER_TAG)) - g_string_append_c (str, ESCAPE_CHAR2); - - if (with_extension) - g_string_append (str, NM_KEYFILE_PATH_SUFFIX_NMCONNECTION); - - /* nm_keyfile_utils_create_filename() must mirror ignore_filename() */ - nm_assert (!strchr (str->str, '/')); - nm_assert (!nm_keyfile_utils_ignore_filename (str->str, with_extension)); - - return g_string_free (str, FALSE);; -} diff --git a/shared/nm-libnm-aux/nm-libnm-aux.c b/shared/nm-libnm-aux/nm-libnm-aux.c deleted file mode 100644 index 169416b9..00000000 --- a/shared/nm-libnm-aux/nm-libnm-aux.c +++ /dev/null @@ -1,146 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1+ - -#include "nm-default.h" - -#include "nm-libnm-aux.h" - -/*****************************************************************************/ - -NMClient * -nmc_client_new_async_valist (GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data, - const char *first_property_name, - va_list ap) -{ - NMClient *nmc; - - nmc = NM_CLIENT (g_object_new_valist (NM_TYPE_CLIENT, first_property_name, ap)); - g_async_initable_init_async (G_ASYNC_INITABLE (nmc), - G_PRIORITY_DEFAULT, - cancellable, - callback, - user_data); - return nmc; -} - -NMClient * -nmc_client_new_async (GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data, - const char *first_property_name, - ...) -{ - NMClient *nmc; - va_list ap; - - va_start (ap, first_property_name); - nmc = nmc_client_new_async_valist (cancellable, - callback, - user_data, - first_property_name, - ap); - va_end (ap); - return nmc; -} - -/*****************************************************************************/ - -typedef struct { - GMainLoop *main_loop; - NMClient *nmc; - GError *error; -} ClientCreateData; - -static void -_nmc_client_new_waitsync_cb (GObject *source_object, - GAsyncResult *result, - gpointer user_data) -{ - ClientCreateData *data = user_data; - - g_async_initable_init_finish (G_ASYNC_INITABLE (source_object), - result, - &data->error); - g_main_loop_quit (data->main_loop); -} - -/** - * nmc_client_new: - * @cancellable: the cancellable to abort the creation. - * @out_nmc: (out): (transfer full): if give, transfers a reference - * to the NMClient instance. Note that this never fails to create - * the NMClient GObject, but depending on the return value, - * the instance was successfully initialized or not. - * @error: the error if creation fails. - * @first_property_name: the name of the first property - * @...: the value of the first property, followed optionally by more - * name/value pairs, followed by %NULL - * - * Returns: %TRUE, if the client was successfully initalized. - * - * This uses nm_client_new_async() to create a NMClient instance, - * but it iterates the current GMainContext until the client is - * ready. As such, it waits for the client creation to complete - * (like sync nm_client_new()) but it iterates the caller's GMainContext - * (unlike sync nm_client_new()). This is often preferable, because - * sync nm_client_new() needs to create an additional internal GMainContext - * that it can iterate instead. That has a performance overhead that - * is often unnecessary. - */ -gboolean -nmc_client_new_waitsync (GCancellable *cancellable, - NMClient **out_nmc, - GError **error, - const char *first_property_name, - ...) -{ - gs_unref_object NMClient *nmc = NULL; - nm_auto_unref_gmainloop GMainLoop *main_loop = g_main_loop_new (g_main_context_get_thread_default (), FALSE); - ClientCreateData data = { - .main_loop = main_loop, - }; - va_list ap; - -#if NM_MORE_ASSERTS > 10 - /* The sync initialization of NMClient is generally a bad idea, because it - * brings the overhead of an additional GMainContext. Anyway, since our own - * code no longer uses that, we hardly test those code paths. But they should - * work just the same. Randomly use instead the sync initialization in a debug - * build... */ - if ((g_random_int () % 2) == 0) { - gboolean success; - - va_start (ap, first_property_name); - nmc = NM_CLIENT (g_object_new_valist (NM_TYPE_CLIENT, first_property_name, ap)); - va_end (ap); - - /* iterate the context at least once, just so that the behavior from POV of the - * caller is roughly the same. */ - g_main_context_iteration (nm_client_get_main_context (nmc), FALSE); - - success = g_initable_init (G_INITABLE (nmc), - cancellable, - error); - NM_SET_OUT (out_nmc, g_steal_pointer (&nmc)); - return success; - } -#endif - - va_start (ap, first_property_name); - nmc = nmc_client_new_async_valist (cancellable, - _nmc_client_new_waitsync_cb, - &data, - first_property_name, - ap); - va_end (ap); - - g_main_loop_run (main_loop); - - NM_SET_OUT (out_nmc, g_steal_pointer (&nmc)); - if (data.error) { - g_propagate_error (error, data.error); - return FALSE; - } - return TRUE; -} diff --git a/shared/nm-libnm-aux/nm-libnm-aux.h b/shared/nm-libnm-aux/nm-libnm-aux.h deleted file mode 100644 index a0aff19f..00000000 --- a/shared/nm-libnm-aux/nm-libnm-aux.h +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1+ - -#ifndef __NM_LIBNM_AUX_H__ -#define __NM_LIBNM_AUX_H__ - -NMClient *nmc_client_new_async_valist (GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data, - const char *first_property_name, - va_list ap); - -NMClient *nmc_client_new_async (GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data, - const char *first_property_name, - ...); - -gboolean nmc_client_new_waitsync (GCancellable *cancellable, - NMClient **out_nmc, - GError **error, - const char *first_property_name, - ...); - -#endif /* __NM_LIBNM_AUX_H__ */ diff --git a/shared/nm-libnm-core-aux/nm-dispatcher-api.h b/shared/nm-libnm-core-aux/nm-dispatcher-api.h deleted file mode 100644 index 96f512e0..00000000 --- a/shared/nm-libnm-core-aux/nm-dispatcher-api.h +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2008 - 2012 Red Hat, Inc. - */ - -#ifndef __NM_DISPACHER_API_H__ -#define __NM_DISPACHER_API_H__ - -#define NM_DISPATCHER_DBUS_SERVICE "org.freedesktop.nm_dispatcher" -#define NM_DISPATCHER_DBUS_INTERFACE "org.freedesktop.nm_dispatcher" -#define NM_DISPATCHER_DBUS_PATH "/org/freedesktop/nm_dispatcher" - -#define NMD_CONNECTION_PROPS_PATH "path" -#define NMD_CONNECTION_PROPS_FILENAME "filename" -#define NMD_CONNECTION_PROPS_EXTERNAL "external" - -#define NMD_DEVICE_PROPS_INTERFACE "interface" -#define NMD_DEVICE_PROPS_IP_INTERFACE "ip-interface" -#define NMD_DEVICE_PROPS_TYPE "type" -#define NMD_DEVICE_PROPS_STATE "state" -#define NMD_DEVICE_PROPS_PATH "path" - -/* Actions */ -#define NMD_ACTION_HOSTNAME "hostname" -#define NMD_ACTION_PRE_UP "pre-up" -#define NMD_ACTION_UP "up" -#define NMD_ACTION_PRE_DOWN "pre-down" -#define NMD_ACTION_DOWN "down" -#define NMD_ACTION_VPN_PRE_UP "vpn-pre-up" -#define NMD_ACTION_VPN_UP "vpn-up" -#define NMD_ACTION_VPN_PRE_DOWN "vpn-pre-down" -#define NMD_ACTION_VPN_DOWN "vpn-down" -#define NMD_ACTION_DHCP4_CHANGE "dhcp4-change" -#define NMD_ACTION_DHCP6_CHANGE "dhcp6-change" -#define NMD_ACTION_CONNECTIVITY_CHANGE "connectivity-change" - -typedef enum { - DISPATCH_RESULT_UNKNOWN = 0, - DISPATCH_RESULT_SUCCESS = 1, - DISPATCH_RESULT_EXEC_FAILED = 2, - DISPATCH_RESULT_FAILED = 3, - DISPATCH_RESULT_TIMEOUT = 4, -} DispatchResult; - -#endif /* __NM_DISPACHER_API_H__ */ diff --git a/shared/nm-libnm-core-aux/nm-libnm-core-aux.c b/shared/nm-libnm-core-aux/nm-libnm-core-aux.c deleted file mode 100644 index f827726f..00000000 --- a/shared/nm-libnm-core-aux/nm-libnm-core-aux.c +++ /dev/null @@ -1,358 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1+ -/* - * Copyright (C) 2019 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-libnm-core-aux.h" - -#include "nm-libnm-core-intern/nm-libnm-core-utils.h" - -/*****************************************************************************/ - -typedef enum { - KEY_TYPE_STRING, - KEY_TYPE_INT, - KEY_TYPE_BOOL, -} KeyType; - -typedef struct { - const char *str_val; - union { - int vint; - bool vbool; - } typ_val; -} ParseData; - -typedef struct { - const char *name; - NMTeamLinkWatcherType watcher_type; - KeyType key_type; - union { - int (*fint) (const NMTeamLinkWatcher *watcher); - gboolean (*fbool) (const NMTeamLinkWatcher *watcher); - const char *(*fstring) (const NMTeamLinkWatcher *watcher); - } get_fcn; - union { - int vint; - bool vbool; - } def_val; -} TeamLinkWatcherKeyInfo; - -static gboolean -_team_link_watcher_validate_active (const NMTeamLinkWatcher *watcher) -{ - return NM_FLAGS_HAS (nm_team_link_watcher_get_flags (watcher), NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE); -} - -static gboolean -_team_link_watcher_validate_inactive (const NMTeamLinkWatcher *watcher) -{ - return NM_FLAGS_HAS (nm_team_link_watcher_get_flags (watcher), NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE); -} - -static gboolean -_team_link_watcher_send_always (const NMTeamLinkWatcher *watcher) -{ - return NM_FLAGS_HAS (nm_team_link_watcher_get_flags (watcher), NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS); -} - -static const TeamLinkWatcherKeyInfo _team_link_watcher_key_infos[_NM_TEAM_LINK_WATCHER_KEY_NUM] = { - -#define _KEY_INFO(key_id, _name, _watcher_type, _key_type, ...) \ - [key_id] = { .name = ""_name"", .watcher_type = (_watcher_type), .key_type = _key_type, ##__VA_ARGS__ } - - _KEY_INFO (NM_TEAM_LINK_WATCHER_KEY_NAME, "name", NM_TEAM_LINK_WATCHER_TYPE_ETHTOOL | NM_TEAM_LINK_WATCHER_TYPE_NSNAPING | NM_TEAM_LINK_WATCHER_TYPE_ARPING, KEY_TYPE_STRING, .get_fcn.fstring = nm_team_link_watcher_get_name, ), - _KEY_INFO (NM_TEAM_LINK_WATCHER_KEY_DELAY_UP, "delay-up", NM_TEAM_LINK_WATCHER_TYPE_ETHTOOL, KEY_TYPE_INT, .get_fcn.fint = nm_team_link_watcher_get_delay_up, ), - _KEY_INFO (NM_TEAM_LINK_WATCHER_KEY_DELAY_DOWN, "delay-down", NM_TEAM_LINK_WATCHER_TYPE_ETHTOOL, KEY_TYPE_INT, .get_fcn.fint = nm_team_link_watcher_get_delay_down, ), - _KEY_INFO (NM_TEAM_LINK_WATCHER_KEY_INIT_WAIT, "init-wait", NM_TEAM_LINK_WATCHER_TYPE_NSNAPING | NM_TEAM_LINK_WATCHER_TYPE_ARPING, KEY_TYPE_INT, .get_fcn.fint = nm_team_link_watcher_get_init_wait, ), - _KEY_INFO (NM_TEAM_LINK_WATCHER_KEY_INTERVAL, "interval", NM_TEAM_LINK_WATCHER_TYPE_NSNAPING | NM_TEAM_LINK_WATCHER_TYPE_ARPING, KEY_TYPE_INT, .get_fcn.fint = nm_team_link_watcher_get_interval, ), - _KEY_INFO (NM_TEAM_LINK_WATCHER_KEY_MISSED_MAX, "missed-max", NM_TEAM_LINK_WATCHER_TYPE_NSNAPING | NM_TEAM_LINK_WATCHER_TYPE_ARPING, KEY_TYPE_INT, .get_fcn.fint = nm_team_link_watcher_get_missed_max, .def_val.vint = 3, ), - _KEY_INFO (NM_TEAM_LINK_WATCHER_KEY_TARGET_HOST, "target-host", NM_TEAM_LINK_WATCHER_TYPE_NSNAPING | NM_TEAM_LINK_WATCHER_TYPE_ARPING, KEY_TYPE_STRING, .get_fcn.fstring = nm_team_link_watcher_get_target_host, ), - _KEY_INFO (NM_TEAM_LINK_WATCHER_KEY_VLANID, "vlanid", NM_TEAM_LINK_WATCHER_TYPE_ARPING, KEY_TYPE_INT, .get_fcn.fint = nm_team_link_watcher_get_vlanid, .def_val.vint = -1, ), - _KEY_INFO (NM_TEAM_LINK_WATCHER_KEY_SOURCE_HOST, "source-host", NM_TEAM_LINK_WATCHER_TYPE_ARPING, KEY_TYPE_STRING, .get_fcn.fstring = nm_team_link_watcher_get_source_host, ), - _KEY_INFO (NM_TEAM_LINK_WATCHER_KEY_VALIDATE_ACTIVE, "validate-active", NM_TEAM_LINK_WATCHER_TYPE_ARPING, KEY_TYPE_BOOL, .get_fcn.fbool = _team_link_watcher_validate_active, ), - _KEY_INFO (NM_TEAM_LINK_WATCHER_KEY_VALIDATE_INACTIVE, "validate-inactive", NM_TEAM_LINK_WATCHER_TYPE_ARPING, KEY_TYPE_BOOL, .get_fcn.fbool = _team_link_watcher_validate_inactive, ), - _KEY_INFO (NM_TEAM_LINK_WATCHER_KEY_SEND_ALWAYS, "send-always", NM_TEAM_LINK_WATCHER_TYPE_ARPING, KEY_TYPE_BOOL, .get_fcn.fbool = _team_link_watcher_send_always, ), - -}; - -static NMTeamLinkWatcherType -_team_link_watcher_get_watcher_type_from_name (const char *name) -{ - if (name) { - if (nm_streq (name, NM_TEAM_LINK_WATCHER_ETHTOOL)) - return NM_TEAM_LINK_WATCHER_TYPE_ETHTOOL; - if (nm_streq (name, NM_TEAM_LINK_WATCHER_NSNA_PING)) - return NM_TEAM_LINK_WATCHER_TYPE_NSNAPING; - if (nm_streq (name, NM_TEAM_LINK_WATCHER_ARP_PING)) - return NM_TEAM_LINK_WATCHER_TYPE_ARPING; - } - return NM_TEAM_LINK_WATCHER_TYPE_NONE; -} - -static const char * -_parse_data_get_str (const ParseData parse_data[static _NM_TEAM_LINK_WATCHER_KEY_NUM], - NMTeamLinkWatcherKeyId key_id) -{ - nm_assert (_NM_INT_NOT_NEGATIVE (key_id) && key_id < _NM_TEAM_LINK_WATCHER_KEY_NUM); - nm_assert (_team_link_watcher_key_infos[key_id].key_type == KEY_TYPE_STRING); - - return parse_data[key_id].str_val; -} - -static int -_parse_data_get_int (const ParseData parse_data[static _NM_TEAM_LINK_WATCHER_KEY_NUM], - NMTeamLinkWatcherKeyId key_id) -{ - nm_assert (_NM_INT_NOT_NEGATIVE (key_id) && key_id < _NM_TEAM_LINK_WATCHER_KEY_NUM); - nm_assert (_team_link_watcher_key_infos[key_id].key_type == KEY_TYPE_INT); - - if (parse_data[key_id].str_val) - return parse_data[key_id].typ_val.vint; - return _team_link_watcher_key_infos[key_id].def_val.vint; -} - -static int -_parse_data_get_bool (const ParseData parse_data[static _NM_TEAM_LINK_WATCHER_KEY_NUM], - NMTeamLinkWatcherKeyId key_id) -{ - nm_assert (_NM_INT_NOT_NEGATIVE (key_id) && key_id < _NM_TEAM_LINK_WATCHER_KEY_NUM); - nm_assert (_team_link_watcher_key_infos[key_id].key_type == KEY_TYPE_BOOL); - - if (parse_data[key_id].str_val) - return parse_data[key_id].typ_val.vbool; - return _team_link_watcher_key_infos[key_id].def_val.vbool; -} - -char * -nm_utils_team_link_watcher_to_string (const NMTeamLinkWatcher *watcher) -{ - nm_auto_free_gstring GString *str = NULL; - const char *name; - NMTeamLinkWatcherType watcher_type; - NMTeamLinkWatcherKeyId key_id; - - if (!watcher) - return NULL; - - str = g_string_new (NULL); - - name = nm_team_link_watcher_get_name (watcher); - g_string_append_printf (str, "name=%s", name ?: ""); - - watcher_type = _team_link_watcher_get_watcher_type_from_name (name); - - for (key_id = 0; key_id < _NM_TEAM_LINK_WATCHER_KEY_NUM; key_id++) { - const TeamLinkWatcherKeyInfo *info = &_team_link_watcher_key_infos[key_id]; - const char *vstr; - int vint; - bool vbool; - - nm_assert (info->name && info->name && NM_STRCHAR_ALL (info->name, ch,((ch >= 'a' && ch <= 'z') || NM_IN_SET (ch, '-')))); - nm_assert (NM_IN_SET (info->key_type, KEY_TYPE_STRING, - KEY_TYPE_INT, - KEY_TYPE_BOOL)); - - if (key_id == NM_TEAM_LINK_WATCHER_KEY_NAME) - continue; - - if (!NM_FLAGS_ALL (info->watcher_type, watcher_type)) - continue; - - switch (info->key_type) { - case KEY_TYPE_STRING: - vstr = info->get_fcn.fstring (watcher); - if (vstr) { - g_string_append_printf (nm_gstring_add_space_delimiter (str), - "%s=%s", info->name, vstr); - } - break; - case KEY_TYPE_INT: - vint = info->get_fcn.fint (watcher); - if (vint != info->def_val.vint) { - g_string_append_printf (nm_gstring_add_space_delimiter (str), - "%s=%d", info->name, vint); - } - break; - case KEY_TYPE_BOOL: - vbool = info->get_fcn.fbool (watcher); - if (vbool != info->def_val.vbool) { - g_string_append_printf (nm_gstring_add_space_delimiter (str), - "%s=%s", info->name, vbool ? "true" : "false"); - } - break; - } - } - - return g_string_free (g_steal_pointer (&str), FALSE); -} - -NMTeamLinkWatcher * -nm_utils_team_link_watcher_from_string (const char *str, - GError **error) -{ - gs_free const char **tokens = NULL; - ParseData parse_data[_NM_TEAM_LINK_WATCHER_KEY_NUM] = { }; - NMTeamLinkWatcherType watcher_type; - NMTeamLinkWatcherKeyId key_id; - gsize i_token; - NMTeamLinkWatcher *watcher; - int errsv; - - g_return_val_if_fail (str, NULL); - g_return_val_if_fail (!error || !*error, NULL); - - tokens = nm_utils_escaped_tokens_split (str, NM_ASCII_SPACES); - if (!tokens) { - g_set_error (error, 1, 0, "'%s' is not valid", str); - return NULL; - } - - for (i_token = 0; tokens[i_token]; i_token++) { - const TeamLinkWatcherKeyInfo *info; - const char *key = tokens[i_token]; - const char *val; - - val = strchr (key, '='); - if (!val) { - nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, - _("'%s' is not valid: properties should be specified as 'key=value'"), - key); - return NULL; - } - ((char *) val)[0] = '\0'; - val++; - - for (key_id = 0; key_id < _NM_TEAM_LINK_WATCHER_KEY_NUM; key_id++) { - info = &_team_link_watcher_key_infos[key_id]; - if (nm_streq (key, info->name)) - break; - } - - if (key_id == _NM_TEAM_LINK_WATCHER_KEY_NUM) { - nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, - _("'%s' is not a valid key"), key); - return NULL; - } - - if (parse_data[key_id].str_val) { - nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, - _("duplicate key '%s'"), key); - return NULL; - } - - parse_data[key_id].str_val = val; - - if (info->key_type == KEY_TYPE_INT) { - gint64 v64; - - v64 = _nm_utils_ascii_str_to_int64 (val, 10, G_MININT, G_MAXINT, G_MAXINT64); - if ( v64 == G_MAXINT64 - && ((errsv = errno) != 0)) { - if (errsv == ERANGE) { - nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, - _("number for '%s' is out of range"), key); - } else { - nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, - _("value for '%s' must be a number"), key); - } - return NULL; - } - parse_data[key_id].typ_val.vint = v64; - } else if (info->key_type == KEY_TYPE_BOOL) { - int vbool; - - vbool = _nm_utils_ascii_str_to_bool (val, -1); - if (vbool == -1) { - nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, - _("value for '%s' must be a boolean"), key); - return NULL; - } - parse_data[key_id].typ_val.vbool = vbool; - } - } - - if (!parse_data[NM_TEAM_LINK_WATCHER_KEY_NAME].str_val) { - nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, - _("missing 'name' attribute")); - return NULL; - } - - watcher_type = _team_link_watcher_get_watcher_type_from_name (parse_data[NM_TEAM_LINK_WATCHER_KEY_NAME].str_val); - if (watcher_type == NM_TEAM_LINK_WATCHER_TYPE_NONE) { - nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, - _("invalid 'name' \"%s\""), - parse_data[NM_TEAM_LINK_WATCHER_KEY_NAME].str_val); - return NULL; - } - - for (key_id = 0; key_id < _NM_TEAM_LINK_WATCHER_KEY_NUM; key_id++) { - const TeamLinkWatcherKeyInfo *info = &_team_link_watcher_key_infos[key_id]; - - if (!parse_data[key_id].str_val) - continue; - if (!NM_FLAGS_ALL (info->watcher_type, watcher_type)) { - nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, - _("attribute '%s' is invalid for \"%s\""), - info->name, - parse_data[NM_TEAM_LINK_WATCHER_KEY_NAME].str_val); - return NULL; - } - } - - switch (watcher_type) { - case NM_TEAM_LINK_WATCHER_TYPE_ETHTOOL: - watcher = nm_team_link_watcher_new_ethtool (_parse_data_get_int (parse_data, NM_TEAM_LINK_WATCHER_KEY_DELAY_UP), - _parse_data_get_int (parse_data, NM_TEAM_LINK_WATCHER_KEY_DELAY_DOWN), - error); - break; - case NM_TEAM_LINK_WATCHER_TYPE_NSNAPING: - watcher = nm_team_link_watcher_new_nsna_ping (_parse_data_get_int (parse_data, NM_TEAM_LINK_WATCHER_KEY_INIT_WAIT), - _parse_data_get_int (parse_data, NM_TEAM_LINK_WATCHER_KEY_INTERVAL), - _parse_data_get_int (parse_data, NM_TEAM_LINK_WATCHER_KEY_MISSED_MAX), - _parse_data_get_str (parse_data, NM_TEAM_LINK_WATCHER_KEY_TARGET_HOST), - error); - break; - default: - nm_assert (watcher_type == NM_TEAM_LINK_WATCHER_TYPE_ARPING); - watcher = nm_team_link_watcher_new_arp_ping2 (_parse_data_get_int (parse_data, NM_TEAM_LINK_WATCHER_KEY_INIT_WAIT), - _parse_data_get_int (parse_data, NM_TEAM_LINK_WATCHER_KEY_INTERVAL), - _parse_data_get_int (parse_data, NM_TEAM_LINK_WATCHER_KEY_MISSED_MAX), - _parse_data_get_int (parse_data, NM_TEAM_LINK_WATCHER_KEY_VLANID), - _parse_data_get_str (parse_data, NM_TEAM_LINK_WATCHER_KEY_TARGET_HOST), - _parse_data_get_str (parse_data, NM_TEAM_LINK_WATCHER_KEY_SOURCE_HOST), - ( NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_NONE - | (_parse_data_get_bool (parse_data, NM_TEAM_LINK_WATCHER_KEY_VALIDATE_ACTIVE) ? NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE : NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_NONE) - | (_parse_data_get_bool (parse_data, NM_TEAM_LINK_WATCHER_KEY_VALIDATE_INACTIVE) ? NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE : NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_NONE) - | (_parse_data_get_bool (parse_data, NM_TEAM_LINK_WATCHER_KEY_SEND_ALWAYS) ? NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS : NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_NONE) - ), - error); - break; - } - -#if NM_MORE_ASSERTS > 5 - if (watcher) { - gs_free char *str2 = NULL; - nm_auto_unref_team_link_watcher NMTeamLinkWatcher *watcher2 = NULL; - static _nm_thread_local int recursive; - - nm_assert (!error || !*error); - if (recursive == 0) { - recursive = 1; - str2 = nm_utils_team_link_watcher_to_string (watcher); - nm_assert (str2); - watcher2 = nm_utils_team_link_watcher_from_string (str2, NULL); - nm_assert (watcher2); - nm_assert (nm_team_link_watcher_equal (watcher, watcher2)); - nm_assert (nm_team_link_watcher_equal (watcher2, watcher)); - nm_assert (recursive == 1); - recursive = 0; - } - } else - nm_assert (!error || *error); -#endif - - return watcher; -} diff --git a/shared/nm-libnm-core-aux/nm-libnm-core-aux.h b/shared/nm-libnm-core-aux/nm-libnm-core-aux.h deleted file mode 100644 index 8a835205..00000000 --- a/shared/nm-libnm-core-aux/nm-libnm-core-aux.h +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1+ -/* - * Copyright (C) 2019 Red Hat, Inc. - */ - -#ifndef __NM_LIBNM_CORE_AUX_H__ -#define __NM_LIBNM_CORE_AUX_H__ - -#include "nm-setting-team.h" - -typedef enum { - NM_TEAM_LINK_WATCHER_TYPE_NONE = 0, - NM_TEAM_LINK_WATCHER_TYPE_ETHTOOL = (1u << 0), - NM_TEAM_LINK_WATCHER_TYPE_NSNAPING = (1u << 1), - NM_TEAM_LINK_WATCHER_TYPE_ARPING = (1u << 2), -} NMTeamLinkWatcherType; - -typedef enum { - NM_TEAM_LINK_WATCHER_KEY_NAME, - NM_TEAM_LINK_WATCHER_KEY_DELAY_UP, - NM_TEAM_LINK_WATCHER_KEY_DELAY_DOWN, - NM_TEAM_LINK_WATCHER_KEY_INIT_WAIT, - NM_TEAM_LINK_WATCHER_KEY_INTERVAL, - NM_TEAM_LINK_WATCHER_KEY_MISSED_MAX, - NM_TEAM_LINK_WATCHER_KEY_TARGET_HOST, - NM_TEAM_LINK_WATCHER_KEY_VLANID, - NM_TEAM_LINK_WATCHER_KEY_SOURCE_HOST, - NM_TEAM_LINK_WATCHER_KEY_VALIDATE_ACTIVE, - NM_TEAM_LINK_WATCHER_KEY_VALIDATE_INACTIVE, - NM_TEAM_LINK_WATCHER_KEY_SEND_ALWAYS, - _NM_TEAM_LINK_WATCHER_KEY_NUM, -} NMTeamLinkWatcherKeyId; - -char *nm_utils_team_link_watcher_to_string (const NMTeamLinkWatcher *watcher); - -NMTeamLinkWatcher *nm_utils_team_link_watcher_from_string (const char *str, - GError **error); - -#endif /* __NM_LIBNM_CORE_AUX_H__ */ diff --git a/shared/nm-libnm-core-intern/nm-auth-subject.c b/shared/nm-libnm-core-intern/nm-auth-subject.c deleted file mode 100644 index 15c2d652..00000000 --- a/shared/nm-libnm-core-intern/nm-auth-subject.c +++ /dev/null @@ -1,446 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1+ -/* - * Copyright (C) 2013 - 2014 Red Hat, Inc. - */ - -/** - * SECTION:nm-auth-subject - * @short_description: Encapsulates authentication information about a requestor - * - * #NMAuthSubject encpasulates identifying information about an entity that - * makes requests, like process identifier and user UID. - */ - -#include "nm-default.h" - -#include "nm-auth-subject.h" - -#include <stdlib.h> - -enum { - PROP_0, - PROP_SUBJECT_TYPE, - PROP_UNIX_PROCESS_DBUS_SENDER, - PROP_UNIX_PROCESS_PID, - PROP_UNIX_PROCESS_UID, - PROP_UNIX_SESSION_ID, - - PROP_LAST, -}; - -typedef struct { - NMAuthSubjectType subject_type; - struct { - gulong pid; - gulong uid; - guint64 start_time; - char *dbus_sender; - } unix_process; - - struct { - char *id; - } unix_session; -} NMAuthSubjectPrivate; - -struct _NMAuthSubject { - GObject parent; - NMAuthSubjectPrivate _priv; -}; - -struct _NMAuthSubjectClass { - GObjectClass parent; -}; - -G_DEFINE_TYPE (NMAuthSubject, nm_auth_subject, G_TYPE_OBJECT) - -#define NM_AUTH_SUBJECT_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMAuthSubject, NM_IS_AUTH_SUBJECT) - -/*****************************************************************************/ - -#define CHECK_SUBJECT(self, error_value) \ - NMAuthSubjectPrivate *priv; \ - g_return_val_if_fail (NM_IS_AUTH_SUBJECT (self), error_value); \ - priv = NM_AUTH_SUBJECT_GET_PRIVATE (self); \ - -#define CHECK_SUBJECT_TYPED(self, expected_subject_type, error_value) \ - CHECK_SUBJECT (self, error_value); \ - g_return_val_if_fail (priv->subject_type == (expected_subject_type), error_value); - -const char * -nm_auth_subject_to_string (NMAuthSubject *self, char *buf, gsize buf_len) -{ - CHECK_SUBJECT (self, NULL); - - switch (priv->subject_type) { - case NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS: - g_snprintf (buf, buf_len, "unix-process[pid=%lu, uid=%lu, start=%llu]", - (unsigned long) priv->unix_process.pid, - (unsigned long) priv->unix_process.uid, - (unsigned long long) priv->unix_process.start_time); - break; - case NM_AUTH_SUBJECT_TYPE_INTERNAL: - g_strlcpy (buf, "internal", buf_len); - break; - case NM_AUTH_SUBJECT_TYPE_UNIX_SESSION: - g_snprintf (buf, buf_len, "unix-session[id=%s]", - priv->unix_session.id); - break; - default: - g_strlcpy (buf, "invalid", buf_len); - break; - } - return buf; -} - -/* returns a floating variant */ -GVariant * -nm_auth_subject_unix_to_polkit_gvariant (NMAuthSubject *self) -{ - GVariantBuilder builder; - CHECK_SUBJECT (self, NULL); - - switch (priv->subject_type) { - - case NM_AUTH_SUBJECT_TYPE_UNIX_SESSION: - g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); - g_variant_builder_add (&builder, "{sv}", "session-id", - g_variant_new_string (priv->unix_session.id)); - return g_variant_new ("(sa{sv})", "unix-session", &builder); - - case NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS: - g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); - g_variant_builder_add (&builder, "{sv}", "pid", - g_variant_new_uint32 (priv->unix_process.pid)); - g_variant_builder_add (&builder, "{sv}", "start-time", - g_variant_new_uint64 (priv->unix_process.start_time)); - g_variant_builder_add (&builder, "{sv}", "uid", - g_variant_new_int32 (priv->unix_process.uid)); - return g_variant_new ("(sa{sv})", "unix-process", &builder); - - default: - g_return_val_if_reached (NULL); - } -} - -NMAuthSubjectType -nm_auth_subject_get_subject_type (NMAuthSubject *subject) -{ - CHECK_SUBJECT (subject, NM_AUTH_SUBJECT_TYPE_INVALID); - - return priv->subject_type; -} - -gulong -nm_auth_subject_get_unix_process_pid (NMAuthSubject *subject) -{ - CHECK_SUBJECT_TYPED (subject, NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS, G_MAXULONG); - - return priv->unix_process.pid; -} - -gulong -nm_auth_subject_get_unix_process_uid (NMAuthSubject *subject) -{ - CHECK_SUBJECT_TYPED (subject, NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS, G_MAXULONG); - - return priv->unix_process.uid; -} - -const char * -nm_auth_subject_get_unix_process_dbus_sender (NMAuthSubject *subject) -{ - CHECK_SUBJECT_TYPED (subject, NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS, NULL); - - return priv->unix_process.dbus_sender; -} - -const char * -nm_auth_subject_get_unix_session_id (NMAuthSubject *subject) -{ - CHECK_SUBJECT_TYPED (subject, NM_AUTH_SUBJECT_TYPE_UNIX_SESSION, NULL); - - return priv->unix_session.id; -} - -/*****************************************************************************/ - -/** - * nm_auth_subject_new_internal(): - * - * Creates a new auth subject representing the NetworkManager process itself. - * - * Returns: the new #NMAuthSubject - */ -NMAuthSubject * -nm_auth_subject_new_internal (void) -{ - return NM_AUTH_SUBJECT (g_object_new (NM_TYPE_AUTH_SUBJECT, - NM_AUTH_SUBJECT_SUBJECT_TYPE, (int) NM_AUTH_SUBJECT_TYPE_INTERNAL, - NULL)); -} - -/** - * nm_auth_subject_new_unix_session(): - * - * Creates a new auth subject representing a given unix session. - * - * Returns: the new #NMAuthSubject - */ -NMAuthSubject * -nm_auth_subject_new_unix_session (const char *session_id) -{ - return NM_AUTH_SUBJECT (g_object_new (NM_TYPE_AUTH_SUBJECT, - NM_AUTH_SUBJECT_SUBJECT_TYPE, (int) NM_AUTH_SUBJECT_TYPE_UNIX_SESSION, - NM_AUTH_SUBJECT_UNIX_SESSION_ID, session_id, - NULL)); -} - -/** - * nm_auth_subject_new_unix_process(): - * - * Creates a new auth subject representing a given unix process. - * - * Returns: the new #NMAuthSubject - */ -NMAuthSubject * -nm_auth_subject_new_unix_process (const char *dbus_sender, gulong pid, gulong uid) -{ - return NM_AUTH_SUBJECT (g_object_new (NM_TYPE_AUTH_SUBJECT, - NM_AUTH_SUBJECT_SUBJECT_TYPE, (int) NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS, - NM_AUTH_SUBJECT_UNIX_PROCESS_DBUS_SENDER, dbus_sender, - NM_AUTH_SUBJECT_UNIX_PROCESS_PID, pid, - NM_AUTH_SUBJECT_UNIX_PROCESS_UID, uid, - NULL)); -} - -/** - * nm_auth_subject_new_unix_process_self(): - * - * Creates a new auth subject representing the current executing process. - * - * Returns: the new #NMAuthSubject - */ -NMAuthSubject * -nm_auth_subject_new_unix_process_self (void) -{ - return nm_auth_subject_new_unix_process (NULL, getpid(), getuid()); -} - -/*****************************************************************************/ - -static void -get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) -{ - NMAuthSubjectPrivate *priv = NM_AUTH_SUBJECT_GET_PRIVATE (object); - - switch (prop_id) { - case PROP_SUBJECT_TYPE: - g_value_set_int (value, priv->subject_type); - break; - case PROP_UNIX_PROCESS_DBUS_SENDER: - g_value_set_string (value, priv->unix_process.dbus_sender); - break; - case PROP_UNIX_PROCESS_PID: - g_value_set_ulong (value, priv->unix_process.pid); - break; - case PROP_UNIX_PROCESS_UID: - g_value_set_ulong (value, priv->unix_process.uid); - break; - case PROP_UNIX_SESSION_ID: - g_value_set_string (value, priv->unix_session.id); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) -{ - NMAuthSubjectPrivate *priv = NM_AUTH_SUBJECT_GET_PRIVATE (object); - NMAuthSubjectType subject_type; - int i; - const char *str; - gulong id; - - switch (prop_id) { - case PROP_SUBJECT_TYPE: - /* construct-only */ - i = g_value_get_int (value); - g_return_if_fail (NM_IN_SET (i, - (int) NM_AUTH_SUBJECT_TYPE_INTERNAL, - (int) NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS, - (int) NM_AUTH_SUBJECT_TYPE_UNIX_SESSION)); - subject_type = i; - priv->subject_type |= subject_type; - g_return_if_fail (priv->subject_type == subject_type); - break; - case PROP_UNIX_PROCESS_DBUS_SENDER: - /* construct-only */ - if ((str = g_value_get_string (value))) { - priv->subject_type |= NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS; - g_return_if_fail (priv->subject_type == NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS); - priv->unix_process.dbus_sender = g_strdup (str); - } - break; - case PROP_UNIX_PROCESS_PID: - /* construct-only */ - if ((id = g_value_get_ulong (value)) != G_MAXULONG) { - priv->subject_type |= NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS; - g_return_if_fail (priv->subject_type == NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS); - priv->unix_process.pid = id; - } - break; - case PROP_UNIX_PROCESS_UID: - /* construct-only */ - if ((id = g_value_get_ulong (value)) != G_MAXULONG) { - priv->subject_type |= NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS; - g_return_if_fail (priv->subject_type == NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS); - priv->unix_process.uid = id; - } - break; - case PROP_UNIX_SESSION_ID: - /* construct-only */ - if ((str = g_value_get_string (value))) { - priv->subject_type |= NM_AUTH_SUBJECT_TYPE_UNIX_SESSION; - g_return_if_fail (priv->subject_type == NM_AUTH_SUBJECT_TYPE_UNIX_SESSION); - priv->unix_session.id = g_strdup (str); - } - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -_clear_private (NMAuthSubject *self) -{ - NMAuthSubjectPrivate *priv = NM_AUTH_SUBJECT_GET_PRIVATE (self); - - priv->subject_type = NM_AUTH_SUBJECT_TYPE_INVALID; - priv->unix_process.pid = G_MAXULONG; - priv->unix_process.uid = G_MAXULONG; - nm_clear_g_free (&priv->unix_process.dbus_sender); - - nm_clear_g_free (&priv->unix_session.id); -} - -static void -nm_auth_subject_init (NMAuthSubject *self) -{ - _clear_private (self); -} - -static void -constructed (GObject *object) -{ - NMAuthSubject *self = NM_AUTH_SUBJECT (object); - NMAuthSubjectPrivate *priv = NM_AUTH_SUBJECT_GET_PRIVATE (self); - - /* validate that the created instance. */ - - switch (priv->subject_type) { - case NM_AUTH_SUBJECT_TYPE_INTERNAL: - priv->unix_process.pid = G_MAXULONG; - priv->unix_process.uid = 0; /* internal uses 'root' user */ - return; - case NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS: - /* Ensure pid and uid to be representable as int32. - * DBUS treats them as uint32, polkit library as int. */ - if (priv->unix_process.pid > MIN (G_MAXINT, G_MAXINT32)) - break; - if (priv->unix_process.uid > MIN (G_MAXINT, G_MAXINT32)) { - /* for uid==-1, libpolkit-gobject-1 detects the user based on the process id. - * Don't bother and require the user id as parameter. */ - break; - } - - priv->unix_process.start_time = nm_utils_get_start_time_for_pid (priv->unix_process.pid, NULL, NULL); - - if (!priv->unix_process.start_time) { - /* Is the process already gone? Then fail creation of the auth subject - * by clearing the type. */ - if (kill (priv->unix_process.pid, 0) != 0) - _clear_private (self); - - /* Otherwise, although we didn't detect a start_time, the process is still around. - * That could be due to procfs mounted with hidepid. So just accept the request. - * - * Polkit on the other side, will accept 0 and try to lookup /proc/$PID/stat - * itself (and if it fails to do so, assume a start-time of 0 and proceed). - * The only combination that would fail here, is when NM is able to read the - * start-time, but polkit is not. */ - } - return; - case NM_AUTH_SUBJECT_TYPE_UNIX_SESSION: - return; - default: - break; - } - - _clear_private (self); - g_return_if_reached (); -} - -static void -finalize (GObject *object) -{ - _clear_private ((NMAuthSubject *) object); - - G_OBJECT_CLASS (nm_auth_subject_parent_class)->finalize (object); -} - -static void -nm_auth_subject_class_init (NMAuthSubjectClass *config_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (config_class); - - object_class->get_property = get_property; - object_class->set_property = set_property; - object_class->constructed = constructed; - object_class->finalize = finalize; - - g_object_class_install_property - (object_class, PROP_SUBJECT_TYPE, - g_param_spec_int (NM_AUTH_SUBJECT_SUBJECT_TYPE, "", "", - NM_AUTH_SUBJECT_TYPE_INVALID, - NM_AUTH_SUBJECT_TYPE_UNIX_SESSION, - NM_AUTH_SUBJECT_TYPE_INVALID, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property - (object_class, PROP_UNIX_PROCESS_DBUS_SENDER, - g_param_spec_string (NM_AUTH_SUBJECT_UNIX_PROCESS_DBUS_SENDER, "", "", - NULL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property - (object_class, PROP_UNIX_PROCESS_PID, - g_param_spec_ulong (NM_AUTH_SUBJECT_UNIX_PROCESS_PID, "", "", - 0, G_MAXULONG, G_MAXULONG, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property - (object_class, PROP_UNIX_PROCESS_UID, - g_param_spec_ulong (NM_AUTH_SUBJECT_UNIX_PROCESS_UID, "", "", - 0, G_MAXULONG, G_MAXULONG, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property - (object_class, PROP_UNIX_SESSION_ID, - g_param_spec_string (NM_AUTH_SUBJECT_UNIX_SESSION_ID, "", "", - NULL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); -} diff --git a/shared/nm-libnm-core-intern/nm-auth-subject.h b/shared/nm-libnm-core-intern/nm-auth-subject.h deleted file mode 100644 index fc37b8e2..00000000 --- a/shared/nm-libnm-core-intern/nm-auth-subject.h +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1+ -/* - * Copyright (C) 2013 Red Hat, Inc. - */ - -#ifndef __NETWORKMANAGER_AUTH_SUBJECT_H__ -#define __NETWORKMANAGER_AUTH_SUBJECT_H__ - -#define NM_TYPE_AUTH_SUBJECT (nm_auth_subject_get_type ()) -#define NM_AUTH_SUBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_AUTH_SUBJECT, NMAuthSubject)) -#define NM_AUTH_SUBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_AUTH_SUBJECT, NMAuthSubjectClass)) -#define NM_IS_AUTH_SUBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_AUTH_SUBJECT)) -#define NM_IS_AUTH_SUBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_AUTH_SUBJECT)) -#define NM_AUTH_SUBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_AUTH_SUBJECT, NMAuthSubjectClass)) - -typedef enum { - NM_AUTH_SUBJECT_TYPE_INVALID = 0, - NM_AUTH_SUBJECT_TYPE_INTERNAL = 1, - NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS = 2, - NM_AUTH_SUBJECT_TYPE_UNIX_SESSION = 4, -} NMAuthSubjectType; - -#define NM_AUTH_SUBJECT_SUBJECT_TYPE "subject-type" -#define NM_AUTH_SUBJECT_UNIX_PROCESS_DBUS_SENDER "unix-process-dbus-sender" -#define NM_AUTH_SUBJECT_UNIX_PROCESS_PID "unix-process-pid" -#define NM_AUTH_SUBJECT_UNIX_PROCESS_UID "unix-process-uid" -#define NM_AUTH_SUBJECT_UNIX_SESSION_ID "unix-session-id" - -typedef struct _NMAuthSubjectClass NMAuthSubjectClass; -typedef struct _NMAuthSubject NMAuthSubject; - -GType nm_auth_subject_get_type (void); - -NMAuthSubject *nm_auth_subject_new_internal (void); - -NMAuthSubject *nm_auth_subject_new_unix_session (const char *session_id); - -NMAuthSubject *nm_auth_subject_new_unix_process (const char *dbus_sender, gulong pid, gulong uid); - -NMAuthSubject *nm_auth_subject_new_unix_process_self (void); - -NMAuthSubjectType nm_auth_subject_get_subject_type (NMAuthSubject *subject); - -gulong nm_auth_subject_get_unix_process_pid (NMAuthSubject *subject); - -const char *nm_auth_subject_get_unix_process_dbus_sender (NMAuthSubject *subject); - -gulong nm_auth_subject_get_unix_process_uid (NMAuthSubject *subject); - -const char *nm_auth_subject_get_unix_session_id (NMAuthSubject *subject); - -const char *nm_auth_subject_to_string (NMAuthSubject *self, char *buf, gsize buf_len); - -GVariant *nm_auth_subject_unix_to_polkit_gvariant (NMAuthSubject *self); - -#endif /* __NETWORKMANAGER_AUTH_SUBJECT_H__ */ diff --git a/shared/nm-libnm-core-intern/nm-common-macros.h b/shared/nm-libnm-core-intern/nm-common-macros.h deleted file mode 100644 index 5f011efd..00000000 --- a/shared/nm-libnm-core-intern/nm-common-macros.h +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1+ -/* - * Copyright (C) 2016 Red Hat, Inc. - */ - -#ifndef __NM_COMMON_MACROS_H__ -#define __NM_COMMON_MACROS_H__ - -/*****************************************************************************/ - -#define NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK "org.freedesktop.NetworkManager.enable-disable-network" -#define NM_AUTH_PERMISSION_SLEEP_WAKE "org.freedesktop.NetworkManager.sleep-wake" -#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI "org.freedesktop.NetworkManager.enable-disable-wifi" -#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN "org.freedesktop.NetworkManager.enable-disable-wwan" -#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX "org.freedesktop.NetworkManager.enable-disable-wimax" -#define NM_AUTH_PERMISSION_NETWORK_CONTROL "org.freedesktop.NetworkManager.network-control" -#define NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED "org.freedesktop.NetworkManager.wifi.share.protected" -#define NM_AUTH_PERMISSION_WIFI_SHARE_OPEN "org.freedesktop.NetworkManager.wifi.share.open" -#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM "org.freedesktop.NetworkManager.settings.modify.system" -#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN "org.freedesktop.NetworkManager.settings.modify.own" -#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME "org.freedesktop.NetworkManager.settings.modify.hostname" -#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS "org.freedesktop.NetworkManager.settings.modify.global-dns" -#define NM_AUTH_PERMISSION_RELOAD "org.freedesktop.NetworkManager.reload" -#define NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK "org.freedesktop.NetworkManager.checkpoint-rollback" -#define NM_AUTH_PERMISSION_ENABLE_DISABLE_STATISTICS "org.freedesktop.NetworkManager.enable-disable-statistics" -#define NM_AUTH_PERMISSION_ENABLE_DISABLE_CONNECTIVITY_CHECK "org.freedesktop.NetworkManager.enable-disable-connectivity-check" -#define NM_AUTH_PERMISSION_WIFI_SCAN "org.freedesktop.NetworkManager.wifi.scan" - -#define NM_CLONED_MAC_PRESERVE "preserve" -#define NM_CLONED_MAC_PERMANENT "permanent" -#define NM_CLONED_MAC_RANDOM "random" -#define NM_CLONED_MAC_STABLE "stable" - -static inline gboolean -NM_CLONED_MAC_IS_SPECIAL (const char *str) -{ - return NM_IN_STRSET (str, - NM_CLONED_MAC_PRESERVE, - NM_CLONED_MAC_PERMANENT, - NM_CLONED_MAC_RANDOM, - NM_CLONED_MAC_STABLE); -} - -#define NM_IAID_MAC "mac" -#define NM_IAID_PERM_MAC "perm-mac" -#define NM_IAID_IFNAME "ifname" -#define NM_IAID_STABLE "stable" - -static inline gboolean -NM_IAID_IS_SPECIAL (const char *str) -{ - return NM_IN_STRSET (str, - NM_IAID_MAC, - NM_IAID_PERM_MAC, - NM_IAID_IFNAME, - NM_IAID_STABLE); -} - -#endif /* __NM_COMMON_MACROS_H__ */ diff --git a/shared/nm-libnm-core-intern/nm-ethtool-utils.c b/shared/nm-libnm-core-intern/nm-ethtool-utils.c deleted file mode 100644 index 2bc182cf..00000000 --- a/shared/nm-libnm-core-intern/nm-ethtool-utils.c +++ /dev/null @@ -1,209 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1+ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-ethtool-utils.h" - -#include "nm-setting-ethtool.h" - -/*****************************************************************************/ - -#define ETHT_DATA(xname) \ - [NM_ETHTOOL_ID_##xname] = (&((const NMEthtoolData) { \ - .optname = NM_ETHTOOL_OPTNAME_##xname, \ - .id = NM_ETHTOOL_ID_##xname, \ - })) - -const NMEthtoolData *const nm_ethtool_data[_NM_ETHTOOL_ID_NUM + 1] = { - /* indexed by NMEthtoolID */ - ETHT_DATA (FEATURE_ESP_HW_OFFLOAD), - ETHT_DATA (FEATURE_ESP_TX_CSUM_HW_OFFLOAD), - ETHT_DATA (FEATURE_FCOE_MTU), - ETHT_DATA (FEATURE_GRO), - ETHT_DATA (FEATURE_GSO), - ETHT_DATA (FEATURE_HIGHDMA), - ETHT_DATA (FEATURE_HW_TC_OFFLOAD), - ETHT_DATA (FEATURE_L2_FWD_OFFLOAD), - ETHT_DATA (FEATURE_LOOPBACK), - ETHT_DATA (FEATURE_LRO), - ETHT_DATA (FEATURE_NTUPLE), - ETHT_DATA (FEATURE_RX), - ETHT_DATA (FEATURE_RXHASH), - ETHT_DATA (FEATURE_RXVLAN), - ETHT_DATA (FEATURE_RX_ALL), - ETHT_DATA (FEATURE_RX_FCS), - ETHT_DATA (FEATURE_RX_GRO_HW), - ETHT_DATA (FEATURE_RX_UDP_TUNNEL_PORT_OFFLOAD), - ETHT_DATA (FEATURE_RX_VLAN_FILTER), - ETHT_DATA (FEATURE_RX_VLAN_STAG_FILTER), - ETHT_DATA (FEATURE_RX_VLAN_STAG_HW_PARSE), - ETHT_DATA (FEATURE_SG), - ETHT_DATA (FEATURE_TLS_HW_RECORD), - ETHT_DATA (FEATURE_TLS_HW_TX_OFFLOAD), - ETHT_DATA (FEATURE_TSO), - ETHT_DATA (FEATURE_TX), - ETHT_DATA (FEATURE_TXVLAN), - ETHT_DATA (FEATURE_TX_CHECKSUM_FCOE_CRC), - ETHT_DATA (FEATURE_TX_CHECKSUM_IPV4), - ETHT_DATA (FEATURE_TX_CHECKSUM_IPV6), - ETHT_DATA (FEATURE_TX_CHECKSUM_IP_GENERIC), - ETHT_DATA (FEATURE_TX_CHECKSUM_SCTP), - ETHT_DATA (FEATURE_TX_ESP_SEGMENTATION), - ETHT_DATA (FEATURE_TX_FCOE_SEGMENTATION), - ETHT_DATA (FEATURE_TX_GRE_CSUM_SEGMENTATION), - ETHT_DATA (FEATURE_TX_GRE_SEGMENTATION), - ETHT_DATA (FEATURE_TX_GSO_PARTIAL), - ETHT_DATA (FEATURE_TX_GSO_ROBUST), - ETHT_DATA (FEATURE_TX_IPXIP4_SEGMENTATION), - ETHT_DATA (FEATURE_TX_IPXIP6_SEGMENTATION), - ETHT_DATA (FEATURE_TX_NOCACHE_COPY), - ETHT_DATA (FEATURE_TX_SCATTER_GATHER), - ETHT_DATA (FEATURE_TX_SCATTER_GATHER_FRAGLIST), - ETHT_DATA (FEATURE_TX_SCTP_SEGMENTATION), - ETHT_DATA (FEATURE_TX_TCP6_SEGMENTATION), - ETHT_DATA (FEATURE_TX_TCP_ECN_SEGMENTATION), - ETHT_DATA (FEATURE_TX_TCP_MANGLEID_SEGMENTATION), - ETHT_DATA (FEATURE_TX_TCP_SEGMENTATION), - ETHT_DATA (FEATURE_TX_UDP_SEGMENTATION), - ETHT_DATA (FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION), - ETHT_DATA (FEATURE_TX_UDP_TNL_SEGMENTATION), - ETHT_DATA (FEATURE_TX_VLAN_STAG_HW_INSERT), - [_NM_ETHTOOL_ID_NUM] = NULL, -}; - -static const guint8 _by_name[_NM_ETHTOOL_ID_NUM] = { - /* sorted by optname. */ - NM_ETHTOOL_ID_FEATURE_ESP_HW_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_ESP_TX_CSUM_HW_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_FCOE_MTU, - NM_ETHTOOL_ID_FEATURE_GRO, - NM_ETHTOOL_ID_FEATURE_GSO, - NM_ETHTOOL_ID_FEATURE_HIGHDMA, - NM_ETHTOOL_ID_FEATURE_HW_TC_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_L2_FWD_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_LOOPBACK, - NM_ETHTOOL_ID_FEATURE_LRO, - NM_ETHTOOL_ID_FEATURE_NTUPLE, - NM_ETHTOOL_ID_FEATURE_RX, - NM_ETHTOOL_ID_FEATURE_RX_ALL, - NM_ETHTOOL_ID_FEATURE_RX_FCS, - NM_ETHTOOL_ID_FEATURE_RX_GRO_HW, - NM_ETHTOOL_ID_FEATURE_RX_UDP_TUNNEL_PORT_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_RX_VLAN_FILTER, - NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_FILTER, - NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_HW_PARSE, - NM_ETHTOOL_ID_FEATURE_RXHASH, - NM_ETHTOOL_ID_FEATURE_RXVLAN, - NM_ETHTOOL_ID_FEATURE_SG, - NM_ETHTOOL_ID_FEATURE_TLS_HW_RECORD, - NM_ETHTOOL_ID_FEATURE_TLS_HW_TX_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_TSO, - NM_ETHTOOL_ID_FEATURE_TX, - NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_FCOE_CRC, - NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IP_GENERIC, - NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV4, - NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV6, - NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_SCTP, - NM_ETHTOOL_ID_FEATURE_TX_ESP_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_FCOE_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_GRE_CSUM_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_GRE_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_GSO_PARTIAL, - NM_ETHTOOL_ID_FEATURE_TX_GSO_ROBUST, - NM_ETHTOOL_ID_FEATURE_TX_IPXIP4_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_IPXIP6_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_NOCACHE_COPY, - NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER, - NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER_FRAGLIST, - NM_ETHTOOL_ID_FEATURE_TX_SCTP_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_TCP_ECN_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_TCP_MANGLEID_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_TCP_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_UDP_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT, - NM_ETHTOOL_ID_FEATURE_TXVLAN, -}; - -/*****************************************************************************/ - -static void -_ASSERT_data (void) -{ -#if NM_MORE_ASSERTS > 10 - int i; - - G_STATIC_ASSERT_EXPR (_NM_ETHTOOL_ID_FIRST == 0); - G_STATIC_ASSERT_EXPR (_NM_ETHTOOL_ID_LAST == _NM_ETHTOOL_ID_NUM - 1); - G_STATIC_ASSERT_EXPR (_NM_ETHTOOL_ID_NUM > 0); - - nm_assert (NM_PTRARRAY_LEN (nm_ethtool_data) == _NM_ETHTOOL_ID_NUM); - nm_assert (G_N_ELEMENTS (_by_name) == _NM_ETHTOOL_ID_NUM); - nm_assert (G_N_ELEMENTS (nm_ethtool_data) == _NM_ETHTOOL_ID_NUM + 1); - - for (i = 0; i < _NM_ETHTOOL_ID_NUM; i++) { - const NMEthtoolData *d = nm_ethtool_data[i]; - - nm_assert (d); - nm_assert (d->id == (NMEthtoolID) i); - nm_assert (d->optname && d->optname[0]); - } - - for (i = 0; i < _NM_ETHTOOL_ID_NUM; i++) { - NMEthtoolID id = _by_name[i]; - const NMEthtoolData *d; - - nm_assert (id >= 0); - nm_assert (id < _NM_ETHTOOL_ID_NUM); - - d = nm_ethtool_data[id]; - if (i > 0) { - /* since we assert that all optnames are sorted strictly monotonically increasing, - * it also follows that there are no duplicates in the _by_name. - * It also follows, that all names in nm_ethtool_data are unique. */ - if (strcmp (nm_ethtool_data[_by_name[i - 1]]->optname, d->optname) >= 0) { - g_error ("nm_ethtool_data is not sorted asciibetically: %u/%s should be after %u/%s", - i - 1, nm_ethtool_data[_by_name[i - 1]]->optname, - i, d->optname); - } - } - } -#endif -} - -static int -_by_name_cmp (gconstpointer a, - gconstpointer b, - gpointer user_data) -{ - const guint8 *p_id = a; - const char *optname = b; - - nm_assert (p_id && p_id >= _by_name && p_id <= &_by_name[_NM_ETHTOOL_ID_NUM]); - nm_assert (*p_id < _NM_ETHTOOL_ID_NUM); - - return strcmp (nm_ethtool_data[*p_id]->optname, optname); -} - -const NMEthtoolData * -nm_ethtool_data_get_by_optname (const char *optname) -{ - gssize idx; - - nm_assert (optname); - - _ASSERT_data (); - - idx = nm_utils_array_find_binary_search ((gconstpointer *) _by_name, - sizeof (_by_name[0]), - _NM_ETHTOOL_ID_NUM, - optname, - _by_name_cmp, - NULL); - return (idx < 0) ? NULL : nm_ethtool_data[_by_name[idx]]; -} diff --git a/shared/nm-libnm-core-intern/nm-ethtool-utils.h b/shared/nm-libnm-core-intern/nm-ethtool-utils.h deleted file mode 100644 index b3413de2..00000000 --- a/shared/nm-libnm-core-intern/nm-ethtool-utils.h +++ /dev/null @@ -1,105 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1+ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#ifndef __NM_ETHTOOL_UTILS_H__ -#define __NM_ETHTOOL_UTILS_H__ - -/*****************************************************************************/ - -typedef enum { - NM_ETHTOOL_ID_UNKNOWN = -1, - - _NM_ETHTOOL_ID_FIRST = 0, - - _NM_ETHTOOL_ID_FEATURE_FIRST = _NM_ETHTOOL_ID_FIRST, - NM_ETHTOOL_ID_FEATURE_ESP_HW_OFFLOAD = _NM_ETHTOOL_ID_FEATURE_FIRST, - NM_ETHTOOL_ID_FEATURE_ESP_TX_CSUM_HW_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_FCOE_MTU, - NM_ETHTOOL_ID_FEATURE_GRO, - NM_ETHTOOL_ID_FEATURE_GSO, - NM_ETHTOOL_ID_FEATURE_HIGHDMA, - NM_ETHTOOL_ID_FEATURE_HW_TC_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_L2_FWD_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_LOOPBACK, - NM_ETHTOOL_ID_FEATURE_LRO, - NM_ETHTOOL_ID_FEATURE_NTUPLE, - NM_ETHTOOL_ID_FEATURE_RX, - NM_ETHTOOL_ID_FEATURE_RXHASH, - NM_ETHTOOL_ID_FEATURE_RXVLAN, - NM_ETHTOOL_ID_FEATURE_RX_ALL, - NM_ETHTOOL_ID_FEATURE_RX_FCS, - NM_ETHTOOL_ID_FEATURE_RX_GRO_HW, - NM_ETHTOOL_ID_FEATURE_RX_UDP_TUNNEL_PORT_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_RX_VLAN_FILTER, - NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_FILTER, - NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_HW_PARSE, - NM_ETHTOOL_ID_FEATURE_SG, - NM_ETHTOOL_ID_FEATURE_TLS_HW_RECORD, - NM_ETHTOOL_ID_FEATURE_TLS_HW_TX_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_TSO, - NM_ETHTOOL_ID_FEATURE_TX, - NM_ETHTOOL_ID_FEATURE_TXVLAN, - NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_FCOE_CRC, - NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV4, - NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV6, - NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IP_GENERIC, - NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_SCTP, - NM_ETHTOOL_ID_FEATURE_TX_ESP_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_FCOE_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_GRE_CSUM_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_GRE_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_GSO_PARTIAL, - NM_ETHTOOL_ID_FEATURE_TX_GSO_ROBUST, - NM_ETHTOOL_ID_FEATURE_TX_IPXIP4_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_IPXIP6_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_NOCACHE_COPY, - NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER, - NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER_FRAGLIST, - NM_ETHTOOL_ID_FEATURE_TX_SCTP_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_TCP_ECN_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_TCP_MANGLEID_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_TCP_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_UDP_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT, - _NM_ETHTOOL_ID_FEATURE_LAST = NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT, - _NM_ETHTOOL_ID_FEATURE_NUM = (_NM_ETHTOOL_ID_FEATURE_LAST - _NM_ETHTOOL_ID_FEATURE_FIRST + 1), - - _NM_ETHTOOL_ID_LAST = _NM_ETHTOOL_ID_FEATURE_LAST, - - _NM_ETHTOOL_ID_NUM = (_NM_ETHTOOL_ID_LAST - _NM_ETHTOOL_ID_FIRST + 1), -} NMEthtoolID; - -typedef struct { - const char *optname; - NMEthtoolID id; -} NMEthtoolData; - -extern const NMEthtoolData *const nm_ethtool_data[_NM_ETHTOOL_ID_NUM + 1]; - -const NMEthtoolData *nm_ethtool_data_get_by_optname (const char *optname); - -/****************************************************************************/ - -static inline NMEthtoolID -nm_ethtool_id_get_by_name (const char *optname) -{ - const NMEthtoolData *d; - - d = nm_ethtool_data_get_by_optname (optname); - return d ? d->id : NM_ETHTOOL_ID_UNKNOWN; -} - -static inline gboolean -nm_ethtool_id_is_feature (NMEthtoolID id) -{ - return id >= _NM_ETHTOOL_ID_FEATURE_FIRST && id <= _NM_ETHTOOL_ID_FEATURE_LAST; -} - -/****************************************************************************/ - -#endif /* __NM_ETHTOOL_UTILS_H__ */ diff --git a/shared/nm-libnm-core-intern/nm-libnm-core-utils.c b/shared/nm-libnm-core-intern/nm-libnm-core-utils.c deleted file mode 100644 index df2f2e77..00000000 --- a/shared/nm-libnm-core-intern/nm-libnm-core-utils.c +++ /dev/null @@ -1,180 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1+ - -#include "nm-default.h" - -#include "nm-libnm-core-utils.h" - -#include "nm-common-macros.h" - -/*****************************************************************************/ - -gboolean -nm_utils_vlan_priority_map_parse_str (NMVlanPriorityMap map_type, - const char *str, - gboolean allow_wildcard_to, - guint32 *out_from, - guint32 *out_to, - gboolean *out_has_wildcard_to) -{ - const char *s2; - gint64 v1, v2; - - nm_assert (str); - - s2 = strchr (str, ':'); - - if (!s2) { - if (!allow_wildcard_to) - return FALSE; - v1 = _nm_utils_ascii_str_to_int64 (str, 10, 0, G_MAXUINT32, -1); - v2 = -1; - } else { - gs_free char *s1_free = NULL; - gsize s1_len = (s2 - str); - - s2 = nm_str_skip_leading_spaces (&s2[1]); - if ( s2[0] == '\0' - || ( s2[0] == '*' - && NM_STRCHAR_ALL (&s2[1], ch, g_ascii_isspace (ch)))) { - if (!allow_wildcard_to) - return FALSE; - v2 = -1; - } else { - v2 = _nm_utils_ascii_str_to_int64 (s2, 10, 0, G_MAXUINT32, -1); - if ( v2 < 0 - || (guint32) v2 > nm_utils_vlan_priority_map_get_max_prio (map_type, FALSE)) - return FALSE; - } - - v1 = _nm_utils_ascii_str_to_int64 (nm_strndup_a (100, str, s1_len, &s1_free), - 10, 0, G_MAXUINT32, -1); - } - - if ( v1 < 0 - || (guint32) v1 > nm_utils_vlan_priority_map_get_max_prio (map_type, TRUE)) - return FALSE; - - NM_SET_OUT (out_from, v1); - NM_SET_OUT (out_to, v2 < 0 - ? 0u - : (guint) v2); - NM_SET_OUT (out_has_wildcard_to, v2 < 0); - return TRUE; -} - -/*****************************************************************************/ - -const char *const nm_auth_permission_names_by_idx[NM_CLIENT_PERMISSION_LAST] = { - [NM_CLIENT_PERMISSION_CHECKPOINT_ROLLBACK - 1] = NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, - [NM_CLIENT_PERMISSION_ENABLE_DISABLE_CONNECTIVITY_CHECK - 1] = NM_AUTH_PERMISSION_ENABLE_DISABLE_CONNECTIVITY_CHECK, - [NM_CLIENT_PERMISSION_ENABLE_DISABLE_NETWORK - 1] = NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK, - [NM_CLIENT_PERMISSION_ENABLE_DISABLE_STATISTICS - 1] = NM_AUTH_PERMISSION_ENABLE_DISABLE_STATISTICS, - [NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIFI - 1] = NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI, - [NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIMAX - 1] = NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX, - [NM_CLIENT_PERMISSION_ENABLE_DISABLE_WWAN - 1] = NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN, - [NM_CLIENT_PERMISSION_NETWORK_CONTROL - 1] = NM_AUTH_PERMISSION_NETWORK_CONTROL, - [NM_CLIENT_PERMISSION_RELOAD - 1] = NM_AUTH_PERMISSION_RELOAD, - [NM_CLIENT_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS - 1] = NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS, - [NM_CLIENT_PERMISSION_SETTINGS_MODIFY_HOSTNAME - 1] = NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME, - [NM_CLIENT_PERMISSION_SETTINGS_MODIFY_OWN - 1] = NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN, - [NM_CLIENT_PERMISSION_SETTINGS_MODIFY_SYSTEM - 1] = NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM, - [NM_CLIENT_PERMISSION_SLEEP_WAKE - 1] = NM_AUTH_PERMISSION_SLEEP_WAKE, - [NM_CLIENT_PERMISSION_WIFI_SCAN - 1] = NM_AUTH_PERMISSION_WIFI_SCAN, - [NM_CLIENT_PERMISSION_WIFI_SHARE_OPEN - 1] = NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, - [NM_CLIENT_PERMISSION_WIFI_SHARE_PROTECTED - 1] = NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, -}; - -const NMClientPermission nm_auth_permission_sorted[NM_CLIENT_PERMISSION_LAST] = { - NM_CLIENT_PERMISSION_CHECKPOINT_ROLLBACK, - NM_CLIENT_PERMISSION_ENABLE_DISABLE_CONNECTIVITY_CHECK, - NM_CLIENT_PERMISSION_ENABLE_DISABLE_NETWORK, - NM_CLIENT_PERMISSION_ENABLE_DISABLE_STATISTICS, - NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIFI, - NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIMAX, - NM_CLIENT_PERMISSION_ENABLE_DISABLE_WWAN, - NM_CLIENT_PERMISSION_NETWORK_CONTROL, - NM_CLIENT_PERMISSION_RELOAD, - NM_CLIENT_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS, - NM_CLIENT_PERMISSION_SETTINGS_MODIFY_HOSTNAME, - NM_CLIENT_PERMISSION_SETTINGS_MODIFY_OWN, - NM_CLIENT_PERMISSION_SETTINGS_MODIFY_SYSTEM, - NM_CLIENT_PERMISSION_SLEEP_WAKE, - NM_CLIENT_PERMISSION_WIFI_SCAN, - NM_CLIENT_PERMISSION_WIFI_SHARE_OPEN, - NM_CLIENT_PERMISSION_WIFI_SHARE_PROTECTED, -}; - -const char * -nm_auth_permission_to_string (NMClientPermission permission) -{ - if (permission < 1) - return NULL; - if (permission > NM_CLIENT_PERMISSION_LAST) - return NULL; - return nm_auth_permission_names_by_idx[permission - 1]; -} - -#define AUTH_PERMISSION_PREFIX "org.freedesktop.NetworkManager." - -static int -_nm_auth_permission_from_string_cmp (gconstpointer a, gconstpointer b, gpointer user_data) -{ - const NMClientPermission *const p = a; - const char *const needle = b; - const char *ss = nm_auth_permission_names_by_idx[*p - 1]; - - nm_assert (NM_STR_HAS_PREFIX (ss, AUTH_PERMISSION_PREFIX)); - nm_assert (ss[NM_STRLEN (AUTH_PERMISSION_PREFIX)] != '\0'); - - return strcmp (&ss[NM_STRLEN (AUTH_PERMISSION_PREFIX)], needle); -} - -NMClientPermission -nm_auth_permission_from_string (const char *str) -{ - gssize idx; - - if (!str) - return NM_CLIENT_PERMISSION_NONE; - - if (!NM_STR_HAS_PREFIX (str, AUTH_PERMISSION_PREFIX)) - return NM_CLIENT_PERMISSION_NONE; - idx = nm_utils_array_find_binary_search (nm_auth_permission_sorted, - sizeof (nm_auth_permission_sorted[0]), - G_N_ELEMENTS (nm_auth_permission_sorted), - &str[NM_STRLEN (AUTH_PERMISSION_PREFIX)], - _nm_auth_permission_from_string_cmp, - NULL); - if (idx < 0) - return NM_CLIENT_PERMISSION_NONE; - return nm_auth_permission_sorted[idx]; -} - -/*****************************************************************************/ - -NMClientPermissionResult -nm_client_permission_result_from_string (const char *nm) -{ - if (!nm) - return NM_CLIENT_PERMISSION_RESULT_UNKNOWN; - if (nm_streq (nm, "yes")) - return NM_CLIENT_PERMISSION_RESULT_YES; - if (nm_streq (nm, "no")) - return NM_CLIENT_PERMISSION_RESULT_NO; - if (nm_streq (nm, "auth")) - return NM_CLIENT_PERMISSION_RESULT_AUTH; - return NM_CLIENT_PERMISSION_RESULT_UNKNOWN; -} - -const char * -nm_client_permission_result_to_string (NMClientPermissionResult permission) -{ - switch (permission) { - case NM_CLIENT_PERMISSION_RESULT_YES: return "yes"; - case NM_CLIENT_PERMISSION_RESULT_NO: return "no"; - case NM_CLIENT_PERMISSION_RESULT_AUTH: return "auth"; - case NM_CLIENT_PERMISSION_RESULT_UNKNOWN: return "unknown"; - } - nm_assert_not_reached (); - return NULL; -} diff --git a/shared/nm-libnm-core-intern/nm-libnm-core-utils.h b/shared/nm-libnm-core-intern/nm-libnm-core-utils.h deleted file mode 100644 index a35a5e15..00000000 --- a/shared/nm-libnm-core-intern/nm-libnm-core-utils.h +++ /dev/null @@ -1,115 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1+ - -#ifndef __NM_LIBNM_SHARED_UTILS_H__ -#define __NM_LIBNM_SHARED_UTILS_H__ - -/****************************************************************************/ - -#include "nm-setting-bridge.h" -#include "nm-setting-connection.h" -#include "nm-setting-ip-config.h" -#include "nm-setting-ip4-config.h" -#include "nm-setting-ip6-config.h" -#include "nm-setting-sriov.h" -#include "nm-setting-team.h" -#include "nm-setting-vlan.h" -#include "nm-setting-wireguard.h" - -/****************************************************************************/ - -#define nm_auto_unref_ip_address nm_auto (_nm_ip_address_unref) -NM_AUTO_DEFINE_FCN0 (NMIPAddress *, _nm_ip_address_unref, nm_ip_address_unref) - -#define nm_auto_unref_ip_route nm_auto (_nm_auto_unref_ip_route) -NM_AUTO_DEFINE_FCN0 (NMIPRoute *, _nm_auto_unref_ip_route, nm_ip_route_unref) - -#define nm_auto_unref_ip_routing_rule nm_auto(_nm_auto_unref_ip_routing_rule) -NM_AUTO_DEFINE_FCN0 (NMIPRoutingRule *, _nm_auto_unref_ip_routing_rule, nm_ip_routing_rule_unref) - -#define nm_auto_unref_sriov_vf nm_auto (_nm_auto_unref_sriov_vf) -NM_AUTO_DEFINE_FCN0 (NMSriovVF *, _nm_auto_unref_sriov_vf, nm_sriov_vf_unref) - -#define nm_auto_unref_tc_qdisc nm_auto (_nm_auto_unref_tc_qdisc) -NM_AUTO_DEFINE_FCN0 (NMTCQdisc *, _nm_auto_unref_tc_qdisc, nm_tc_qdisc_unref) - -#define nm_auto_unref_tc_tfilter nm_auto (_nm_auto_unref_tc_tfilter) -NM_AUTO_DEFINE_FCN0 (NMTCTfilter *, _nm_auto_unref_tc_tfilter, nm_tc_tfilter_unref) - -#define nm_auto_unref_bridge_vlan nm_auto (_nm_auto_unref_bridge_vlan) -NM_AUTO_DEFINE_FCN0 (NMBridgeVlan *, _nm_auto_unref_bridge_vlan, nm_bridge_vlan_unref) - -#define nm_auto_unref_team_link_watcher nm_auto (_nm_auto_unref_team_link_watcher) -NM_AUTO_DEFINE_FCN0 (NMTeamLinkWatcher *, _nm_auto_unref_team_link_watcher, nm_team_link_watcher_unref) - -#define nm_auto_unref_wgpeer nm_auto (_nm_auto_unref_wgpeer) -NM_AUTO_DEFINE_FCN0 (NMWireGuardPeer *, _nm_auto_unref_wgpeer, nm_wireguard_peer_unref) - -/****************************************************************************/ - -static inline guint32 -nm_utils_vlan_priority_map_get_max_prio (NMVlanPriorityMap map, gboolean from) -{ - if (map == NM_VLAN_INGRESS_MAP) { - return from - ? 7u /* MAX_8021P_PRIO */ - : (guint32) G_MAXUINT32 /* MAX_SKB_PRIO */; - } - nm_assert (map == NM_VLAN_EGRESS_MAP); - return from - ? (guint32) G_MAXUINT32 /* MAX_SKB_PRIO */ - : 7u /* MAX_8021P_PRIO */; -} - -gboolean nm_utils_vlan_priority_map_parse_str (NMVlanPriorityMap map_type, - const char *str, - gboolean allow_wildcard_to, - guint32 *out_from, - guint32 *out_to, - gboolean *out_has_wildcard_to); - -/*****************************************************************************/ - -static inline int -nm_setting_ip_config_get_addr_family (NMSettingIPConfig *s_ip) -{ - if (NM_IS_SETTING_IP4_CONFIG (s_ip)) - return AF_INET; - if (NM_IS_SETTING_IP6_CONFIG (s_ip)) - return AF_INET6; - g_return_val_if_reached (AF_UNSPEC); -} - -/*****************************************************************************/ - -/* The maximum MTU for infiniband. - * - * This is both in transport-mode "datagram" and "connected" - * and they both have the same maximum define. - * - * Note that in the past, MTU in "datagram" mode was restricted - * to 2044 bytes. That is no longer the case and we accept large - * MTUs. - * - * This define is the maxiumum for the MTU in a connection profile (the - * setting). Whether large MTUs can be configured later (at activation time) - * depends on other factors. */ -#define NM_INFINIBAND_MAX_MTU ((guint) 65520) - -/*****************************************************************************/ - -#define _NM_CAPABILITY_MAX NM_CAPABILITY_OVS - -/*****************************************************************************/ - -extern const char *const nm_auth_permission_names_by_idx[NM_CLIENT_PERMISSION_LAST]; -extern const NMClientPermission nm_auth_permission_sorted[NM_CLIENT_PERMISSION_LAST]; - -const char *nm_auth_permission_to_string (NMClientPermission permission); -NMClientPermission nm_auth_permission_from_string (const char *str); - -/*****************************************************************************/ - -NMClientPermissionResult nm_client_permission_result_from_string (const char *nm); -const char *nm_client_permission_result_to_string (NMClientPermissionResult permission); - -#endif /* __NM_LIBNM_SHARED_UTILS_H__ */ diff --git a/shared/nm-test-libnm-utils.h b/shared/nm-test-libnm-utils.h index 06dbc72d..04e8fb64 100644 --- a/shared/nm-test-libnm-utils.h +++ b/shared/nm-test-libnm-utils.h @@ -3,6 +3,9 @@ * Copyright (C) 2014 - 2015 Red Hat, Inc. */ +#ifndef __NM_TEST_LIBNM_UTILS_H__ +#define __NM_TEST_LIBNM_UTILS_H__ + #include "NetworkManager.h" #include "nm-utils/nm-test-utils.h" @@ -82,3 +85,5 @@ nmtstc_client_new (gboolean allow_iterate_main_context) { return nmtstc_context_object_new (NM_TYPE_CLIENT, allow_iterate_main_context, NULL); } + +#endif /* __NM_TEST_LIBNM_UTILS_H__ */ diff --git a/shared/nm-utils/nm-test-utils.h b/shared/nm-utils/nm-test-utils.h index 96e36135..c270d2b9 100644 --- a/shared/nm-utils/nm-test-utils.h +++ b/shared/nm-utils/nm-test-utils.h @@ -1924,8 +1924,8 @@ nmtst_assert_connection_equals (NMConnection *a, gboolean normalize_a, NMConnect gs_unref_keyfile GKeyFile *kf_a = NULL, *kf_b = NULL; gs_free char *str_a = NULL, *str_b = NULL; - kf_a = nm_keyfile_write (a, NULL, NULL, NULL); - kf_b = nm_keyfile_write (b, NULL, NULL, NULL); + kf_a = nm_keyfile_write (a, NM_KEYFILE_HANDLER_FLAGS_NONE, NULL, NULL, NULL); + kf_b = nm_keyfile_write (b, NM_KEYFILE_HANDLER_FLAGS_NONE, NULL, NULL, NULL); if (kf_a) str_a = g_key_file_to_data (kf_a, NULL, NULL); @@ -2262,7 +2262,7 @@ nmtst_create_connection_from_keyfile (const char *keyfile_str, const char *full_ success = g_key_file_load_from_data (keyfile, keyfile_str, strlen (keyfile_str), G_KEY_FILE_NONE, &error); nmtst_assert_success (success, error); - con = nm_keyfile_read (keyfile, base_dir, NULL, NULL, &error); + con = nm_keyfile_read (keyfile, base_dir, NM_KEYFILE_HANDLER_FLAGS_NONE, NULL, NULL, &error); nmtst_assert_success (NM_IS_CONNECTION (con), error); nm_keyfile_read_ensure_id (con, filename); @@ -2326,6 +2326,43 @@ _nmtst_variant_new_vardict (int dummy, ...) g_assert_cmpint (_l, ==, strlen (_str)); \ } G_STMT_END +#ifdef __NM_SHARED_UTILS_H__ +#define _nmtst_assert_variant_bytestring_cmp_str(_ptr, _ptr2, _len) \ + G_STMT_START { \ + if (memcmp (_ptr2, _ptr, _len) != 0) { \ + gs_free char *_x1 = NULL; \ + gs_free char *_x2 = NULL; \ + const char *_xx1; \ + const char *_xx2; \ + \ + _xx1 = nm_utils_buf_utf8safe_escape (_ptr, _len, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, &_x1); \ + _xx2 = nm_utils_buf_utf8safe_escape (_ptr2, _len, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, &_x2); \ + g_assert_cmpstr (_xx1, ==, _xx2); \ + g_assert_not_reached (); \ + } \ + } G_STMT_END +#else +#define _nmtst_assert_variant_bytestring_cmp_str(_ptr, _ptr2, _len) G_STMT_START { } G_STMT_END +#endif + +#define nmtst_assert_variant_bytestring(variant, ptr, len) \ + G_STMT_START { \ + GVariant *_variant = (variant); \ + gconstpointer _ptr = (ptr); \ + gconstpointer _ptr2; \ + gsize _len = (len); \ + gsize _len2; \ + \ + nmtst_assert_variant_is_of_type (_variant, G_VARIANT_TYPE_BYTESTRING); \ + _ptr2 = g_variant_get_fixed_array (_variant, &_len2, 1); \ + g_assert_cmpint (_len2, ==, _len); \ + if ( _len != 0 \ + && _ptr) { \ + _nmtst_assert_variant_bytestring_cmp_str(_ptr, _ptr2, _len); \ + g_assert_cmpmem (_ptr2, _len2, _ptr, _len); \ + } \ + } G_STMT_END + typedef enum { NMTST_VARIANT_EDITOR_CONNECTION, NMTST_VARIANT_EDITOR_SETTING, diff --git a/shared/nm-version-macros.h b/shared/nm-version-macros.h index 4858a066..40bfaaa9 100644 --- a/shared/nm-version-macros.h +++ b/shared/nm-version-macros.h @@ -22,7 +22,7 @@ * Evaluates to the minor version number of NetworkManager which this source * is compiled against. */ -#define NM_MINOR_VERSION (24) +#define NM_MINOR_VERSION (25) /** * NM_MICRO_VERSION: @@ -30,7 +30,7 @@ * Evaluates to the micro version number of NetworkManager which this source * compiled against. */ -#define NM_MICRO_VERSION (2) +#define NM_MICRO_VERSION (90) /** * NM_CHECK_VERSION: @@ -64,6 +64,7 @@ #define NM_VERSION_1_20 (NM_ENCODE_VERSION (1, 20, 0)) #define NM_VERSION_1_22 (NM_ENCODE_VERSION (1, 22, 0)) #define NM_VERSION_1_24 (NM_ENCODE_VERSION (1, 24, 0)) +#define NM_VERSION_1_26 (NM_ENCODE_VERSION (1, 26, 0)) /* For releases, NM_API_VERSION is equal to NM_VERSION. * diff --git a/shared/nm-version-macros.h.in b/shared/nm-version-macros.h.in index e97f506c..83da86ce 100644 --- a/shared/nm-version-macros.h.in +++ b/shared/nm-version-macros.h.in @@ -64,6 +64,7 @@ #define NM_VERSION_1_20 (NM_ENCODE_VERSION (1, 20, 0)) #define NM_VERSION_1_22 (NM_ENCODE_VERSION (1, 22, 0)) #define NM_VERSION_1_24 (NM_ENCODE_VERSION (1, 24, 0)) +#define NM_VERSION_1_26 (NM_ENCODE_VERSION (1, 26, 0)) /* For releases, NM_API_VERSION is equal to NM_VERSION. * diff --git a/shared/systemd/nm-sd-utils-shared.c b/shared/systemd/nm-sd-utils-shared.c index 85fd6a35..4444e6c7 100644 --- a/shared/systemd/nm-sd-utils-shared.c +++ b/shared/systemd/nm-sd-utils-shared.c @@ -13,6 +13,7 @@ #include "hexdecoct.h" #include "hostname-util.h" #include "path-util.h" +#include "web-util.h" /*****************************************************************************/ @@ -94,3 +95,45 @@ gboolean nm_sd_hostname_is_valid (const char *s, bool allow_trailing_dot) { return hostname_is_valid (s, allow_trailing_dot); } + +/*****************************************************************************/ + +static gboolean +_http_url_is_valid (const char *url, gboolean only_https) +{ + if ( !url + || !url[0]) + return FALSE; + + if ( !only_https + && NM_STR_HAS_PREFIX (url, "http://")) + url += NM_STRLEN ("http://"); + else if (NM_STR_HAS_PREFIX (url, "https://")) + url += NM_STRLEN ("https://"); + else + return FALSE; + + if (!url[0]) + return FALSE; + + return !NM_STRCHAR_ANY (url, ch, (guchar) ch >= 128u); +} + +gboolean +nm_sd_http_url_is_valid_https (const char *url) +{ + /* We use this function to verify connection:mud-url property, it must thus + * not change behavior. + * + * Note that sd_dhcp_client_set_mud_url() and sd_dhcp6_client_set_request_mud_url() + * assert with http_url_is_valid() that the argument is valid. We thus must make + * sure to only pass URLs that are valid according to http_url_is_valid(). + * + * This is given, because our nm_sd_http_url_is_valid_https() is more strict + * than http_url_is_valid(). + * + * We only must make sure that this is also correct in the future, when we + * re-import systemd code. */ + nm_assert (_http_url_is_valid (url, FALSE) == http_url_is_valid (url)); + return _http_url_is_valid (url, TRUE); +} diff --git a/shared/systemd/nm-sd-utils-shared.h b/shared/systemd/nm-sd-utils-shared.h index 3495a31d..a3ca1edc 100644 --- a/shared/systemd/nm-sd-utils-shared.h +++ b/shared/systemd/nm-sd-utils-shared.h @@ -34,4 +34,8 @@ int nm_sd_dns_name_to_wire_format (const char *domain, int nm_sd_dns_name_is_valid (const char *s); gboolean nm_sd_hostname_is_valid(const char *s, bool allow_trailing_dot); +/*****************************************************************************/ + +gboolean nm_sd_http_url_is_valid_https (const char *url); + #endif /* __NM_SD_UTILS_SHARED_H__ */ diff --git a/shared/systemd/sd-adapt-shared/blockdev-util.h b/shared/systemd/sd-adapt-shared/blockdev-util.h new file mode 100644 index 00000000..637892c2 --- /dev/null +++ b/shared/systemd/sd-adapt-shared/blockdev-util.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/shared/systemd/src/basic/cgroup-util.h b/shared/systemd/src/basic/cgroup-util.h index 237139fa..2b88571b 100644 --- a/shared/systemd/src/basic/cgroup-util.h +++ b/shared/systemd/src/basic/cgroup-util.h @@ -180,9 +180,31 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path); int cg_rmdir(const char *controller, const char *path); +typedef enum { + CG_KEY_MODE_GRACEFUL = 1 << 0, +} CGroupKeyMode; + int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value); int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret); -int cg_get_keyed_attribute(const char *controller, const char *path, const char *attribute, char **keys, char **values); +int cg_get_keyed_attribute_full(const char *controller, const char *path, const char *attribute, char **keys, char **values, CGroupKeyMode mode); + +static inline int cg_get_keyed_attribute( + const char *controller, + const char *path, + const char *attribute, + char **keys, + char **ret_values) { + return cg_get_keyed_attribute_full(controller, path, attribute, keys, ret_values, 0); +} + +static inline int cg_get_keyed_attribute_graceful( + const char *controller, + const char *path, + const char *attribute, + char **keys, + char **ret_values) { + return cg_get_keyed_attribute_full(controller, path, attribute, keys, ret_values, CG_KEY_MODE_GRACEFUL); +} int cg_get_attribute_as_uint64(const char *controller, const char *path, const char *attribute, uint64_t *ret); @@ -238,6 +260,7 @@ int cg_mask_to_string(CGroupMask mask, char **ret); int cg_kernel_controllers(Set **controllers); bool cg_ns_supported(void); +bool cg_freezer_supported(void); int cg_all_unified(void); int cg_hybrid_unified(void); diff --git a/shared/systemd/src/basic/errno-util.h b/shared/systemd/src/basic/errno-util.h index 65a6384e..0ca650f4 100644 --- a/shared/systemd/src/basic/errno-util.h +++ b/shared/systemd/src/basic/errno-util.h @@ -87,12 +87,16 @@ static inline bool ERRNO_IS_RESOURCE(int r) { ENOMEM); } -/* Three different errors for "operation/system call/ioctl not supported" */ +/* Seven different errors for "operation/system call/ioctl/socket feature not supported" */ static inline bool ERRNO_IS_NOT_SUPPORTED(int r) { return IN_SET(abs(r), EOPNOTSUPP, ENOTTY, - ENOSYS); + ENOSYS, + EAFNOSUPPORT, + EPFNOSUPPORT, + EPROTONOSUPPORT, + ESOCKTNOSUPPORT); } /* Two different errors for access problems */ diff --git a/shared/systemd/src/basic/escape.c b/shared/systemd/src/basic/escape.c index 2cc5be18..1ea0b6a6 100644 --- a/shared/systemd/src/basic/escape.c +++ b/shared/systemd/src/basic/escape.c @@ -522,22 +522,28 @@ char* shell_maybe_quote(const char *s, EscapeStyle style) { return NULL; t = r; - if (style == ESCAPE_BACKSLASH) + switch (style) { + case ESCAPE_BACKSLASH: + case ESCAPE_BACKSLASH_ONELINE: *(t++) = '"'; - else if (style == ESCAPE_POSIX) { + break; + case ESCAPE_POSIX: *(t++) = '$'; *(t++) = '\''; - } else + break; + default: assert_not_reached("Bad EscapeStyle"); + } t = mempcpy(t, s, p - s); - if (style == ESCAPE_BACKSLASH) - t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE, false); + if (IN_SET(style, ESCAPE_BACKSLASH, ESCAPE_BACKSLASH_ONELINE)) + t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE, + style == ESCAPE_BACKSLASH_ONELINE); else t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE_POSIX, true); - if (style == ESCAPE_BACKSLASH) + if (IN_SET(style, ESCAPE_BACKSLASH, ESCAPE_BACKSLASH_ONELINE)) *(t++) = '"'; else *(t++) = '\''; diff --git a/shared/systemd/src/basic/escape.h b/shared/systemd/src/basic/escape.h index b8eb137c..0b00b116 100644 --- a/shared/systemd/src/basic/escape.h +++ b/shared/systemd/src/basic/escape.h @@ -34,8 +34,13 @@ typedef enum UnescapeFlags { } UnescapeFlags; typedef enum EscapeStyle { - ESCAPE_BACKSLASH = 1, - ESCAPE_POSIX = 2, + ESCAPE_BACKSLASH = 1, /* Add shell quotes ("") so the shell will consider this a single + argument, possibly multiline. Tabs and newlines are not escaped. */ + ESCAPE_BACKSLASH_ONELINE = 2, /* Similar to ESCAPE_BACKSLASH, but always produces a single-line + string instead. Shell escape sequences are produced for tabs and + newlines. */ + ESCAPE_POSIX = 3, /* Similar to ESCAPE_BACKSLASH_ONELINE, but uses POSIX shell escape + * syntax (a string enclosed in $'') instead of plain quotes. */ } EscapeStyle; char *cescape(const char *s); diff --git a/shared/systemd/src/basic/fd-util.c b/shared/systemd/src/basic/fd-util.c index ea2c1612..a401663e 100644 --- a/shared/systemd/src/basic/fd-util.c +++ b/shared/systemd/src/basic/fd-util.c @@ -23,9 +23,10 @@ #include "path-util.h" #include "process-util.h" #include "socket-util.h" +#include "stat-util.h" #include "stdio-util.h" -#include "util.h" #include "tmpfile-util.h" +#include "util.h" /* The maximum number of iterations in the loop to close descriptors in the fallback case * when /proc/self/fd/ is inaccessible. */ @@ -104,13 +105,16 @@ int fclose_nointr(FILE *f) { /* Same as close_nointr(), but for fclose() */ + errno = 0; /* Extra safety: if the FILE* object is not encapsulating an fd, it might not set errno + * correctly. Let's hence initialize it to zero first, so that we aren't confused by any + * prior errno here */ if (fclose(f) == 0) return 0; if (errno == EINTR) return 0; - return -errno; + return errno_or_else(EIO); } FILE* safe_fclose(FILE *f) { @@ -146,11 +150,7 @@ int fd_nonblock(int fd, bool nonblock) { if (flags < 0) return -errno; - if (nonblock) - nflags = flags | O_NONBLOCK; - else - nflags = flags & ~O_NONBLOCK; - + nflags = UPDATE_FLAG(flags, O_NONBLOCK, nonblock); if (nflags == flags) return 0; @@ -169,11 +169,7 @@ int fd_cloexec(int fd, bool cloexec) { if (flags < 0) return -errno; - if (cloexec) - nflags = flags | FD_CLOEXEC; - else - nflags = flags & ~FD_CLOEXEC; - + nflags = UPDATE_FLAG(flags, FD_CLOEXEC, cloexec); if (nflags == flags) return 0; @@ -954,8 +950,15 @@ int fd_reopen(int fd, int flags) { xsprintf(procfs_path, "/proc/self/fd/%i", fd); new_fd = open(procfs_path, flags); - if (new_fd < 0) - return -errno; + if (new_fd < 0) { + if (errno != ENOENT) + return -errno; + + if (proc_mounted() == 0) + return -ENOSYS; /* if we have no /proc/, the concept is not implementable */ + + return -ENOENT; + } return new_fd; } diff --git a/shared/systemd/src/basic/fileio.c b/shared/systemd/src/basic/fileio.c index 543dd4eb..30903263 100644 --- a/shared/systemd/src/basic/fileio.c +++ b/shared/systemd/src/basic/fileio.c @@ -122,7 +122,7 @@ int write_string_stream_ts( struct timespec *ts) { bool needs_nl; - int r; + int r, fd; assert(f); assert(line); @@ -130,6 +130,14 @@ int write_string_stream_ts( if (ferror(f)) return -EIO; + if (ts) { + /* If we shall set the timestamp we need the fd. But fmemopen() streams generally don't have + * an fd. Let's fail early in that case. */ + fd = fileno(f); + if (fd < 0) + return -EBADF; + } + needs_nl = !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n"); if (needs_nl && (flags & WRITE_STRING_FILE_DISABLE_BUFFER)) { @@ -157,7 +165,7 @@ int write_string_stream_ts( if (ts) { struct timespec twice[2] = {*ts, *ts}; - if (futimens(fileno(f), twice) < 0) + if (futimens(fd, twice) < 0) return -errno; } @@ -197,6 +205,13 @@ static int write_string_file_atomic( goto fail; } + if (FLAGS_SET(flags, WRITE_STRING_FILE_SYNC)) { + /* Sync the rename, too */ + r = fsync_directory_of_file(fileno(f)); + if (r < 0) + return r; + } + return 0; fail: @@ -415,7 +430,7 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re break; } - if (errno != -EINTR) + if (errno != EINTR) return -errno; } @@ -895,7 +910,7 @@ int fflush_and_check(FILE *f) { #if 0 /* NM_IGNORED */ int fflush_sync_and_check(FILE *f) { - int r; + int r, fd; assert(f); @@ -903,10 +918,16 @@ int fflush_sync_and_check(FILE *f) { if (r < 0) return r; - if (fsync(fileno(f)) < 0) + /* Not all file streams have an fd associated (think: fmemopen()), let's handle this gracefully and + * assume that in that case we need no explicit syncing */ + fd = fileno(f); + if (fd < 0) + return 0; + + if (fsync(fd) < 0) return -errno; - r = fsync_directory_of_file(fileno(f)); + r = fsync_directory_of_file(fd); if (r < 0) return r; @@ -1006,7 +1027,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, funlockfile); int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) { size_t n = 0, allocated = 0, count = 0; _cleanup_free_ char *buffer = NULL; - int r, tty = -1; + int r; assert(f); @@ -1081,13 +1102,23 @@ int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) { count++; if (eol != EOL_NONE) { - /* If we are on a tty, we can't wait for more input. But we expect only - * \n as the single EOL marker, so there is no need to wait. We check - * this condition last to avoid isatty() check if not necessary. */ - - if (tty < 0) - tty = isatty(fileno(f)); - if (tty > 0) + /* If we are on a tty, we can't shouldn't wait for more input, because that + * generally means waiting for the user, interactively. In the case of a TTY + * we expect only \n as the single EOL marker, so we are in the lucky + * position that there is no need to wait. We check this condition last, to + * avoid isatty() check if not necessary. */ + + if ((flags & (READ_LINE_IS_A_TTY|READ_LINE_NOT_A_TTY)) == 0) { + int fd; + + fd = fileno(f); + if (fd < 0) /* Maybe an fmemopen() stream? Handle this gracefully, + * and don't call isatty() on an invalid fd */ + flags |= READ_LINE_NOT_A_TTY; + else + flags |= isatty(fd) ? READ_LINE_IS_A_TTY : READ_LINE_NOT_A_TTY; + } + if (FLAGS_SET(flags, READ_LINE_IS_A_TTY)) break; } @@ -1168,3 +1199,27 @@ int warn_file_is_world_accessible(const char *filename, struct stat *st, const c filename, st->st_mode & 07777); return 0; } + +#if 0 /* NM_IGNORED */ +int sync_rights(int from, int to) { + struct stat st; + + if (fstat(from, &st) < 0) + return -errno; + + return fchmod_and_chown(to, st.st_mode & 07777, st.st_uid, st.st_gid); +} + +int rename_and_apply_smack_floor_label(const char *from, const char *to) { + int r = 0; + if (rename(from, to) < 0) + return -errno; + +#ifdef SMACK_RUN_LABEL + r = mac_smack_apply(to, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL); + if (r < 0) + return r; +#endif + return r; +} +#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/fileio.h b/shared/systemd/src/basic/fileio.h index 58daabaa..e2830b79 100644 --- a/shared/systemd/src/basic/fileio.h +++ b/shared/systemd/src/basic/fileio.h @@ -88,7 +88,9 @@ int read_timestamp_file(const char *fn, usec_t *ret); int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space); typedef enum ReadLineFlags { - READ_LINE_ONLY_NUL = 1 << 0, + READ_LINE_ONLY_NUL = 1 << 0, + READ_LINE_IS_A_TTY = 1 << 1, + READ_LINE_NOT_A_TTY = 1 << 2, } ReadLineFlags; int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret); @@ -104,3 +106,7 @@ static inline int read_nul_string(FILE *f, size_t limit, char **ret) { int safe_fgetc(FILE *f, char *ret); int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line); + +int sync_rights(int from, int to); + +int rename_and_apply_smack_floor_label(const char *temp_path, const char *dest_path); diff --git a/shared/systemd/src/basic/fs-util.c b/shared/systemd/src/basic/fs-util.c index 79966571..75df913a 100644 --- a/shared/systemd/src/basic/fs-util.c +++ b/shared/systemd/src/basic/fs-util.c @@ -10,8 +10,10 @@ #include <unistd.h> #include "alloc-util.h" +#include "blockdev-util.h" #include "dirent-util.h" #include "fd-util.h" +#include "fileio.h" #include "fs-util.h" #include "locale-util.h" #include "log.h" @@ -23,6 +25,7 @@ #include "parse-util.h" #include "path-util.h" #include "process-util.h" +#include "random-util.h" #include "stat-util.h" #include "stdio-util.h" #include "string-util.h" @@ -342,34 +345,51 @@ int fchmod_opath(int fd, mode_t m) { * fchownat() does. */ xsprintf(procfs_path, "/proc/self/fd/%i", fd); - if (chmod(procfs_path, m) < 0) - return -errno; + if (chmod(procfs_path, m) < 0) { + if (errno != ENOENT) + return -errno; + + if (proc_mounted() == 0) + return -ENOSYS; /* if we have no /proc/, the concept is not implementable */ + + return -ENOENT; + } return 0; } -int fd_warn_permissions(const char *path, int fd) { - struct stat st; - - if (fstat(fd, &st) < 0) - return -errno; +int stat_warn_permissions(const char *path, const struct stat *st) { + assert(path); + assert(st); /* Don't complain if we are reading something that is not a file, for example /dev/null */ - if (!S_ISREG(st.st_mode)) + if (!S_ISREG(st->st_mode)) return 0; - if (st.st_mode & 0111) + if (st->st_mode & 0111) log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path); - if (st.st_mode & 0002) + if (st->st_mode & 0002) log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path); - if (getpid_cached() == 1 && (st.st_mode & 0044) != 0044) + if (getpid_cached() == 1 && (st->st_mode & 0044) != 0044) log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path); return 0; } +int fd_warn_permissions(const char *path, int fd) { + struct stat st; + + assert(path); + assert(fd >= 0); + + if (fstat(fd, &st) < 0) + return -errno; + + return stat_warn_permissions(path, &st); +} + int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) { char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; _cleanup_close_ int fd = -1; @@ -702,29 +722,31 @@ int unlink_or_warn(const char *filename) { int inotify_add_watch_fd(int fd, int what, uint32_t mask) { char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; - int r; + int wd; /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */ xsprintf(path, "/proc/self/fd/%i", what); - r = inotify_add_watch(fd, path, mask); - if (r < 0) + wd = inotify_add_watch(fd, path, mask); + if (wd < 0) return -errno; - return r; + return wd; } #if 0 /* NM_IGNORED */ int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask) { + int wd; - if (inotify_add_watch(fd, pathname, mask) < 0) { + wd = inotify_add_watch(fd, pathname, mask); + if (wd < 0) { if (errno == ENOSPC) return log_error_errno(errno, "Failed to add a watch for %s: inotify watch limit reached", pathname); return log_error_errno(errno, "Failed to add a watch for %s: %m", pathname); } - return 0; + return wd; } static bool unsafe_transition(const struct stat *a, const struct stat *b) { @@ -1303,11 +1325,13 @@ void unlink_tempfilep(char (*p)[]) { (void) unlink_noerrno(*p); } -int unlinkat_deallocate(int fd, const char *name, int flags) { +int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) { _cleanup_close_ int truncate_fd = -1; struct stat st; off_t l, bs; + assert((flags & ~(UNLINK_REMOVEDIR|UNLINK_ERASE)) == 0); + /* Operates like unlinkat() but also deallocates the file contents if it is a regular file and there's no other * link to it. This is useful to ensure that other processes that might have the file open for reading won't be * able to keep the data pinned on disk forever. This call is particular useful whenever we execute clean-up @@ -1324,7 +1348,7 @@ int unlinkat_deallocate(int fd, const char *name, int flags) { * Note that we attempt deallocation, but failure to succeed with that is not considered fatal, as long as the * primary job – to delete the file – is accomplished. */ - if ((flags & AT_REMOVEDIR) == 0) { + if (!FLAGS_SET(flags, UNLINK_REMOVEDIR)) { truncate_fd = openat(fd, name, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK); if (truncate_fd < 0) { @@ -1340,7 +1364,7 @@ int unlinkat_deallocate(int fd, const char *name, int flags) { } } - if (unlinkat(fd, name, flags) < 0) + if (unlinkat(fd, name, FLAGS_SET(flags, UNLINK_REMOVEDIR) ? AT_REMOVEDIR : 0) < 0) return -errno; if (truncate_fd < 0) /* Don't have a file handle, can't do more ☹️ */ @@ -1351,7 +1375,45 @@ int unlinkat_deallocate(int fd, const char *name, int flags) { return 0; } - if (!S_ISREG(st.st_mode) || st.st_blocks == 0 || st.st_nlink > 0) + if (!S_ISREG(st.st_mode)) + return 0; + + if (FLAGS_SET(flags, UNLINK_ERASE) && st.st_size > 0 && st.st_nlink == 0) { + uint64_t left = st.st_size; + char buffer[64 * 1024]; + + /* If erasing is requested, let's overwrite the file with random data once before deleting + * it. This isn't going to give you shred(1) semantics, but hopefully should be good enough + * for stuff backed by tmpfs at least. + * + * Note that we only erase like this if the link count of the file is zero. If it is higher it + * is still linked by someone else and we'll leave it to them to remove it securely + * eventually! */ + + random_bytes(buffer, sizeof(buffer)); + + while (left > 0) { + ssize_t n; + + n = write(truncate_fd, buffer, MIN(sizeof(buffer), left)); + if (n < 0) { + log_debug_errno(errno, "Failed to erase data in file '%s', ignoring.", name); + break; + } + + assert(left >= (size_t) n); + left -= n; + } + + /* Let's refresh metadata */ + if (fstat(truncate_fd, &st) < 0) { + log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring: %m", name); + return 0; + } + } + + /* Don't dallocate if there's nothing to deallocate or if the file is linked elsewhere */ + if (st.st_blocks == 0 || st.st_nlink > 0) return 0; /* If this is a regular file, it actually took up space on disk and there are no other links it's time to @@ -1490,4 +1552,89 @@ int open_parent(const char *path, int flags, mode_t mode) { return fd; } + +static int blockdev_is_encrypted(const char *sysfs_path, unsigned depth_left) { + _cleanup_free_ char *p = NULL, *uuids = NULL; + _cleanup_closedir_ DIR *d = NULL; + int r, found_encrypted = false; + + assert(sysfs_path); + + if (depth_left == 0) + return -EINVAL; + + p = path_join(sysfs_path, "dm/uuid"); + if (!p) + return -ENOMEM; + + r = read_one_line_file(p, &uuids); + if (r != -ENOENT) { + if (r < 0) + return r; + + /* The DM device's uuid attribute is prefixed with "CRYPT-" if this is a dm-crypt device. */ + if (startswith(uuids, "CRYPT-")) + return true; + } + + /* Not a dm-crypt device itself. But maybe it is on top of one? Follow the links in the "slaves/" + * subdir. */ + + p = mfree(p); + p = path_join(sysfs_path, "slaves"); + if (!p) + return -ENOMEM; + + d = opendir(p); + if (!d) { + if (errno == ENOENT) /* Doesn't have slaves */ + return false; + + return -errno; + } + + for (;;) { + _cleanup_free_ char *q = NULL; + struct dirent *de; + + errno = 0; + de = readdir_no_dot(d); + if (!de) { + if (errno != 0) + return -errno; + + break; /* No more slaves */ + } + + q = path_join(p, de->d_name); + if (!q) + return -ENOMEM; + + r = blockdev_is_encrypted(q, depth_left - 1); + if (r < 0) + return r; + if (r == 0) /* we found one that is not encrypted? then propagate that immediately */ + return false; + + found_encrypted = true; + } + + return found_encrypted; +} + +int path_is_encrypted(const char *path) { + char p[SYS_BLOCK_PATH_MAX(NULL)]; + dev_t devt; + int r; + + r = get_block_device(path, &devt); + if (r < 0) + return r; + if (r == 0) /* doesn't have a block device */ + return false; + + xsprintf_sys_block_path(p, NULL, devt); + + return blockdev_is_encrypted(p, 10 /* safety net: maximum recursion depth */); +} #endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/fs-util.h b/shared/systemd/src/basic/fs-util.h index 6b9ade2e..b184570f 100644 --- a/shared/systemd/src/basic/fs-util.h +++ b/shared/systemd/src/basic/fs-util.h @@ -40,6 +40,7 @@ int fchmod_umask(int fd, mode_t mode); int fchmod_opath(int fd, mode_t m); int fd_warn_permissions(const char *path, int fd); +int stat_warn_permissions(const char *path, const struct stat *st); #define laccess(path, mode) faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW) @@ -82,7 +83,7 @@ enum { CHASE_SAFE = 1 << 3, /* Return EPERM if we ever traverse from unprivileged to privileged files or directories */ CHASE_TRAIL_SLASH = 1 << 4, /* Any trailing slash will be preserved */ CHASE_STEP = 1 << 5, /* Just execute a single step of the normalization */ - CHASE_NOFOLLOW = 1 << 6, /* Do not follow the path's right-most compontent. With ret_fd, when the path's + CHASE_NOFOLLOW = 1 << 6, /* Do not follow the path's right-most component. With ret_fd, when the path's * right-most component refers to symlink, return O_PATH fd of the symlink. */ CHASE_WARN = 1 << 7, /* Emit an appropriate warning when an error is encountered */ }; @@ -113,7 +114,13 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free); int access_fd(int fd, int mode); void unlink_tempfilep(char (*p)[]); -int unlinkat_deallocate(int fd, const char *name, int flags); + +typedef enum UnlinkDeallocateFlags { + UNLINK_REMOVEDIR = 1 << 0, + UNLINK_ERASE = 1 << 1, +} UnlinkDeallocateFlags; + +int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags); int fsync_directory_of_file(int fd); int fsync_full(int fd); @@ -122,3 +129,5 @@ int fsync_path_at(int at_fd, const char *path); int syncfs_path(int atfd, const char *path); int open_parent(const char *path, int flags, mode_t mode); + +int path_is_encrypted(const char *path); diff --git a/shared/systemd/src/basic/hash-funcs.c b/shared/systemd/src/basic/hash-funcs.c index 1b0d1292..b1c19c95 100644 --- a/shared/systemd/src/basic/hash-funcs.c +++ b/shared/systemd/src/basic/hash-funcs.c @@ -11,12 +11,14 @@ void string_hash_func(const char *p, struct siphash *state) { siphash24_compress(p, strlen(p) + 1, state); } -#if 0 /* NM_IGNORED */ DEFINE_HASH_OPS(string_hash_ops, char, string_hash_func, string_compare_func); +DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(string_hash_ops_free, + char, string_hash_func, string_compare_func, free); DEFINE_HASH_OPS_FULL(string_hash_ops_free_free, char, string_hash_func, string_compare_func, free, char, free); +#if 0 /* NM_IGNORED */ void path_hash_func(const char *q, struct siphash *state) { size_t n; @@ -56,6 +58,8 @@ void path_hash_func(const char *q, struct siphash *state) { } DEFINE_HASH_OPS(path_hash_ops, char, path_hash_func, path_compare); +DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(path_hash_ops_free, + char, path_hash_func, path_compare, free); #endif /* NM_IGNORED */ void trivial_hash_func(const void *p, struct siphash *state) { diff --git a/shared/systemd/src/basic/hash-funcs.h b/shared/systemd/src/basic/hash-funcs.h index 7bb5d1cd..005d1b21 100644 --- a/shared/systemd/src/basic/hash-funcs.h +++ b/shared/systemd/src/basic/hash-funcs.h @@ -76,10 +76,12 @@ struct hash_ops { void string_hash_func(const char *p, struct siphash *state); #define string_compare_func strcmp extern const struct hash_ops string_hash_ops; +extern const struct hash_ops string_hash_ops_free; extern const struct hash_ops string_hash_ops_free_free; void path_hash_func(const char *p, struct siphash *state); extern const struct hash_ops path_hash_ops; +extern const struct hash_ops path_hash_ops_free; /* This will compare the passed pointers directly, and will not dereference them. This is hence not useful for strings * or suchlike. */ diff --git a/shared/systemd/src/basic/hashmap.c b/shared/systemd/src/basic/hashmap.c index 1aa00947..c0296bde 100644 --- a/shared/systemd/src/basic/hashmap.c +++ b/shared/systemd/src/basic/hashmap.c @@ -147,12 +147,7 @@ struct hashmap_debug_info { /* Tracks all existing hashmaps. Get at it from gdb. See sd_dump_hashmaps.py */ static LIST_HEAD(struct hashmap_debug_info, hashmap_debug_list); static pthread_mutex_t hashmap_debug_list_mutex = PTHREAD_MUTEX_INITIALIZER; - -#define HASHMAP_DEBUG_FIELDS struct hashmap_debug_info debug; - -#else /* !ENABLE_DEBUG_HASHMAP */ -#define HASHMAP_DEBUG_FIELDS -#endif /* ENABLE_DEBUG_HASHMAP */ +#endif enum HashmapType { HASHMAP_TYPE_PLAIN, @@ -214,7 +209,10 @@ struct HashmapBase { bool from_pool:1; /* whether was allocated from mempool */ bool dirty:1; /* whether dirtied since last iterated_cache_get() */ bool cached:1; /* whether this hashmap is being cached */ - HASHMAP_DEBUG_FIELDS /* optional hashmap_debug_info */ + +#if ENABLE_DEBUG_HASHMAP + struct hashmap_debug_info debug; +#endif }; /* Specific hash types @@ -256,7 +254,7 @@ struct hashmap_type_info { unsigned n_direct_buckets; }; -static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = { +static _used_ const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = { [HASHMAP_TYPE_PLAIN] = { .head_size = sizeof(Hashmap), .entry_size = sizeof(struct plain_hashmap_entry), @@ -709,7 +707,7 @@ static unsigned hashmap_iterate_entry(HashmapBase *h, Iterator *i) { : hashmap_iterate_in_internal_order(h, i); } -bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key) { +bool _hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key) { struct hashmap_base_entry *e; void *data; unsigned idx; @@ -735,7 +733,7 @@ bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const v } bool set_iterate(const Set *s, Iterator *i, void **value) { - return internal_hashmap_iterate(HASHMAP_BASE((Set*) s), i, value, NULL); + return _hashmap_iterate(HASHMAP_BASE((Set*) s), i, value, NULL); } #define HASHMAP_FOREACH_IDX(idx, h, i) \ @@ -743,7 +741,7 @@ bool set_iterate(const Set *s, Iterator *i, void **value) { (idx != IDX_NIL); \ (idx) = hashmap_iterate_entry((h), &(i))) -IteratedCache *internal_hashmap_iterated_cache_new(HashmapBase *h) { +IteratedCache *_hashmap_iterated_cache_new(HashmapBase *h) { IteratedCache *cache; assert(h); @@ -811,15 +809,15 @@ static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enu return h; } -Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { +Hashmap *_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { return (Hashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); } -OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { +OrderedHashmap *_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); } -Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { +Set *_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { return (Set*) hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); } @@ -837,18 +835,18 @@ static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops return -ENOMEM; *h = q; - return 0; + return 1; } -int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { +int _hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); } -int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { +int _ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); } -int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { +int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); } @@ -870,16 +868,16 @@ static void hashmap_free_no_clear(HashmapBase *h) { free(h); } -HashmapBase *internal_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) { +HashmapBase *_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) { if (h) { - internal_hashmap_clear(h, default_free_key, default_free_value); + _hashmap_clear(h, default_free_key, default_free_value); hashmap_free_no_clear(h); } return NULL; } -void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) { +void _hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) { free_func_t free_key, free_value; if (!h) return; @@ -893,11 +891,11 @@ void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_f * hash table, and only then call the destructor functions. If these destructors then try to unregister * themselves from our hash table a second time, the entry is already gone. */ - while (internal_hashmap_size(h) > 0) { + while (_hashmap_size(h) > 0) { void *k = NULL; void *v; - v = internal_hashmap_first_key_and_value(h, true, &k); + v = _hashmap_first_key_and_value(h, true, &k); if (free_key) free_key(k); @@ -1303,7 +1301,7 @@ int hashmap_update(Hashmap *h, const void *key, void *value) { return 0; } -void *internal_hashmap_get(HashmapBase *h, const void *key) { +void *_hashmap_get(HashmapBase *h, const void *key) { struct hashmap_base_entry *e; unsigned hash, idx; @@ -1338,7 +1336,7 @@ void *hashmap_get2(Hashmap *h, const void *key, void **key2) { return e->value; } -bool internal_hashmap_contains(HashmapBase *h, const void *key) { +bool _hashmap_contains(HashmapBase *h, const void *key) { unsigned hash; if (!h) @@ -1348,7 +1346,7 @@ bool internal_hashmap_contains(HashmapBase *h, const void *key) { return bucket_scan(h, hash, key) != IDX_NIL; } -void *internal_hashmap_remove(HashmapBase *h, const void *key) { +void *_hashmap_remove(HashmapBase *h, const void *key) { struct hashmap_base_entry *e; unsigned hash, idx; void *data; @@ -1486,7 +1484,7 @@ int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_ return 0; } -void *internal_hashmap_remove_value(HashmapBase *h, const void *key, void *value) { +void *_hashmap_remove_value(HashmapBase *h, const void *key, void *value) { struct hashmap_base_entry *e; unsigned hash, idx; @@ -1516,7 +1514,7 @@ static unsigned find_first_entry(HashmapBase *h) { return hashmap_iterate_entry(h, &i); } -void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key) { +void *_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key) { struct hashmap_base_entry *e; void *key, *data; unsigned idx; @@ -1541,21 +1539,21 @@ void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **r return data; } -unsigned internal_hashmap_size(HashmapBase *h) { +unsigned _hashmap_size(HashmapBase *h) { if (!h) return 0; return n_entries(h); } -unsigned internal_hashmap_buckets(HashmapBase *h) { +unsigned _hashmap_buckets(HashmapBase *h) { if (!h) return 0; return n_buckets(h); } -int internal_hashmap_merge(Hashmap *h, Hashmap *other) { +int _hashmap_merge(Hashmap *h, Hashmap *other) { Iterator i; unsigned idx; @@ -1591,7 +1589,7 @@ int set_merge(Set *s, Set *other) { return 0; } -int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) { +int _hashmap_reserve(HashmapBase *h, unsigned entries_add) { int r; assert(h); @@ -1609,7 +1607,7 @@ int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) { * Returns: 0 on success. * -ENOMEM on alloc failure, in which case no move has been done. */ -int internal_hashmap_move(HashmapBase *h, HashmapBase *other) { +int _hashmap_move(HashmapBase *h, HashmapBase *other) { struct swap_entries swap; struct hashmap_base_entry *e, *n; Iterator i; @@ -1654,7 +1652,7 @@ int internal_hashmap_move(HashmapBase *h, HashmapBase *other) { return 0; } -int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) { +int _hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) { struct swap_entries swap; unsigned h_hash, other_hash, idx; struct hashmap_base_entry *e, *n; @@ -1691,7 +1689,7 @@ int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *ke return 0; } -HashmapBase *internal_hashmap_copy(HashmapBase *h) { +HashmapBase *_hashmap_copy(HashmapBase *h) { HashmapBase *copy; int r; @@ -1714,14 +1712,14 @@ HashmapBase *internal_hashmap_copy(HashmapBase *h) { } if (r < 0) { - internal_hashmap_free(copy, false, false); + _hashmap_free(copy, false, false); return NULL; } return copy; } -char **internal_hashmap_get_strv(HashmapBase *h) { +char **_hashmap_get_strv(HashmapBase *h) { char **sv; Iterator i; unsigned idx, n; @@ -1778,42 +1776,55 @@ int hashmap_put_strdup(Hashmap **h, const char *k, const char *v) { return r; _cleanup_free_ char *kdup = NULL, *vdup = NULL; + kdup = strdup(k); - vdup = strdup(v); - if (!kdup || !vdup) + if (!kdup) return -ENOMEM; + if (v) { + vdup = strdup(v); + if (!vdup) + return -ENOMEM; + } + r = hashmap_put(*h, kdup, vdup); if (r < 0) { - if (r == -EEXIST && streq(v, hashmap_get(*h, kdup))) + if (r == -EEXIST && streq_ptr(v, hashmap_get(*h, kdup))) return 0; return r; } - assert(r > 0); /* 0 would mean vdup is already in the hashmap, which cannot be */ - kdup = vdup = NULL; + /* 0 with non-null vdup would mean vdup is already in the hashmap, which cannot be */ + assert(vdup == NULL || r > 0); + if (r > 0) + kdup = vdup = NULL; - return 0; + return r; } #endif /* NM_IGNORED */ -int set_put_strdup(Set *s, const char *p) { +int set_put_strdup(Set **s, const char *p) { char *c; + int r; assert(s); assert(p); - if (set_contains(s, (char*) p)) + r = set_ensure_allocated(s, &string_hash_ops_free); + if (r < 0) + return r; + + if (set_contains(*s, (char*) p)) return 0; c = strdup(p); if (!c) return -ENOMEM; - return set_consume(s, c); + return set_consume(*s, c); } -int set_put_strdupv(Set *s, char **l) { +int set_put_strdupv(Set **s, char **l) { int n = 0, r; char **i; diff --git a/shared/systemd/src/basic/hashmap.h b/shared/systemd/src/basic/hashmap.h index 65adc925..230d3222 100644 --- a/shared/systemd/src/basic/hashmap.h +++ b/shared/systemd/src/basic/hashmap.h @@ -14,7 +14,7 @@ * will be treated as empty hashmap for all read operations. That way it is not * necessary to instantiate an object for each Hashmap use. * - * If ENABLE_DEBUG_HASHMAP is defined (by configuring with --enable-debug=hashmap), + * If ENABLE_DEBUG_HASHMAP is defined (by configuring with -Ddebug-extra=hashmap), * the implementation will: * - store extra data for debugging and statistics (see tools/gdb-sd_dump_hashmaps.py) * - perform extra checks for invalid use of iterators @@ -24,10 +24,9 @@ typedef void* (*hashmap_destroy_t)(void *p); -/* The base type for all hashmap and set types. Many functions in the - * implementation take (HashmapBase*) parameters and are run-time polymorphic, - * though the API is not meant to be polymorphic (do not call functions - * internal_*() directly). */ +/* The base type for all hashmap and set types. Many functions in the implementation take (HashmapBase*) + * parameters and are run-time polymorphic, though the API is not meant to be polymorphic (do not call + * underscore-prefixed functions directly). */ typedef struct HashmapBase HashmapBase; /* Specific hashmap/set types */ @@ -84,62 +83,70 @@ typedef struct { # define HASHMAP_DEBUG_PASS_ARGS #endif -Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define hashmap_new(ops) internal_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) -#define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) +Hashmap *_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +OrderedHashmap *_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define hashmap_new(ops) _hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) +#define ordered_hashmap_new(ops) _ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) + +#define hashmap_free_and_replace(a, b) \ + ({ \ + hashmap_free(a); \ + (a) = (b); \ + (b) = NULL; \ + 0; \ + }) -HashmapBase *internal_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value); +HashmapBase *_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value); static inline Hashmap *hashmap_free(Hashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, NULL); + return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, NULL); } static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, NULL); + return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, NULL); } static inline Hashmap *hashmap_free_free(Hashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, free); + return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, free); } static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, free); + return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, free); } static inline Hashmap *hashmap_free_free_key(Hashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, NULL); + return (void*) _hashmap_free(HASHMAP_BASE(h), free, NULL); } static inline OrderedHashmap *ordered_hashmap_free_free_key(OrderedHashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, NULL); + return (void*) _hashmap_free(HASHMAP_BASE(h), free, NULL); } static inline Hashmap *hashmap_free_free_free(Hashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, free); + return (void*) _hashmap_free(HASHMAP_BASE(h), free, free); } static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, free); + return (void*) _hashmap_free(HASHMAP_BASE(h), free, free); } IteratedCache *iterated_cache_free(IteratedCache *cache); int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void ***res_values, unsigned *res_n_entries); -HashmapBase *internal_hashmap_copy(HashmapBase *h); +HashmapBase *_hashmap_copy(HashmapBase *h); static inline Hashmap *hashmap_copy(Hashmap *h) { - return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h)); + return (Hashmap*) _hashmap_copy(HASHMAP_BASE(h)); } static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) { - return (OrderedHashmap*) internal_hashmap_copy(HASHMAP_BASE(h)); + return (OrderedHashmap*) _hashmap_copy(HASHMAP_BASE(h)); } -int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define hashmap_ensure_allocated(h, ops) internal_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) -#define ordered_hashmap_ensure_allocated(h, ops) internal_ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) +int _hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +int _ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define hashmap_ensure_allocated(h, ops) _hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) +#define ordered_hashmap_ensure_allocated(h, ops) _ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) -IteratedCache *internal_hashmap_iterated_cache_new(HashmapBase *h); +IteratedCache *_hashmap_iterated_cache_new(HashmapBase *h); static inline IteratedCache *hashmap_iterated_cache_new(Hashmap *h) { - return (IteratedCache*) internal_hashmap_iterated_cache_new(HASHMAP_BASE(h)); + return (IteratedCache*) _hashmap_iterated_cache_new(HASHMAP_BASE(h)); } static inline IteratedCache *ordered_hashmap_iterated_cache_new(OrderedHashmap *h) { - return (IteratedCache*) internal_hashmap_iterated_cache_new(HASHMAP_BASE(h)); + return (IteratedCache*) _hashmap_iterated_cache_new(HASHMAP_BASE(h)); } int hashmap_put(Hashmap *h, const void *key, void *value); @@ -159,12 +166,12 @@ static inline int ordered_hashmap_replace(OrderedHashmap *h, const void *key, vo return hashmap_replace(PLAIN_HASHMAP(h), key, value); } -void *internal_hashmap_get(HashmapBase *h, const void *key); +void *_hashmap_get(HashmapBase *h, const void *key); static inline void *hashmap_get(Hashmap *h, const void *key) { - return internal_hashmap_get(HASHMAP_BASE(h), key); + return _hashmap_get(HASHMAP_BASE(h), key); } static inline void *ordered_hashmap_get(OrderedHashmap *h, const void *key) { - return internal_hashmap_get(HASHMAP_BASE(h), key); + return _hashmap_get(HASHMAP_BASE(h), key); } void *hashmap_get2(Hashmap *h, const void *key, void **rkey); @@ -172,20 +179,20 @@ static inline void *ordered_hashmap_get2(OrderedHashmap *h, const void *key, voi return hashmap_get2(PLAIN_HASHMAP(h), key, rkey); } -bool internal_hashmap_contains(HashmapBase *h, const void *key); +bool _hashmap_contains(HashmapBase *h, const void *key); static inline bool hashmap_contains(Hashmap *h, const void *key) { - return internal_hashmap_contains(HASHMAP_BASE(h), key); + return _hashmap_contains(HASHMAP_BASE(h), key); } static inline bool ordered_hashmap_contains(OrderedHashmap *h, const void *key) { - return internal_hashmap_contains(HASHMAP_BASE(h), key); + return _hashmap_contains(HASHMAP_BASE(h), key); } -void *internal_hashmap_remove(HashmapBase *h, const void *key); +void *_hashmap_remove(HashmapBase *h, const void *key); static inline void *hashmap_remove(Hashmap *h, const void *key) { - return internal_hashmap_remove(HASHMAP_BASE(h), key); + return _hashmap_remove(HASHMAP_BASE(h), key); } static inline void *ordered_hashmap_remove(OrderedHashmap *h, const void *key) { - return internal_hashmap_remove(HASHMAP_BASE(h), key); + return _hashmap_remove(HASHMAP_BASE(h), key); } void *hashmap_remove2(Hashmap *h, const void *key, void **rkey); @@ -193,9 +200,9 @@ static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key, return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey); } -void *internal_hashmap_remove_value(HashmapBase *h, const void *key, void *value); +void *_hashmap_remove_value(HashmapBase *h, const void *key, void *value); static inline void *hashmap_remove_value(Hashmap *h, const void *key, void *value) { - return internal_hashmap_remove_value(HASHMAP_BASE(h), key, value); + return _hashmap_remove_value(HASHMAP_BASE(h), key, value); } static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) { @@ -214,41 +221,41 @@ static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const vo /* Since merging data from a OrderedHashmap into a Hashmap or vice-versa * should just work, allow this by having looser type-checking here. */ -int internal_hashmap_merge(Hashmap *h, Hashmap *other); -#define hashmap_merge(h, other) internal_hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other)) +int _hashmap_merge(Hashmap *h, Hashmap *other); +#define hashmap_merge(h, other) _hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other)) #define ordered_hashmap_merge(h, other) hashmap_merge(h, other) -int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add); +int _hashmap_reserve(HashmapBase *h, unsigned entries_add); static inline int hashmap_reserve(Hashmap *h, unsigned entries_add) { - return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); + return _hashmap_reserve(HASHMAP_BASE(h), entries_add); } static inline int ordered_hashmap_reserve(OrderedHashmap *h, unsigned entries_add) { - return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); + return _hashmap_reserve(HASHMAP_BASE(h), entries_add); } -int internal_hashmap_move(HashmapBase *h, HashmapBase *other); +int _hashmap_move(HashmapBase *h, HashmapBase *other); /* Unlike hashmap_merge, hashmap_move does not allow mixing the types. */ static inline int hashmap_move(Hashmap *h, Hashmap *other) { - return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); + return _hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); } static inline int ordered_hashmap_move(OrderedHashmap *h, OrderedHashmap *other) { - return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); + return _hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); } -int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key); +int _hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key); static inline int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) { - return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); + return _hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); } static inline int ordered_hashmap_move_one(OrderedHashmap *h, OrderedHashmap *other, const void *key) { - return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); + return _hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); } -unsigned internal_hashmap_size(HashmapBase *h) _pure_; +unsigned _hashmap_size(HashmapBase *h) _pure_; static inline unsigned hashmap_size(Hashmap *h) { - return internal_hashmap_size(HASHMAP_BASE(h)); + return _hashmap_size(HASHMAP_BASE(h)); } static inline unsigned ordered_hashmap_size(OrderedHashmap *h) { - return internal_hashmap_size(HASHMAP_BASE(h)); + return _hashmap_size(HASHMAP_BASE(h)); } static inline bool hashmap_isempty(Hashmap *h) { @@ -258,49 +265,49 @@ static inline bool ordered_hashmap_isempty(OrderedHashmap *h) { return ordered_hashmap_size(h) == 0; } -unsigned internal_hashmap_buckets(HashmapBase *h) _pure_; +unsigned _hashmap_buckets(HashmapBase *h) _pure_; static inline unsigned hashmap_buckets(Hashmap *h) { - return internal_hashmap_buckets(HASHMAP_BASE(h)); + return _hashmap_buckets(HASHMAP_BASE(h)); } static inline unsigned ordered_hashmap_buckets(OrderedHashmap *h) { - return internal_hashmap_buckets(HASHMAP_BASE(h)); + return _hashmap_buckets(HASHMAP_BASE(h)); } -bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key); +bool _hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key); static inline bool hashmap_iterate(Hashmap *h, Iterator *i, void **value, const void **key) { - return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key); + return _hashmap_iterate(HASHMAP_BASE(h), i, value, key); } static inline bool ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, void **value, const void **key) { - return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key); + return _hashmap_iterate(HASHMAP_BASE(h), i, value, key); } -void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value); +void _hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value); static inline void hashmap_clear(Hashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), NULL, NULL); + _hashmap_clear(HASHMAP_BASE(h), NULL, NULL); } static inline void ordered_hashmap_clear(OrderedHashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), NULL, NULL); + _hashmap_clear(HASHMAP_BASE(h), NULL, NULL); } static inline void hashmap_clear_free(Hashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), NULL, free); + _hashmap_clear(HASHMAP_BASE(h), NULL, free); } static inline void ordered_hashmap_clear_free(OrderedHashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), NULL, free); + _hashmap_clear(HASHMAP_BASE(h), NULL, free); } static inline void hashmap_clear_free_key(Hashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), free, NULL); + _hashmap_clear(HASHMAP_BASE(h), free, NULL); } static inline void ordered_hashmap_clear_free_key(OrderedHashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), free, NULL); + _hashmap_clear(HASHMAP_BASE(h), free, NULL); } static inline void hashmap_clear_free_free(Hashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), free, free); + _hashmap_clear(HASHMAP_BASE(h), free, free); } static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), free, free); + _hashmap_clear(HASHMAP_BASE(h), free, free); } /* @@ -314,50 +321,50 @@ static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) { * the first entry is O(1). */ -void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key); +void *_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key); static inline void *hashmap_steal_first_key_and_value(Hashmap *h, void **ret) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret); } static inline void *ordered_hashmap_steal_first_key_and_value(OrderedHashmap *h, void **ret) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret); } static inline void *hashmap_first_key_and_value(Hashmap *h, void **ret) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret); } static inline void *ordered_hashmap_first_key_and_value(OrderedHashmap *h, void **ret) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret); } static inline void *hashmap_steal_first(Hashmap *h) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL); } static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL); } static inline void *hashmap_first(Hashmap *h) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL); } static inline void *ordered_hashmap_first(OrderedHashmap *h) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL); } -static inline void *internal_hashmap_first_key(HashmapBase *h, bool remove) { +static inline void *_hashmap_first_key(HashmapBase *h, bool remove) { void *key = NULL; - (void) internal_hashmap_first_key_and_value(HASHMAP_BASE(h), remove, &key); + (void) _hashmap_first_key_and_value(HASHMAP_BASE(h), remove, &key); return key; } static inline void *hashmap_steal_first_key(Hashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h), true); + return _hashmap_first_key(HASHMAP_BASE(h), true); } static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h), true); + return _hashmap_first_key(HASHMAP_BASE(h), true); } static inline void *hashmap_first_key(Hashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h), false); + return _hashmap_first_key(HASHMAP_BASE(h), false); } static inline void *ordered_hashmap_first_key(OrderedHashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h), false); + return _hashmap_first_key(HASHMAP_BASE(h), false); } #define hashmap_clear_with_destructor(_s, _f) \ @@ -386,12 +393,12 @@ static inline void *ordered_hashmap_first_key(OrderedHashmap *h) { /* no hashmap_next */ void *ordered_hashmap_next(OrderedHashmap *h, const void *key); -char **internal_hashmap_get_strv(HashmapBase *h); +char **_hashmap_get_strv(HashmapBase *h); static inline char **hashmap_get_strv(Hashmap *h) { - return internal_hashmap_get_strv(HASHMAP_BASE(h)); + return _hashmap_get_strv(HASHMAP_BASE(h)); } static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) { - return internal_hashmap_get_strv(HASHMAP_BASE(h)); + return _hashmap_get_strv(HASHMAP_BASE(h)); } /* diff --git a/shared/systemd/src/basic/hexdecoct.c b/shared/systemd/src/basic/hexdecoct.c index 36174ec3..1fb52305 100644 --- a/shared/systemd/src/basic/hexdecoct.c +++ b/shared/systemd/src/basic/hexdecoct.c @@ -604,13 +604,13 @@ ssize_t base64mem(const void *p, size_t l, char **out) { static int base64_append_width( char **prefix, int plen, - const char *sep, int indent, + char sep, int indent, const void *p, size_t l, int width) { _cleanup_free_ char *x = NULL; char *t, *s; - ssize_t len, slen, avail, line, lines; + ssize_t len, avail, line, lines; len = base64mem(p, l, &x); if (len <= 0) @@ -618,21 +618,20 @@ static int base64_append_width( lines = DIV_ROUND_UP(len, width); - slen = strlen_ptr(sep); - if (plen >= SSIZE_MAX - 1 - slen || - lines > (SSIZE_MAX - plen - 1 - slen) / (indent + width + 1)) + if ((size_t) plen >= SSIZE_MAX - 1 - 1 || + lines > (SSIZE_MAX - plen - 1 - 1) / (indent + width + 1)) return -ENOMEM; - t = realloc(*prefix, (ssize_t) plen + 1 + slen + (indent + width + 1) * lines); + t = realloc(*prefix, (ssize_t) plen + 1 + 1 + (indent + width + 1) * lines); if (!t) return -ENOMEM; - memcpy_safe(t + plen, sep, slen); + t[plen] = sep; - for (line = 0, s = t + plen + slen, avail = len; line < lines; line++) { + for (line = 0, s = t + plen + 1, avail = len; line < lines; line++) { int act = MIN(width, avail); - if (line > 0 || sep) { + if (line > 0 || sep == '\n') { memset(s, ' ', indent); s += indent; } @@ -655,10 +654,10 @@ int base64_append( if (plen > width / 2 || plen + indent > width) /* leave indent on the left, keep last column free */ - return base64_append_width(prefix, plen, "\n", indent, p, l, width - indent - 1); + return base64_append_width(prefix, plen, '\n', indent, p, l, width - indent - 1); else /* leave plen on the left, keep last column free */ - return base64_append_width(prefix, plen, " ", plen, p, l, width - plen - 1); + return base64_append_width(prefix, plen, ' ', plen + 1, p, l, width - plen - 1); } #endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/hostname-util.c b/shared/systemd/src/basic/hostname-util.c index 00a92cb7..82fc56db 100644 --- a/shared/systemd/src/basic/hostname-util.c +++ b/shared/systemd/src/basic/hostname-util.c @@ -14,6 +14,7 @@ #include "hostname-util.h" #include "macro.h" #include "string-util.h" +#include "strv.h" #if 0 /* NM_IGNORED */ bool hostname_is_set(void) { @@ -24,7 +25,7 @@ bool hostname_is_set(void) { if (isempty(u.nodename)) return false; - /* This is the built-in kernel default host name */ + /* This is the built-in kernel default hostname */ if (streq(u.nodename, "(none)")) return false; @@ -33,6 +34,7 @@ bool hostname_is_set(void) { char* gethostname_malloc(void) { struct utsname u; + const char *s; /* This call tries to return something useful, either the actual hostname * or it makes something up. The only reason it might fail is OOM. @@ -40,10 +42,28 @@ char* gethostname_malloc(void) { assert_se(uname(&u) >= 0); - if (isempty(u.nodename) || streq(u.nodename, "(none)")) - return strdup(FALLBACK_HOSTNAME); + s = u.nodename; + if (isempty(s) || streq(s, "(none)")) + s = FALLBACK_HOSTNAME; - return strdup(u.nodename); + return strdup(s); +} + +char* gethostname_short_malloc(void) { + struct utsname u; + const char *s; + + /* Like above, but kills the FQDN part if present. */ + + assert_se(uname(&u) >= 0); + + s = u.nodename; + if (isempty(s) || streq(s, "(none)") || s[0] == '.') { + s = FALLBACK_HOSTNAME; + assert(s[0] != '.'); + } + + return strndup(s, strcspn(s, ".")); } #endif /* NM_IGNORED */ @@ -81,7 +101,7 @@ bool valid_ldh_char(char c) { } /** - * Check if s looks like a valid host name or FQDN. This does not do + * Check if s looks like a valid hostname or FQDN. This does not do * full DNS validation, but only checks if the name is composed of * allowed characters and the length is not above the maximum allowed * by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if @@ -184,14 +204,16 @@ bool is_localhost(const char *hostname) { /* This tries to identify local host and domain names * described in RFC6761 plus the redhatism of localdomain */ - return strcaseeq(hostname, "localhost") || - strcaseeq(hostname, "localhost.") || - strcaseeq(hostname, "localhost.localdomain") || - strcaseeq(hostname, "localhost.localdomain.") || - endswith_no_case(hostname, ".localhost") || - endswith_no_case(hostname, ".localhost.") || - endswith_no_case(hostname, ".localhost.localdomain") || - endswith_no_case(hostname, ".localhost.localdomain."); + return STRCASE_IN_SET( + hostname, + "localhost", + "localhost.", + "localhost.localdomain", + "localhost.localdomain.") || + endswith_no_case(hostname, ".localhost") || + endswith_no_case(hostname, ".localhost.") || + endswith_no_case(hostname, ".localhost.localdomain") || + endswith_no_case(hostname, ".localhost.localdomain."); } #if 0 /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/hostname-util.h b/shared/systemd/src/basic/hostname-util.h index 7ba386a0..cafd6f02 100644 --- a/shared/systemd/src/basic/hostname-util.h +++ b/shared/systemd/src/basic/hostname-util.h @@ -9,6 +9,7 @@ bool hostname_is_set(void); char* gethostname_malloc(void); +char* gethostname_short_malloc(void); int gethostname_strict(char **ret); bool valid_ldh_char(char c) _const_; diff --git a/shared/systemd/src/basic/in-addr-util.c b/shared/systemd/src/basic/in-addr-util.c index 0e770621..cddc269f 100644 --- a/shared/systemd/src/basic/in-addr-util.c +++ b/shared/systemd/src/basic/in-addr-util.c @@ -180,47 +180,89 @@ int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) assert(u); /* Increases the network part of an address by one. Returns - * positive it that succeeds, or 0 if this overflows. */ + * positive if that succeeds, or -ERANGE if this overflows. */ + + return in_addr_prefix_nth(family, u, prefixlen, 1); +} + +/* + * Calculates the nth prefix of size prefixlen starting from the address denoted by u. + * + * On success 1 will be returned and the calculated prefix will be available in + * u. In the case nth == 0 the input will be left unchanged and 1 will be returned. + * In case the calculation cannot be performed (invalid prefix length, + * overflows would occur) -ERANGE is returned. If the address family given isn't + * supported -EAFNOSUPPORT will be returned. + * + * + * Examples: + * - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 2), returns 1, writes 192.168.2.0 to u + * - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 0), returns 1, no data written + * - in_addr_prefix_nth(AF_INET, 255.255.255.0, 24, 1), returns -ERANGE, no data written + * - in_addr_prefix_nth(AF_INET, 255.255.255.0, 0, 1), returns -ERANGE, no data written + * - in_addr_prefix_nth(AF_INET6, 2001:db8, 64, 0xff00) returns 1, writes 2001:0db8:0000:ff00:: to u + */ +int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, uint64_t nth) { + assert(u); if (prefixlen <= 0) - return 0; + return -ERANGE; - if (family == AF_INET) { - uint32_t c, n; + if (nth == 0) + return 1; + if (family == AF_INET) { + uint32_t c, n, t; if (prefixlen > 32) prefixlen = 32; c = be32toh(u->in.s_addr); - n = c + (1UL << (32 - prefixlen)); - if (n < c) - return 0; - n &= 0xFFFFFFFFUL << (32 - prefixlen); + t = nth << (32 - prefixlen); + + /* Check for wrap */ + if (c > UINT32_MAX - t) + return -ERANGE; + + n = c + t; + + n &= UINT32_C(0xFFFFFFFF) << (32 - prefixlen); u->in.s_addr = htobe32(n); return 1; } if (family == AF_INET6) { - struct in6_addr add = {}, result; + struct in6_addr result = {}; uint8_t overflow = 0; - unsigned i; + uint64_t delta; /* this assumes that we only ever have to up to 1<<64 subnets */ + unsigned start_byte = (prefixlen - 1) / 8; if (prefixlen > 128) prefixlen = 128; /* First calculate what we have to add */ - add.s6_addr[(prefixlen-1) / 8] = 1 << (7 - (prefixlen-1) % 8); + delta = nth << ((128 - prefixlen) % 8); - for (i = 16; i > 0; i--) { + for (unsigned i = 16; i > 0; i--) { unsigned j = i - 1; + unsigned d = 0; - result.s6_addr[j] = u->in6.s6_addr[j] + add.s6_addr[j] + overflow; - overflow = (result.s6_addr[j] < u->in6.s6_addr[j]); + if (j <= start_byte) { + int16_t t; + + d = delta & 0xFF; + delta >>= 8; + + t = u->in6.s6_addr[j] + d + overflow; + overflow = t > UINT8_MAX ? t - UINT8_MAX : 0; + + result.s6_addr[j] = (uint8_t)t; + } else + result.s6_addr[j] = u->in6.s6_addr[j]; } - if (overflow) - return 0; + if (overflow || delta != 0) + return -ERANGE; u->in6 = result; return 1; diff --git a/shared/systemd/src/basic/in-addr-util.h b/shared/systemd/src/basic/in-addr-util.h index ae2dad0b..90d79a5e 100644 --- a/shared/systemd/src/basic/in-addr-util.h +++ b/shared/systemd/src/basic/in-addr-util.h @@ -36,6 +36,7 @@ bool in4_addr_equal(const struct in_addr *a, const struct in_addr *b); int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b); int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen); int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen); +int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, uint64_t nth); int in_addr_random_prefix(int family, union in_addr_union *u, unsigned prefixlen_fixed_part, unsigned prefixlen); int in_addr_to_string(int family, const union in_addr_union *u, char **ret); int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret); diff --git a/shared/systemd/src/basic/io-util.c b/shared/systemd/src/basic/io-util.c index 4f57f044..f1df3fac 100644 --- a/shared/systemd/src/basic/io-util.c +++ b/shared/systemd/src/basic/io-util.c @@ -14,10 +14,6 @@ #if 0 /* NM_IGNORED */ int flush_fd(int fd) { - struct pollfd pollfd = { - .fd = fd, - .events = POLLIN, - }; int count = 0; /* Read from the specified file descriptor, until POLLIN is not set anymore, throwing away everything @@ -30,19 +26,18 @@ int flush_fd(int fd) { ssize_t l; int r; - r = poll(&pollfd, 1, 0); + r = fd_wait_for_event(fd, POLLIN, 0); if (r < 0) { - if (errno == EINTR) + if (r == -EINTR) continue; - return -errno; - - } else if (r == 0) + return r; + } + if (r == 0) return count; l = read(fd, buf, sizeof(buf)); if (l < 0) { - if (errno == EINTR) continue; @@ -160,21 +155,15 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { } int pipe_eof(int fd) { - struct pollfd pollfd = { - .fd = fd, - .events = POLLIN|POLLHUP, - }; - int r; - r = poll(&pollfd, 1, 0); + r = fd_wait_for_event(fd, POLLIN, 0); if (r < 0) - return -errno; - + return r; if (r == 0) return 0; - return pollfd.revents & POLLHUP; + return !!(r & POLLHUP); } #endif /* NM_IGNORED */ @@ -194,6 +183,9 @@ int fd_wait_for_event(int fd, int event, usec_t t) { if (r == 0) return 0; + if (pollfd.revents & POLLNVAL) + return -EBADF; + return pollfd.revents; } diff --git a/shared/systemd/src/basic/macro.h b/shared/systemd/src/basic/macro.h index 6cd9c516..0c2eb894 100644 --- a/shared/systemd/src/basic/macro.h +++ b/shared/systemd/src/basic/macro.h @@ -84,6 +84,14 @@ #define _variable_no_sanitize_address_ #endif +/* Apparently there's no has_feature() call defined to check for ubsan, hence let's define this + * unconditionally on llvm */ +#if defined(__clang__) +#define _function_no_sanitize_float_cast_overflow_ __attribute__((no_sanitize("float-cast-overflow"))) +#else +#define _function_no_sanitize_float_cast_overflow_ +#endif + #if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) || defined (__clang__) /* Temporarily disable some warnings */ #define DISABLE_WARNING_FORMAT_NONLITERAL \ @@ -115,6 +123,14 @@ _Pragma("GCC diagnostic push") #endif +#define DISABLE_WARNING_FLOAT_EQUAL \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wfloat-equal\"") + +#define DISABLE_WARNING_TYPE_LIMITS \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wtype-limits\"") + #define REENABLE_WARNING \ _Pragma("GCC diagnostic pop") #else @@ -441,6 +457,8 @@ static inline int __coverity_check_and_return__(int condition) { #define char_array_0(x) x[sizeof(x)-1] = 0; +#define sizeof_field(struct_type, member) sizeof(((struct_type *) 0)->member) + /* Returns the number of chars needed to format variables of the * specified type as a decimal string. Adds in extra space for a * negative '-' prefix (hence works correctly on signed @@ -460,8 +478,10 @@ static inline int __coverity_check_and_return__(int condition) { ans; \ }) +#define UPDATE_FLAG(orig, flag, b) \ + ((b) ? ((orig) | (flag)) : ((orig) & ~(flag))) #define SET_FLAG(v, flag, b) \ - (v) = (b) ? ((v) | (flag)) : ((v) & ~(flag)) + (v) = UPDATE_FLAG(v, flag, b) #define FLAGS_SET(v, flags) \ ((~(v) & (flags)) == 0) @@ -592,4 +612,17 @@ static inline int __coverity_check_and_return__(int condition) { DEFINE_PUBLIC_TRIVIAL_REF_FUNC(type, name); \ DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC(type, name, free_func); +/* A macro to force copying of a variable from memory. This is useful whenever we want to read something from + * memory and want to make sure the compiler won't optimize away the destination variable for us. It's not + * supposed to be a full CPU memory barrier, i.e. CPU is still allowed to reorder the reads, but it is not + * allowed to remove our local copies of the variables. We want this to work for unaligned memory, hence + * memcpy() is great for our purposes. */ +#define READ_NOW(x) \ + ({ \ + typeof(x) _copy; \ + memcpy(&_copy, &(x), sizeof(_copy)); \ + asm volatile ("" : : : "memory"); \ + _copy; \ + }) + #include "log.h" diff --git a/shared/systemd/src/basic/memory-util.h b/shared/systemd/src/basic/memory-util.h index b7e2e67e..a6a2ccdb 100644 --- a/shared/systemd/src/basic/memory-util.h +++ b/shared/systemd/src/basic/memory-util.h @@ -12,7 +12,7 @@ size_t page_size(void) _pure_; #define PAGE_ALIGN(l) ALIGN_TO((l), page_size()) -#define PAGE_ALIGN_DOWN(l) (l & ~(page_size() - 1)) +#define PAGE_ALIGN_DOWN(l) ((l) & ~(page_size() - 1)) /* Normal memcpy requires src to be nonnull. We do nothing if n is 0. */ static inline void memcpy_safe(void *dst, const void *src, size_t n) { diff --git a/shared/systemd/src/basic/missing_random.h b/shared/systemd/src/basic/missing_random.h index 2e76031b..17af87a3 100644 --- a/shared/systemd/src/basic/missing_random.h +++ b/shared/systemd/src/basic/missing_random.h @@ -14,3 +14,7 @@ #ifndef GRND_RANDOM #define GRND_RANDOM 0x0002 #endif + +#ifndef GRND_INSECURE +#define GRND_INSECURE 0x0004 +#endif diff --git a/shared/systemd/src/basic/missing_socket.h b/shared/systemd/src/basic/missing_socket.h index 29828dba..647e56f1 100644 --- a/shared/systemd/src/basic/missing_socket.h +++ b/shared/systemd/src/basic/missing_socket.h @@ -64,3 +64,8 @@ struct sockaddr_vm { #ifndef IP_TRANSPARENT #define IP_TRANSPARENT 19 #endif + +/* linux/sockios.h */ +#ifndef SIOCGSKNS +#define SIOCGSKNS 0x894C +#endif diff --git a/shared/systemd/src/basic/parse-util.c b/shared/systemd/src/basic/parse-util.c index 475a06cc..0b343b2a 100644 --- a/shared/systemd/src/basic/parse-util.c +++ b/shared/systemd/src/basic/parse-util.c @@ -20,14 +20,28 @@ #include "process-util.h" #include "stat-util.h" #include "string-util.h" +#include "strv.h" int parse_boolean(const char *v) { if (!v) return -EINVAL; - if (streq(v, "1") || strcaseeq(v, "yes") || strcaseeq(v, "y") || strcaseeq(v, "true") || strcaseeq(v, "t") || strcaseeq(v, "on")) + if (STRCASE_IN_SET(v, + "1", + "yes", + "y", + "true", + "t", + "on")) return 1; - else if (streq(v, "0") || strcaseeq(v, "no") || strcaseeq(v, "n") || strcaseeq(v, "false") || strcaseeq(v, "f") || strcaseeq(v, "off")) + + if (STRCASE_IN_SET(v, + "0", + "no", + "n", + "false", + "f", + "off")) return 0; return -EINVAL; @@ -59,26 +73,24 @@ int parse_pid(const char *s, pid_t* ret_pid) { } int parse_mode(const char *s, mode_t *ret) { - char *x; - long l; + unsigned m; + int r; assert(s); - assert(ret); - s += strspn(s, WHITESPACE); - if (s[0] == '-') - return -ERANGE; - - errno = 0; - l = strtol(s, &x, 8); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if (l < 0 || l > 07777) + r = safe_atou_full(s, 8 | + SAFE_ATO_REFUSE_PLUS_MINUS, /* Leading '+' or even '-' char? that's just weird, + * refuse. User might have wanted to add mode flags or + * so, but this parser doesn't allow that, so let's + * better be safe. */ + &m); + if (r < 0) + return r; + if (m > 07777) return -ERANGE; - *ret = (mode_t) l; + if (ret) + *ret = m; return 0; } @@ -344,30 +356,73 @@ int parse_syscall_and_errno(const char *in, char **name, int *error) { } #endif /* NM_IGNORED */ +static const char *mangle_base(const char *s, unsigned *base) { + const char *k; + + assert(s); + assert(base); + + /* Base already explicitly specified, then don't do anything. */ + if (SAFE_ATO_MASK_FLAGS(*base) != 0) + return s; + + /* Support Python 3 style "0b" and 0x" prefixes, because they truly make sense, much more than C's "0" prefix for octal. */ + k = STARTSWITH_SET(s, "0b", "0B"); + if (k) { + *base = 2 | (*base & SAFE_ATO_ALL_FLAGS); + return k; + } + + k = STARTSWITH_SET(s, "0o", "0O"); + if (k) { + *base = 8 | (*base & SAFE_ATO_ALL_FLAGS); + return k; + } + + return s; +} + int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { char *x = NULL; unsigned long l; assert(s); - assert(base <= 16); + assert(SAFE_ATO_MASK_FLAGS(base) <= 16); - /* strtoul() is happy to parse negative values, and silently - * converts them to unsigned values without generating an - * error. We want a clean error, hence let's look for the "-" - * prefix on our own, and generate an error. But let's do so - * only after strtoul() validated that the string is clean - * otherwise, so that we return EINVAL preferably over - * ERANGE. */ + /* strtoul() is happy to parse negative values, and silently converts them to unsigned values without + * generating an error. We want a clean error, hence let's look for the "-" prefix on our own, and + * generate an error. But let's do so only after strtoul() validated that the string is clean + * otherwise, so that we return EINVAL preferably over ERANGE. */ + + if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) && + strchr(WHITESPACE, s[0])) + return -EINVAL; s += strspn(s, WHITESPACE); + if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) && + IN_SET(s[0], '+', '-')) + return -EINVAL; /* Note that we check the "-" prefix again a second time below, but return a + * different error. I.e. if the SAFE_ATO_REFUSE_PLUS_MINUS flag is set we + * blanket refuse +/- prefixed integers, while if it is missing we'll just + * return ERANGE, because the string actually parses correctly, but doesn't + * fit in the return type. */ + + if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) && + s[0] == '0' && !streq(s, "0")) + return -EINVAL; /* This is particularly useful to avoid ambiguities between C's octal + * notation and assumed-to-be-decimal integers with a leading zero. */ + + s = mangle_base(s, &base); + errno = 0; - l = strtoul(s, &x, base); + l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base) /* Let's mask off the flags bits so that only the actual + * base is left */); if (errno > 0) return -errno; if (!x || x == s || *x != 0) return -EINVAL; - if (s[0] == '-') + if (l != 0 && s[0] == '-') return -ERANGE; if ((unsigned long) (unsigned) l != l) return -ERANGE; @@ -379,13 +434,17 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { } int safe_atoi(const char *s, int *ret_i) { + unsigned base = 0; char *x = NULL; long l; assert(s); + s += strspn(s, WHITESPACE); + s = mangle_base(s, &base); + errno = 0; - l = strtol(s, &x, 0); + l = strtol(s, &x, base); if (errno > 0) return -errno; if (!x || x == s || *x != 0) @@ -399,21 +458,36 @@ int safe_atoi(const char *s, int *ret_i) { return 0; } -int safe_atollu(const char *s, long long unsigned *ret_llu) { +int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu) { char *x = NULL; unsigned long long l; assert(s); + assert(SAFE_ATO_MASK_FLAGS(base) <= 16); + + if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) && + strchr(WHITESPACE, s[0])) + return -EINVAL; s += strspn(s, WHITESPACE); + if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) && + IN_SET(s[0], '+', '-')) + return -EINVAL; + + if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) && + s[0] == '0' && s[1] != 0) + return -EINVAL; + + s = mangle_base(s, &base); + errno = 0; - l = strtoull(s, &x, 0); + l = strtoull(s, &x, SAFE_ATO_MASK_FLAGS(base)); if (errno > 0) return -errno; if (!x || x == s || *x != 0) return -EINVAL; - if (*s == '-') + if (l != 0 && s[0] == '-') return -ERANGE; if (ret_llu) @@ -423,13 +497,17 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) { } int safe_atolli(const char *s, long long int *ret_lli) { + unsigned base = 0; char *x = NULL; long long l; assert(s); + s += strspn(s, WHITESPACE); + s = mangle_base(s, &base); + errno = 0; - l = strtoll(s, &x, 0); + l = strtoll(s, &x, base); if (errno > 0) return -errno; if (!x || x == s || *x != 0) @@ -442,20 +520,22 @@ int safe_atolli(const char *s, long long int *ret_lli) { } int safe_atou8(const char *s, uint8_t *ret) { - char *x = NULL; + unsigned base = 0; unsigned long l; + char *x = NULL; assert(s); s += strspn(s, WHITESPACE); + s = mangle_base(s, &base); errno = 0; - l = strtoul(s, &x, 0); + l = strtoul(s, &x, base); if (errno > 0) return -errno; if (!x || x == s || *x != 0) return -EINVAL; - if (s[0] == '-') + if (l != 0 && s[0] == '-') return -ERANGE; if ((unsigned long) (uint8_t) l != l) return -ERANGE; @@ -470,34 +550,53 @@ int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) { unsigned long l; assert(s); - assert(ret); - assert(base <= 16); + assert(SAFE_ATO_MASK_FLAGS(base) <= 16); + + if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) && + strchr(WHITESPACE, s[0])) + return -EINVAL; s += strspn(s, WHITESPACE); + if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) && + IN_SET(s[0], '+', '-')) + return -EINVAL; + + if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) && + s[0] == '0' && s[1] != 0) + return -EINVAL; + + s = mangle_base(s, &base); + errno = 0; - l = strtoul(s, &x, base); + l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base)); if (errno > 0) return -errno; if (!x || x == s || *x != 0) return -EINVAL; - if (s[0] == '-') + if (l != 0 && s[0] == '-') return -ERANGE; if ((unsigned long) (uint16_t) l != l) return -ERANGE; - *ret = (uint16_t) l; + if (ret) + *ret = (uint16_t) l; + return 0; } int safe_atoi16(const char *s, int16_t *ret) { + unsigned base = 0; char *x = NULL; long l; assert(s); + s += strspn(s, WHITESPACE); + s = mangle_base(s, &base); + errno = 0; - l = strtol(s, &x, 0); + l = strtol(s, &x, base); if (errno > 0) return -errno; if (!x || x == s || *x != 0) diff --git a/shared/systemd/src/basic/parse-util.h b/shared/systemd/src/basic/parse-util.h index 36d76ba5..9a516ce5 100644 --- a/shared/systemd/src/basic/parse-util.h +++ b/shared/systemd/src/basic/parse-util.h @@ -21,6 +21,12 @@ int parse_range(const char *t, unsigned *lower, unsigned *upper); int parse_errno(const char *t); int parse_syscall_and_errno(const char *in, char **name, int *error); +#define SAFE_ATO_REFUSE_PLUS_MINUS (1U << 30) +#define SAFE_ATO_REFUSE_LEADING_ZERO (1U << 29) +#define SAFE_ATO_REFUSE_LEADING_WHITESPACE (1U << 28) +#define SAFE_ATO_ALL_FLAGS (SAFE_ATO_REFUSE_PLUS_MINUS|SAFE_ATO_REFUSE_LEADING_ZERO|SAFE_ATO_REFUSE_LEADING_WHITESPACE) +#define SAFE_ATO_MASK_FLAGS(base) ((base) & ~SAFE_ATO_ALL_FLAGS) + int safe_atou_full(const char *s, unsigned base, unsigned *ret_u); static inline int safe_atou(const char *s, unsigned *ret_u) { @@ -28,7 +34,6 @@ static inline int safe_atou(const char *s, unsigned *ret_u) { } int safe_atoi(const char *s, int *ret_i); -int safe_atollu(const char *s, unsigned long long *ret_u); int safe_atolli(const char *s, long long int *ret_i); int safe_atou8(const char *s, uint8_t *ret); @@ -59,6 +64,12 @@ static inline int safe_atoi32(const char *s, int32_t *ret_i) { return safe_atoi(s, (int*) ret_i); } +int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu); + +static inline int safe_atollu(const char *s, long long unsigned *ret_llu) { + return safe_atollu_full(s, 0, ret_llu); +} + static inline int safe_atou64(const char *s, uint64_t *ret_u) { assert_cc(sizeof(uint64_t) == sizeof(unsigned long long)); return safe_atollu(s, (unsigned long long*) ret_u); @@ -69,6 +80,11 @@ static inline int safe_atoi64(const char *s, int64_t *ret_i) { return safe_atolli(s, (long long int*) ret_i); } +static inline int safe_atoux64(const char *s, uint64_t *ret) { + assert_cc(sizeof(int64_t) == sizeof(long long unsigned)); + return safe_atollu_full(s, 16, (long long unsigned*) ret); +} + #if LONG_MAX == INT_MAX static inline int safe_atolu(const char *s, unsigned long *ret_u) { assert_cc(sizeof(unsigned long) == sizeof(unsigned)); diff --git a/shared/systemd/src/basic/path-util.c b/shared/systemd/src/basic/path-util.c index 7baab4be..45477bc2 100644 --- a/shared/systemd/src/basic/path-util.c +++ b/shared/systemd/src/basic/path-util.c @@ -1062,7 +1062,7 @@ int systemd_installation_has_version(const char *root, unsigned minimal_version) if (!path) return -ENOMEM; - r = glob_extend(&names, path); + r = glob_extend(&names, path, 0); if (r == -ENOENT) continue; if (r < 0) diff --git a/shared/systemd/src/basic/process-util.c b/shared/systemd/src/basic/process-util.c index 2de3541c..6dcb26fb 100644 --- a/shared/systemd/src/basic/process-util.c +++ b/shared/systemd/src/basic/process-util.c @@ -212,50 +212,12 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags return 0; } -int rename_process(const char name[]) { - static size_t mm_size = 0; - static char *mm = NULL; - bool truncated = false; - size_t l; - - /* This is a like a poor man's setproctitle(). It changes the comm field, argv[0], and also the glibc's - * internally used name of the process. For the first one a limit of 16 chars applies; to the second one in - * many cases one of 10 (i.e. length of "/sbin/init") — however if we have CAP_SYS_RESOURCES it is unbounded; - * to the third one 7 (i.e. the length of "systemd". If you pass a longer string it will likely be - * truncated. - * - * Returns 0 if a name was set but truncated, > 0 if it was set but not truncated. */ - - if (isempty(name)) - return -EINVAL; /* let's not confuse users unnecessarily with an empty name */ - - if (!is_main_thread()) - return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we - * cache things without locking, and we make assumptions that PR_SET_NAME sets the - * process name that isn't correct on any other threads */ - - l = strlen(name); - - /* First step, change the comm field. The main thread's comm is identical to the process comm. This means we - * can use PR_SET_NAME, which sets the thread name for the calling thread. */ - if (prctl(PR_SET_NAME, name) < 0) - log_debug_errno(errno, "PR_SET_NAME failed: %m"); - if (l >= TASK_COMM_LEN) /* Linux userspace process names can be 15 chars at max */ - truncated = true; +static int update_argv(const char name[], size_t l) { + static int can_do = -1; - /* Second step, change glibc's ID of the process name. */ - if (program_invocation_name) { - size_t k; - - k = strlen(program_invocation_name); - strncpy(program_invocation_name, name, k); - if (l > k) - truncated = true; - } - - /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but - * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at - * the end. This is the best option for changing /proc/self/cmdline. */ + if (can_do == 0) + return 0; + can_do = false; /* We'll set it to true only if the whole process works */ /* Let's not bother with this if we don't have euid == 0. Strictly speaking we should check for the * CAP_SYS_RESOURCE capability which is independent of the euid. In our own code the capability generally is @@ -263,22 +225,29 @@ int rename_process(const char name[]) { * PR_SET_MM_ARG_{START,END} fails with EPERM later on anyway. After all geteuid() is dead cheap to call, but * mmap() is not. */ if (geteuid() != 0) - log_debug("Skipping PR_SET_MM, as we don't have privileges."); - else if (mm_size < l+1) { + return log_debug_errno(SYNTHETIC_ERRNO(EPERM), + "Skipping PR_SET_MM, as we don't have privileges."); + + static size_t mm_size = 0; + static char *mm = NULL; + int r; + + if (mm_size < l+1) { size_t nn_size; char *nn; nn_size = PAGE_ALIGN(l+1); nn = mmap(NULL, nn_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - if (nn == MAP_FAILED) { - log_debug_errno(errno, "mmap() failed: %m"); - goto use_saved_argv; - } + if (nn == MAP_FAILED) + return log_debug_errno(errno, "mmap() failed: %m"); strncpy(nn, name, nn_size); /* Now, let's tell the kernel about this new memory */ if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) { + if (ERRNO_IS_PRIVILEGE(errno)) + return log_debug_errno(errno, "PR_SET_MM_ARG_START failed: %m"); + /* HACK: prctl() API is kind of dumb on this point. The existing end address may already be * below the desired start address, in which case the kernel may have kicked this back due * to a range-check failure (see linux/kernel/sys.c:validate_prctl_map() to see this in @@ -290,15 +259,13 @@ int rename_process(const char name[]) { log_debug_errno(errno, "PR_SET_MM_ARG_START failed, attempting PR_SET_MM_ARG_END hack: %m"); if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) { - log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m"); + r = log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m"); (void) munmap(nn, nn_size); - goto use_saved_argv; + return r; } - if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) { - log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m"); - goto use_saved_argv; - } + if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) + return log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m"); } else { /* And update the end pointer to the new end, too. If this fails, we don't really know what * to do, it's pretty unlikely that we can rollback, hence we'll just accept the failure, @@ -320,13 +287,56 @@ int rename_process(const char name[]) { log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m"); } -use_saved_argv: + can_do = true; + return 0; +} + +int rename_process(const char name[]) { + bool truncated = false; + + /* This is a like a poor man's setproctitle(). It changes the comm field, argv[0], and also the glibc's + * internally used name of the process. For the first one a limit of 16 chars applies; to the second one in + * many cases one of 10 (i.e. length of "/sbin/init") — however if we have CAP_SYS_RESOURCES it is unbounded; + * to the third one 7 (i.e. the length of "systemd". If you pass a longer string it will likely be + * truncated. + * + * Returns 0 if a name was set but truncated, > 0 if it was set but not truncated. */ + + if (isempty(name)) + return -EINVAL; /* let's not confuse users unnecessarily with an empty name */ + + if (!is_main_thread()) + return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we + * cache things without locking, and we make assumptions that PR_SET_NAME sets the + * process name that isn't correct on any other threads */ + + size_t l = strlen(name); + + /* First step, change the comm field. The main thread's comm is identical to the process comm. This means we + * can use PR_SET_NAME, which sets the thread name for the calling thread. */ + if (prctl(PR_SET_NAME, name) < 0) + log_debug_errno(errno, "PR_SET_NAME failed: %m"); + if (l >= TASK_COMM_LEN) /* Linux userspace process names can be 15 chars at max */ + truncated = true; + + /* Second step, change glibc's ID of the process name. */ + if (program_invocation_name) { + size_t k; + + k = strlen(program_invocation_name); + strncpy(program_invocation_name, name, k); + if (l > k) + truncated = true; + } + + /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but + * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at + * the end. This is the best option for changing /proc/self/cmdline. */ + (void) update_argv(name, l); + /* Fourth step: in all cases we'll also update the original argv[], so that our own code gets it right too if * it still looks here */ - if (saved_argc > 0) { - int i; - if (saved_argv[0]) { size_t k; @@ -336,7 +346,7 @@ use_saved_argv: truncated = true; } - for (i = 1; i < saved_argc; i++) { + for (int i = 1; i < saved_argc; i++) { if (!saved_argv[i]) break; @@ -634,6 +644,23 @@ int get_process_ppid(pid_t pid, pid_t *_ppid) { return 0; } +int get_process_umask(pid_t pid, mode_t *umask) { + _cleanup_free_ char *m = NULL; + const char *p; + int r; + + assert(umask); + assert(pid >= 0); + + p = procfs_file_alloca(pid, "status"); + + r = get_proc_field(p, "Umask", WHITESPACE, &m); + if (r == -ENOENT) + return -ESRCH; + + return parse_mode(m, umask); +} + int wait_for_terminate(pid_t pid, siginfo_t *status) { siginfo_t dummy; @@ -1284,8 +1311,8 @@ int safe_fork_full( r, "Failed to rename process, ignoring: %m"); } - if (flags & FORK_DEATHSIG) - if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) { + if (flags & (FORK_DEATHSIG|FORK_DEATHSIG_SIGINT)) + if (prctl(PR_SET_PDEATHSIG, (flags & FORK_DEATHSIG_SIGINT) ? SIGINT : SIGTERM) < 0) { log_full_errno(prio, errno, "Failed to set death signal: %m"); _exit(EXIT_FAILURE); } diff --git a/shared/systemd/src/basic/process-util.h b/shared/systemd/src/basic/process-util.h index 7b70c9f3..05012377 100644 --- a/shared/systemd/src/basic/process-util.h +++ b/shared/systemd/src/basic/process-util.h @@ -45,6 +45,7 @@ int get_process_cwd(pid_t pid, char **cwd); int get_process_root(pid_t pid, char **root); int get_process_environ(pid_t pid, char **environ); int get_process_ppid(pid_t pid, pid_t *ppid); +int get_process_umask(pid_t pid, mode_t *umask); int wait_for_terminate(pid_t pid, siginfo_t *status); @@ -151,15 +152,16 @@ int must_be_root(void); typedef enum ForkFlags { FORK_RESET_SIGNALS = 1 << 0, /* Reset all signal handlers and signal mask */ FORK_CLOSE_ALL_FDS = 1 << 1, /* Close all open file descriptors in the child, except for 0,1,2 */ - FORK_DEATHSIG = 1 << 2, /* Set PR_DEATHSIG in the child */ - FORK_NULL_STDIO = 1 << 3, /* Connect 0,1,2 to /dev/null */ - FORK_REOPEN_LOG = 1 << 4, /* Reopen log connection */ - FORK_LOG = 1 << 5, /* Log above LOG_DEBUG log level about failures */ - FORK_WAIT = 1 << 6, /* Wait until child exited */ - FORK_NEW_MOUNTNS = 1 << 7, /* Run child in its own mount namespace */ - FORK_MOUNTNS_SLAVE = 1 << 8, /* Make child's mount namespace MS_SLAVE */ - FORK_RLIMIT_NOFILE_SAFE = 1 << 9, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */ - FORK_STDOUT_TO_STDERR = 1 << 10, /* Make stdout a copy of stderr */ + FORK_DEATHSIG = 1 << 2, /* Set PR_DEATHSIG in the child to SIGTERM */ + FORK_DEATHSIG_SIGINT = 1 << 3, /* Set PR_DEATHSIG in the child to SIGINT */ + FORK_NULL_STDIO = 1 << 4, /* Connect 0,1,2 to /dev/null */ + FORK_REOPEN_LOG = 1 << 5, /* Reopen log connection */ + FORK_LOG = 1 << 6, /* Log above LOG_DEBUG log level about failures */ + FORK_WAIT = 1 << 7, /* Wait until child exited */ + FORK_NEW_MOUNTNS = 1 << 8, /* Run child in its own mount namespace */ + FORK_MOUNTNS_SLAVE = 1 << 9, /* Make child's mount namespace MS_SLAVE */ + FORK_RLIMIT_NOFILE_SAFE = 1 << 10, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */ + FORK_STDOUT_TO_STDERR = 1 << 11, /* Make stdout a copy of stderr */ } ForkFlags; int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid); diff --git a/shared/systemd/src/basic/random-util.c b/shared/systemd/src/basic/random-util.c index 1a294944..512e7af9 100644 --- a/shared/systemd/src/basic/random-util.c +++ b/shared/systemd/src/basic/random-util.c @@ -21,6 +21,7 @@ #endif #include "alloc-util.h" +#include "errno-util.h" #include "fd-util.h" #include "fileio.h" #include "io-util.h" @@ -215,7 +216,9 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) { r = -1; errno = ENOSYS; #else - r = getrandom(p, n, FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_NONBLOCK); + r = getrandom(p, n, + (FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_NONBLOCK) | + (FLAGS_SET(flags, RANDOM_ALLOW_INSECURE) ? GRND_INSECURE : 0)); #endif if (r > 0) { have_syscall = true; @@ -246,7 +249,7 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) { have_syscall = true; return -EIO; - } else if (errno == ENOSYS) { + } else if (ERRNO_IS_NOT_SUPPORTED(errno)) { /* We lack the syscall, continue with reading from /dev/urandom. */ have_syscall = false; break; @@ -272,6 +275,18 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) { /* Use /dev/urandom instead */ break; + + } else if (errno == EINVAL) { + + /* Most likely: unknown flag. We know that GRND_INSECURE might cause this, + * hence try without. */ + + if (FLAGS_SET(flags, RANDOM_ALLOW_INSECURE)) { + flags = flags &~ RANDOM_ALLOW_INSECURE; + continue; + } + + return -errno; } else return -errno; } @@ -334,9 +349,11 @@ void initialize_srand(void) { /* INT_MAX gives us only 31 bits, so use 24 out of that. */ #if RAND_MAX >= INT_MAX +assert_cc(RAND_MAX >= 16777215); # define RAND_STEP 3 #else -/* SHORT_INT_MAX or lower gives at most 15 bits, we just just 8 out of that. */ +/* SHORT_INT_MAX or lower gives at most 15 bits, we just use 8 out of that. */ +assert_cc(RAND_MAX >= 255); # define RAND_STEP 1 #endif @@ -401,7 +418,7 @@ void random_bytes(void *p, size_t n) { * This function is hence not useful for generating UUIDs or cryptographic key material. */ - if (genuine_random_bytes(p, n, RANDOM_EXTEND_WITH_PSEUDO|RANDOM_MAY_FAIL|RANDOM_ALLOW_RDRAND) >= 0) + if (genuine_random_bytes(p, n, RANDOM_EXTEND_WITH_PSEUDO|RANDOM_MAY_FAIL|RANDOM_ALLOW_RDRAND|RANDOM_ALLOW_INSECURE) >= 0) return; /* If for some reason some user made /dev/urandom unavailable to us, or the kernel has no entropy, use a PRNG instead. */ diff --git a/shared/systemd/src/basic/random-util.h b/shared/systemd/src/basic/random-util.h index facc11b9..d8e067d9 100644 --- a/shared/systemd/src/basic/random-util.h +++ b/shared/systemd/src/basic/random-util.h @@ -10,6 +10,7 @@ typedef enum RandomFlags { RANDOM_BLOCK = 1 << 1, /* Rather block than return crap randomness (only if the kernel supports that) */ RANDOM_MAY_FAIL = 1 << 2, /* If we can't get any randomness at all, return early with -ENODATA */ RANDOM_ALLOW_RDRAND = 1 << 3, /* Allow usage of the CPU RNG */ + RANDOM_ALLOW_INSECURE = 1 << 4, /* Allow usage of GRND_INSECURE flag to kernel's getrandom() API */ } RandomFlags; int genuine_random_bytes(void *p, size_t n, RandomFlags flags); /* returns "genuine" randomness, optionally filled up with pseudo random, if not enough is available */ diff --git a/shared/systemd/src/basic/set.h b/shared/systemd/src/basic/set.h index 5f195617..621e83bf 100644 --- a/shared/systemd/src/basic/set.h +++ b/shared/systemd/src/basic/set.h @@ -5,40 +5,48 @@ #include "hashmap.h" #include "macro.h" -Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS) +#define set_free_and_replace(a, b) \ + ({ \ + set_free(a); \ + (a) = (b); \ + (b) = NULL; \ + 0; \ + }) + +Set *_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define set_new(ops) _set_new(ops HASHMAP_DEBUG_SRC_ARGS) static inline Set *set_free(Set *s) { - return (Set*) internal_hashmap_free(HASHMAP_BASE(s), NULL, NULL); + return (Set*) _hashmap_free(HASHMAP_BASE(s), NULL, NULL); } static inline Set *set_free_free(Set *s) { - return (Set*) internal_hashmap_free(HASHMAP_BASE(s), free, NULL); + return (Set*) _hashmap_free(HASHMAP_BASE(s), free, NULL); } /* no set_free_free_free */ static inline Set *set_copy(Set *s) { - return (Set*) internal_hashmap_copy(HASHMAP_BASE(s)); + return (Set*) _hashmap_copy(HASHMAP_BASE(s)); } -int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) +int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define set_ensure_allocated(h, ops) _set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) int set_put(Set *s, const void *key); /* no set_update */ /* no set_replace */ static inline void *set_get(const Set *s, void *key) { - return internal_hashmap_get(HASHMAP_BASE((Set *) s), key); + return _hashmap_get(HASHMAP_BASE((Set *) s), key); } /* no set_get2 */ static inline bool set_contains(const Set *s, const void *key) { - return internal_hashmap_contains(HASHMAP_BASE((Set *) s), key); + return _hashmap_contains(HASHMAP_BASE((Set *) s), key); } static inline void *set_remove(Set *s, const void *key) { - return internal_hashmap_remove(HASHMAP_BASE(s), key); + return _hashmap_remove(HASHMAP_BASE(s), key); } /* no set_remove2 */ @@ -48,19 +56,19 @@ int set_remove_and_put(Set *s, const void *old_key, const void *new_key); int set_merge(Set *s, Set *other); static inline int set_reserve(Set *h, unsigned entries_add) { - return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); + return _hashmap_reserve(HASHMAP_BASE(h), entries_add); } static inline int set_move(Set *s, Set *other) { - return internal_hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other)); + return _hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other)); } static inline int set_move_one(Set *s, Set *other, const void *key) { - return internal_hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key); + return _hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key); } static inline unsigned set_size(const Set *s) { - return internal_hashmap_size(HASHMAP_BASE((Set *) s)); + return _hashmap_size(HASHMAP_BASE((Set *) s)); } static inline bool set_isempty(const Set *s) { @@ -68,23 +76,23 @@ static inline bool set_isempty(const Set *s) { } static inline unsigned set_buckets(const Set *s) { - return internal_hashmap_buckets(HASHMAP_BASE((Set *) s)); + return _hashmap_buckets(HASHMAP_BASE((Set *) s)); } bool set_iterate(const Set *s, Iterator *i, void **value); static inline void set_clear(Set *s) { - internal_hashmap_clear(HASHMAP_BASE(s), NULL, NULL); + _hashmap_clear(HASHMAP_BASE(s), NULL, NULL); } static inline void set_clear_free(Set *s) { - internal_hashmap_clear(HASHMAP_BASE(s), free, NULL); + _hashmap_clear(HASHMAP_BASE(s), free, NULL); } /* no set_clear_free_free */ static inline void *set_steal_first(Set *s) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL); + return _hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL); } #define set_clear_with_destructor(_s, _f) \ @@ -103,18 +111,18 @@ static inline void *set_steal_first(Set *s) { /* no set_first_key */ static inline void *set_first(const Set *s) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE((Set *) s), false, NULL); + return _hashmap_first_key_and_value(HASHMAP_BASE((Set *) s), false, NULL); } /* no set_next */ static inline char **set_get_strv(Set *s) { - return internal_hashmap_get_strv(HASHMAP_BASE(s)); + return _hashmap_get_strv(HASHMAP_BASE(s)); } int set_consume(Set *s, void *value); -int set_put_strdup(Set *s, const char *p); -int set_put_strdupv(Set *s, char **l); +int set_put_strdup(Set **s, const char *p); +int set_put_strdupv(Set **s, char **l); int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags); #define SET_FOREACH(e, s, i) \ diff --git a/shared/systemd/src/basic/socket-util.c b/shared/systemd/src/basic/socket-util.c index 452b9ced..9ced3a12 100644 --- a/shared/systemd/src/basic/socket-util.c +++ b/shared/systemd/src/basic/socket-util.c @@ -25,6 +25,7 @@ #include "fd-util.h" #include "fileio.h" #include "format-util.h" +#include "io-util.h" #include "log.h" #include "macro.h" #include "memory-util.h" @@ -823,10 +824,7 @@ ssize_t send_one_fd_iov_sa( const struct sockaddr *sa, socklen_t len, int flags) { - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int))]; - } control = {}; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int))) control = {}; struct msghdr mh = { .msg_name = (struct sockaddr*) sa, .msg_namelen = len, @@ -855,8 +853,6 @@ ssize_t send_one_fd_iov_sa( cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(int)); memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); - - mh.msg_controllen = CMSG_SPACE(sizeof(int)); } k = sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags); if (k < 0) @@ -882,17 +878,14 @@ ssize_t receive_one_fd_iov( int flags, int *ret_fd) { - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int))]; - } control = {}; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int))) control; struct msghdr mh = { .msg_control = &control, .msg_controllen = sizeof(control), .msg_iov = iov, .msg_iovlen = iovlen, }; - struct cmsghdr *cmsg, *found = NULL; + struct cmsghdr *found; ssize_t k; assert(transport_fd >= 0); @@ -906,26 +899,18 @@ ssize_t receive_one_fd_iov( * combination with send_one_fd(). */ - k = recvmsg(transport_fd, &mh, MSG_CMSG_CLOEXEC | flags); + k = recvmsg_safe(transport_fd, &mh, MSG_CMSG_CLOEXEC | flags); if (k < 0) - return (ssize_t) -errno; + return k; - CMSG_FOREACH(cmsg, &mh) { - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_RIGHTS && - cmsg->cmsg_len == CMSG_LEN(sizeof(int))) { - assert(!found); - found = cmsg; - break; - } - } - - if (!found) + found = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int))); + if (!found) { cmsg_close_all(&mh); - /* If didn't receive an FD or any data, return an error. */ - if (k == 0 && !found) - return -EIO; + /* If didn't receive an FD or any data, return an error. */ + if (k == 0) + return -EIO; + } if (found) *ret_fd = *(int*) CMSG_DATA(found); @@ -991,10 +976,6 @@ fallback: int flush_accept(int fd) { - struct pollfd pollfd = { - .fd = fd, - .events = POLLIN, - }; int r, b; socklen_t l = sizeof(b); @@ -1015,12 +996,12 @@ int flush_accept(int fd) { for (unsigned iteration = 0;; iteration++) { int cfd; - r = poll(&pollfd, 1, 0); + r = fd_wait_for_event(fd, POLLIN, 0); if (r < 0) { - if (errno == EINTR) + if (r == -EINTR) continue; - return -errno; + return r; } if (r == 0) return 0; @@ -1043,6 +1024,7 @@ int flush_accept(int fd) { safe_close(cfd); } } +#endif /* NM_IGNORED */ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length) { struct cmsghdr *cmsg; @@ -1058,6 +1040,7 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng return NULL; } +#if 0 /* NM_IGNORED */ int socket_ioctl_fd(void) { int fd; @@ -1179,3 +1162,46 @@ int socket_bind_to_ifindex(int fd, int ifindex) { return socket_bind_to_ifname(fd, ifname); } + +ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags) { + ssize_t n; + + /* A wrapper around recvmsg() that checks for MSG_CTRUNC, and turns it into an error, in a reasonably + * safe way, closing any SCM_RIGHTS fds in the error path. + * + * Note that unlike our usual coding style this might modify *msg on failure. */ + + n = recvmsg(sockfd, msg, flags); + if (n < 0) + return -errno; + + if (FLAGS_SET(msg->msg_flags, MSG_CTRUNC)) { + cmsg_close_all(msg); + return -EXFULL; /* a recognizable error code */ + } + + return n; +} + +int socket_pass_pktinfo(int fd, bool b) { + int af; + socklen_t sl = sizeof(af); + + if (getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &af, &sl) < 0) + return -errno; + + switch (af) { + + case AF_INET: + return setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, b); + + case AF_INET6: + return setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, b); + + case AF_NETLINK: + return setsockopt_int(fd, SOL_NETLINK, NETLINK_PKTINFO, b); + + default: + return -EAFNOSUPPORT; + } +} diff --git a/shared/systemd/src/basic/socket-util.h b/shared/systemd/src/basic/socket-util.h index 2596c540..5e5cf731 100644 --- a/shared/systemd/src/basic/socket-util.h +++ b/shared/systemd/src/basic/socket-util.h @@ -160,6 +160,25 @@ int flush_accept(int fd); struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length); +/* Type-safe, dereferencing version of cmsg_find() */ +#define CMSG_FIND_DATA(mh, level, type, ctype) \ + ({ \ + struct cmsghdr *_found; \ + _found = cmsg_find(mh, level, type, CMSG_LEN(sizeof(ctype))); \ + (ctype*) (_found ? CMSG_DATA(_found) : NULL); \ + }) + +/* Resolves to a type that can carry cmsghdr structures. Make sure things are properly aligned, i.e. the type + * itself is placed properly in memory and the size is also aligned to what's appropriate for "cmsghdr" + * structures. */ +#define CMSG_BUFFER_TYPE(size) \ + union { \ + struct cmsghdr cmsghdr; \ + uint8_t buf[size]; \ + uint8_t align_check[(size) >= CMSG_SPACE(0) && \ + (size) == CMSG_ALIGN(size) ? 1 : -1]; \ + } + /* * Certain hardware address types (e.g Infiniband) do not fit into sll_addr * (8 bytes) and run over the structure. This macro returns the correct size that @@ -201,3 +220,7 @@ static inline int setsockopt_int(int fd, int level, int optname, int value) { int socket_bind_to_ifname(int fd, const char *ifname); int socket_bind_to_ifindex(int fd, int ifindex); + +ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags); + +int socket_pass_pktinfo(int fd, bool b); diff --git a/shared/systemd/src/basic/sort-util.h b/shared/systemd/src/basic/sort-util.h index e029f864..a8dc3bb6 100644 --- a/shared/systemd/src/basic/sort-util.h +++ b/shared/systemd/src/basic/sort-util.h @@ -39,7 +39,7 @@ static inline void* bsearch_safe(const void *key, const void *base, * Normal qsort requires base to be nonnull. Here were require * that only if nmemb > 0. */ -static inline void qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn_t compar) { +static inline void _qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn_t compar) { if (nmemb <= 1) return; @@ -52,7 +52,7 @@ static inline void qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn #define typesafe_qsort(p, n, func) \ ({ \ int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \ - qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \ + _qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \ }) static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, __compar_d_fn_t compar, void *userdata) { diff --git a/shared/systemd/src/basic/stat-util.c b/shared/systemd/src/basic/stat-util.c index a895c1c3..5c51e13a 100644 --- a/shared/systemd/src/basic/stat-util.c +++ b/shared/systemd/src/basic/stat-util.c @@ -99,10 +99,10 @@ bool null_or_empty(struct stat *st) { if (S_ISREG(st->st_mode) && st->st_size <= 0) return true; - /* We don't want to hardcode the major/minor of /dev/null, - * hence we do a simpler "is this a device node?" check. */ + /* We don't want to hardcode the major/minor of /dev/null, hence we do a simpler "is this a character + * device node?" check. */ - if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) + if (S_ISCHR(st->st_mode)) return true; return false; @@ -113,6 +113,10 @@ int null_or_empty_path(const char *fn) { assert(fn); + /* If we have the path, let's do an easy text comparison first. */ + if (path_equal(fn, "/dev/null")) + return true; + if (stat(fn, &st) < 0) return -errno; @@ -183,13 +187,12 @@ int fd_is_fs_type(int fd, statfs_f_type_t magic_value) { } int path_is_fs_type(const char *path, statfs_f_type_t magic_value) { - _cleanup_close_ int fd = -1; + struct statfs s; - fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH); - if (fd < 0) + if (statfs(path, &s) < 0) return -errno; - return fd_is_fs_type(fd, magic_value); + return is_fs_type(&s, magic_value); } bool is_temporary_fs(const struct statfs *s) { @@ -384,4 +387,37 @@ int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret return 0; } + +int proc_mounted(void) { + int r; + + /* A quick check of procfs is properly mounted */ + + r = path_is_fs_type("/proc/", PROC_SUPER_MAGIC); + if (r == -ENOENT) /* not mounted at all */ + return false; + + return r; +} + +bool stat_inode_unmodified(const struct stat *a, const struct stat *b) { + + /* Returns if the specified stat structures reference the same, unmodified inode. This check tries to + * be reasonably careful when detecting changes: we check both inode and mtime, to cater for file + * systems where mtimes are fixed to 0 (think: ostree/nixos type installations). We also check file + * size, backing device, inode type and if this refers to a device not the major/minor. + * + * Note that we don't care if file attributes such as ownership or access mode change, this here is + * about contents of the file. The purpose here is to detect file contents changes, and nothing + * else. */ + + return a && b && + (a->st_mode & S_IFMT) != 0 && /* We use the check for .st_mode if the structure was ever initialized */ + ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 && /* same inode type */ + a->st_mtime == b->st_mtime && + (!S_ISREG(a->st_mode) || a->st_size == b->st_size) && /* if regular file, compare file size */ + a->st_dev == b->st_dev && + a->st_ino == b->st_ino && + (!(S_ISCHR(a->st_mode) || S_ISBLK(a->st_mode)) || a->st_rdev == b->st_rdev); /* if device node, also compare major/minor, because we can */ +} #endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/stat-util.h b/shared/systemd/src/basic/stat-util.h index 7824af35..59aedcb7 100644 --- a/shared/systemd/src/basic/stat-util.h +++ b/shared/systemd/src/basic/stat-util.h @@ -87,3 +87,7 @@ int fd_verify_directory(int fd); int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret); int device_path_make_canonical(mode_t mode, dev_t devno, char **ret); int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno); + +int proc_mounted(void); + +bool stat_inode_unmodified(const struct stat *a, const struct stat *b); diff --git a/shared/systemd/src/basic/string-util.c b/shared/systemd/src/basic/string-util.c index 9f15caca..c30e88c5 100644 --- a/shared/systemd/src/basic/string-util.c +++ b/shared/systemd/src/basic/string-util.c @@ -21,18 +21,19 @@ #include "util.h" int strcmp_ptr(const char *a, const char *b) { - /* Like strcmp(), but tries to make sense of NULL pointers */ + if (a && b) return strcmp(a, b); + return CMP(a, b); /* Direct comparison of pointers, one of which is NULL */ +} - if (!a && b) - return -1; - - if (a && !b) - return 1; +int strcasecmp_ptr(const char *a, const char *b) { + /* Like strcasecmp(), but tries to make sense of NULL pointers */ - return 0; + if (a && b) + return strcasecmp(a, b); + return CMP(a, b); /* Direct comparison of pointers, one of which is NULL */ } char* endswith(const char *s, const char *postfix) { @@ -219,7 +220,6 @@ char *strnappend(const char *s, const char *suffix, size_t b) { return r; } -#if 0 /* NM_IGNORED */ char *strjoin_real(const char *x, ...) { va_list ap; size_t l; @@ -277,6 +277,7 @@ char *strjoin_real(const char *x, ...) { return r; } +#if 0 /* NM_IGNORED */ char *strstrip(char *s) { if (!s) return NULL; diff --git a/shared/systemd/src/basic/string-util.h b/shared/systemd/src/basic/string-util.h index 2a344b99..09131455 100644 --- a/shared/systemd/src/basic/string-util.h +++ b/shared/systemd/src/basic/string-util.h @@ -27,6 +27,7 @@ #define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0) int strcmp_ptr(const char *a, const char *b) _pure_; +int strcasecmp_ptr(const char *a, const char *b) _pure_; static inline bool streq_ptr(const char *a, const char *b) { return strcmp_ptr(a, b) == 0; diff --git a/shared/systemd/src/basic/strv.c b/shared/systemd/src/basic/strv.c index be1c8325..6f812139 100644 --- a/shared/systemd/src/basic/strv.c +++ b/shared/systemd/src/basic/strv.c @@ -30,6 +30,18 @@ char *strv_find(char * const *l, const char *name) { return NULL; } +char *strv_find_case(char * const *l, const char *name) { + char * const *i; + + assert(name); + + STRV_FOREACH(i, l) + if (strcaseeq(*i, name)) + return *i; + + return NULL; +} + char *strv_find_prefix(char * const *l, const char *name) { char * const *i; @@ -944,20 +956,20 @@ static int string_strv_hashmap_put_internal(Hashmap *h, const char *key, const c return 1; } -int string_strv_hashmap_put(Hashmap **h, const char *key, const char *value) { +int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS) { int r; - r = hashmap_ensure_allocated(h, &string_strv_hash_ops); + r = _hashmap_ensure_allocated(h, &string_strv_hash_ops HASHMAP_DEBUG_PASS_ARGS); if (r < 0) return r; return string_strv_hashmap_put_internal(*h, key, value); } -int string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value) { +int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS) { int r; - r = ordered_hashmap_ensure_allocated(h, &string_strv_hash_ops); + r = _ordered_hashmap_ensure_allocated(h, &string_strv_hash_ops HASHMAP_DEBUG_PASS_ARGS); if (r < 0) return r; diff --git a/shared/systemd/src/basic/strv.h b/shared/systemd/src/basic/strv.h index dd3323c2..2ad927bc 100644 --- a/shared/systemd/src/basic/strv.h +++ b/shared/systemd/src/basic/strv.h @@ -14,9 +14,13 @@ #include "string-util.h" char *strv_find(char * const *l, const char *name) _pure_; +char *strv_find_case(char * const *l, const char *name) _pure_; char *strv_find_prefix(char * const *l, const char *name) _pure_; char *strv_find_startswith(char * const *l, const char *name) _pure_; +#define strv_contains(l, s) (!!strv_find((l), (s))) +#define strv_contains_case(l, s) (!!strv_find_case((l), (s))) + char **strv_free(char **l); DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free); #define _cleanup_strv_free_ _cleanup_(strv_freep) @@ -54,8 +58,6 @@ static inline bool strv_equal(char * const *a, char * const *b) { return strv_compare(a, b) == 0; } -#define strv_contains(l, s) (!!strv_find((l), (s))) - char **strv_new_internal(const char *x, ...) _sentinel_; char **strv_new_ap(const char *x, va_list ap); #define strv_new(...) strv_new_internal(__VA_ARGS__, NULL) @@ -104,14 +106,14 @@ bool strv_overlap(char * const *a, char * const *b) _pure_; #define STRV_FOREACH_BACKWARDS(s, l) \ for (s = ({ \ - char **_l = l; \ + typeof(l) _l = l; \ _l ? _l + strv_length(_l) - 1U : NULL; \ }); \ (l) && ((s) >= (l)); \ (s)--) #define STRV_FOREACH_PAIR(x, y, l) \ - for ((x) = (l), (y) = (x+1); (x) && *(x) && *(y); (x) += 2, (y) = (x + 1)) + for ((x) = (l), (y) = (x) ? (x+1) : NULL; (x) && *(x) && *(y); (x) += 2, (y) = (x + 1)) char **strv_sort(char **l); void strv_print(char * const *l); @@ -156,6 +158,13 @@ void strv_print(char * const *l); _x && strv_contains(STRV_MAKE(__VA_ARGS__), _x); \ }) +#define STRCASE_IN_SET(x, ...) strv_contains_case(STRV_MAKE(__VA_ARGS__), x) +#define STRCASEPTR_IN_SET(x, ...) \ + ({ \ + const char* _x = (x); \ + _x && strv_contains_case(STRV_MAKE(__VA_ARGS__), _x); \ + }) + #define STARTSWITH_SET(p, ...) \ ({ \ const char *_p = (p); \ @@ -217,5 +226,7 @@ int fputstrv(FILE *f, char * const *l, const char *separator, bool *space); }) extern const struct hash_ops string_strv_hash_ops; -int string_strv_hashmap_put(Hashmap **h, const char *key, const char *value); -int string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value); +int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS); +int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS); +#define string_strv_hashmap_put(h, k, v) _string_strv_hashmap_put(h, k, v HASHMAP_DEBUG_SRC_ARGS) +#define string_strv_ordered_hashmap_put(h, k, v) _string_strv_ordered_hashmap_put(h, k, v HASHMAP_DEBUG_SRC_ARGS) diff --git a/shared/systemd/src/basic/utf8.c b/shared/systemd/src/basic/utf8.c index ba28e129..f2e27b68 100644 --- a/shared/systemd/src/basic/utf8.c +++ b/shared/systemd/src/basic/utf8.c @@ -50,7 +50,6 @@ bool unichar_is_valid(char32_t ch) { return true; } -#if 0 /* NM_IGNORED */ static bool unichar_is_control(char32_t ch) { /* @@ -62,7 +61,6 @@ static bool unichar_is_control(char32_t ch) { return (ch < ' ' && !IN_SET(ch, '\t', '\n')) || (0x7F <= ch && ch <= 0x9F); } -#endif /* NM_IGNORED */ /* count of characters used to encode one unicode char */ static size_t utf8_encoded_expected_len(uint8_t c) { @@ -127,7 +125,6 @@ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) { return 0; } -#if 0 /* NM_IGNORED */ bool utf8_is_printable_newline(const char* str, size_t length, bool newline) { const char *p; @@ -154,7 +151,6 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool newline) { return true; } -#endif /* NM_IGNORED */ char *utf8_is_valid(const char *str) { const char *p; diff --git a/shared/systemd/src/shared/web-util.c b/shared/systemd/src/shared/web-util.c index f5a5362b..4cff5e27 100644 --- a/shared/systemd/src/shared/web-util.c +++ b/shared/systemd/src/shared/web-util.c @@ -24,21 +24,25 @@ bool http_etag_is_valid(const char *etag) { } #endif /* NM_IGNORED */ -bool http_url_is_valid(const char *url) { - const char *p; - - if (isempty(url)) - return false; - - p = STARTSWITH_SET(url, "http://", "https://"); - if (!p) - return false; - - if (isempty(p)) - return false; - - return ascii_is_valid(p); -} +/* NM: we use http_url_is_valid() for our own code, and it must not + * change behavior. If a re-import results in a merge-conflict, you must + * ensure that it does not change behavior, and possibly do something + * about that. */ +/**/ bool http_url_is_valid(const char *url) { +/**/ const char *p; +/**/ +/**/ if (isempty(url)) +/**/ return false; +/**/ +/**/ p = STARTSWITH_SET(url, "http://", "https://"); +/**/ if (!p) +/**/ return false; +/**/ +/**/ if (isempty(p)) +/**/ return false; +/**/ +/**/ return ascii_is_valid(p); +/**/ } #if 0 /* NM_IGNORED */ bool documentation_url_is_valid(const char *url) { |