1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <netinet/ether.h>
4 
5 #include "condition.h"
6 #include "env-util.h"
7 #include "log.h"
8 #include "net-condition.h"
9 #include "netif-util.h"
10 #include "network-util.h"
11 #include "socket-util.h"
12 #include "string-table.h"
13 #include "strv.h"
14 #include "wifi-util.h"
15 
net_match_clear(NetMatch * match)16 void net_match_clear(NetMatch *match) {
17         if (!match)
18                 return;
19 
20         match->hw_addr = set_free(match->hw_addr);
21         match->permanent_hw_addr = set_free(match->permanent_hw_addr);
22         match->path = strv_free(match->path);
23         match->driver = strv_free(match->driver);
24         match->iftype = strv_free(match->iftype);
25         match->kind = strv_free(match->kind);
26         match->ifname = strv_free(match->ifname);
27         match->property = strv_free(match->property);
28         match->wlan_iftype = strv_free(match->wlan_iftype);
29         match->ssid = strv_free(match->ssid);
30         match->bssid = set_free(match->bssid);
31 }
32 
net_match_is_empty(const NetMatch * match)33 bool net_match_is_empty(const NetMatch *match) {
34         assert(match);
35 
36         return
37                 set_isempty(match->hw_addr) &&
38                 set_isempty(match->permanent_hw_addr) &&
39                 strv_isempty(match->path) &&
40                 strv_isempty(match->driver) &&
41                 strv_isempty(match->iftype) &&
42                 strv_isempty(match->kind) &&
43                 strv_isempty(match->ifname) &&
44                 strv_isempty(match->property) &&
45                 strv_isempty(match->wlan_iftype) &&
46                 strv_isempty(match->ssid) &&
47                 set_isempty(match->bssid);
48 }
49 
net_condition_test_strv(char * const * patterns,const char * string)50 static bool net_condition_test_strv(char * const *patterns, const char *string) {
51         bool match = false, has_positive_rule = false;
52 
53         if (strv_isempty(patterns))
54                 return true;
55 
56         STRV_FOREACH(p, patterns) {
57                 const char *q = *p;
58                 bool invert;
59 
60                 invert = *q == '!';
61                 q += invert;
62 
63                 if (!invert)
64                         has_positive_rule = true;
65 
66                 if (string && fnmatch(q, string, 0) == 0) {
67                         if (invert)
68                                 return false;
69                         else
70                                 match = true;
71                 }
72         }
73 
74         return has_positive_rule ? match : true;
75 }
76 
net_condition_test_ifname(char * const * patterns,const char * ifname,char * const * alternative_names)77 static bool net_condition_test_ifname(char * const *patterns, const char *ifname, char * const *alternative_names) {
78         if (net_condition_test_strv(patterns, ifname))
79                 return true;
80 
81         STRV_FOREACH(p, alternative_names)
82                 if (net_condition_test_strv(patterns, *p))
83                         return true;
84 
85         return false;
86 }
87 
net_condition_test_property(char * const * match_property,sd_device * device)88 static int net_condition_test_property(char * const *match_property, sd_device *device) {
89         if (strv_isempty(match_property))
90                 return true;
91 
92         STRV_FOREACH(p, match_property) {
93                 _cleanup_free_ char *key = NULL;
94                 const char *val, *dev_val;
95                 bool invert, v;
96 
97                 invert = **p == '!';
98 
99                 val = strchr(*p + invert, '=');
100                 if (!val)
101                         return -EINVAL;
102 
103                 key = strndup(*p + invert, val - *p - invert);
104                 if (!key)
105                         return -ENOMEM;
106 
107                 val++;
108 
109                 v = device &&
110                         sd_device_get_property_value(device, key, &dev_val) >= 0 &&
111                         fnmatch(val, dev_val, 0) == 0;
112 
113                 if (invert ? v : !v)
114                         return false;
115         }
116 
117         return true;
118 }
119 
net_match_config(const NetMatch * match,sd_device * device,const struct hw_addr_data * hw_addr,const struct hw_addr_data * permanent_hw_addr,const char * driver,unsigned short iftype,const char * kind,const char * ifname,char * const * alternative_names,enum nl80211_iftype wlan_iftype,const char * ssid,const struct ether_addr * bssid)120 int net_match_config(
121                 const NetMatch *match,
122                 sd_device *device,
123                 const struct hw_addr_data *hw_addr,
124                 const struct hw_addr_data *permanent_hw_addr,
125                 const char *driver,
126                 unsigned short iftype,
127                 const char *kind,
128                 const char *ifname,
129                 char * const *alternative_names,
130                 enum nl80211_iftype wlan_iftype,
131                 const char *ssid,
132                 const struct ether_addr *bssid) {
133 
134         _cleanup_free_ char *iftype_str = NULL;
135         const char *path = NULL;
136 
137         assert(match);
138 
139         if (net_get_type_string(device, iftype, &iftype_str) == -ENOMEM)
140                 return -ENOMEM;
141 
142         if (device)
143                 (void) sd_device_get_property_value(device, "ID_PATH", &path);
144 
145         if (match->hw_addr && (!hw_addr || !set_contains(match->hw_addr, hw_addr)))
146                 return false;
147 
148         if (match->permanent_hw_addr &&
149             (!permanent_hw_addr ||
150              !set_contains(match->permanent_hw_addr, permanent_hw_addr)))
151                 return false;
152 
153         if (!net_condition_test_strv(match->path, path))
154                 return false;
155 
156         if (!net_condition_test_strv(match->driver, driver))
157                 return false;
158 
159         if (!net_condition_test_strv(match->iftype, iftype_str))
160                 return false;
161 
162         if (!net_condition_test_strv(match->kind, kind))
163                 return false;
164 
165         if (!net_condition_test_ifname(match->ifname, ifname, alternative_names))
166                 return false;
167 
168         if (!net_condition_test_property(match->property, device))
169                 return false;
170 
171         if (!net_condition_test_strv(match->wlan_iftype, nl80211_iftype_to_string(wlan_iftype)))
172                 return false;
173 
174         if (!net_condition_test_strv(match->ssid, ssid))
175                 return false;
176 
177         if (match->bssid && (!bssid || !set_contains(match->bssid, bssid)))
178                 return false;
179 
180         return true;
181 }
182 
config_parse_net_condition(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)183 int config_parse_net_condition(
184                 const char *unit,
185                 const char *filename,
186                 unsigned line,
187                 const char *section,
188                 unsigned section_line,
189                 const char *lvalue,
190                 int ltype,
191                 const char *rvalue,
192                 void *data,
193                 void *userdata) {
194 
195         ConditionType cond = ltype;
196         Condition **list = data, *c;
197         bool negate;
198 
199         assert(filename);
200         assert(lvalue);
201         assert(rvalue);
202         assert(data);
203 
204         if (isempty(rvalue)) {
205                 *list = condition_free_list_type(*list, cond);
206                 return 0;
207         }
208 
209         negate = rvalue[0] == '!';
210         if (negate)
211                 rvalue++;
212 
213         c = condition_new(cond, rvalue, false, negate);
214         if (!c)
215                 return log_oom();
216 
217         /* Drop previous assignment. */
218         *list = condition_free_list_type(*list, cond);
219 
220         LIST_PREPEND(conditions, *list, c);
221         return 0;
222 }
223 
config_parse_match_strv(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)224 int config_parse_match_strv(
225                 const char *unit,
226                 const char *filename,
227                 unsigned line,
228                 const char *section,
229                 unsigned section_line,
230                 const char *lvalue,
231                 int ltype,
232                 const char *rvalue,
233                 void *data,
234                 void *userdata) {
235 
236         const char *p = rvalue;
237         char ***sv = data;
238         bool invert;
239         int r;
240 
241         assert(filename);
242         assert(lvalue);
243         assert(rvalue);
244         assert(data);
245 
246         if (isempty(rvalue)) {
247                 *sv = strv_free(*sv);
248                 return 0;
249         }
250 
251         invert = *p == '!';
252         p += invert;
253 
254         for (;;) {
255                 _cleanup_free_ char *word = NULL, *k = NULL;
256 
257                 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
258                 if (r == 0)
259                         return 0;
260                 if (r == -ENOMEM)
261                         return log_oom();
262                 if (r < 0) {
263                         log_syntax(unit, LOG_WARNING, filename, line, r,
264                                    "Invalid syntax, ignoring: %s", rvalue);
265                         return 0;
266                 }
267 
268                 if (invert) {
269                         k = strjoin("!", word);
270                         if (!k)
271                                 return log_oom();
272                 } else
273                         k = TAKE_PTR(word);
274 
275                 r = strv_consume(sv, TAKE_PTR(k));
276                 if (r < 0)
277                         return log_oom();
278         }
279 }
280 
config_parse_match_ifnames(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)281 int config_parse_match_ifnames(
282                 const char *unit,
283                 const char *filename,
284                 unsigned line,
285                 const char *section,
286                 unsigned section_line,
287                 const char *lvalue,
288                 int ltype,
289                 const char *rvalue,
290                 void *data,
291                 void *userdata) {
292 
293         const char *p = rvalue;
294         char ***sv = data;
295         bool invert;
296         int r;
297 
298         assert(filename);
299         assert(lvalue);
300         assert(rvalue);
301         assert(data);
302 
303         if (isempty(rvalue)) {
304                 *sv = strv_free(*sv);
305                 return 0;
306         }
307 
308         invert = *p == '!';
309         p += invert;
310 
311         for (;;) {
312                 _cleanup_free_ char *word = NULL, *k = NULL;
313 
314                 r = extract_first_word(&p, &word, NULL, 0);
315                 if (r == 0)
316                         return 0;
317                 if (r == -ENOMEM)
318                         return log_oom();
319                 if (r < 0) {
320                         log_syntax(unit, LOG_WARNING, filename, line, 0,
321                                    "Failed to parse interface name list, ignoring: %s", rvalue);
322                         return 0;
323                 }
324 
325                 if (!ifname_valid_full(word, ltype)) {
326                         log_syntax(unit, LOG_WARNING, filename, line, 0,
327                                    "Interface name is not valid or too long, ignoring assignment: %s", word);
328                         continue;
329                 }
330 
331                 if (invert) {
332                         k = strjoin("!", word);
333                         if (!k)
334                                 return log_oom();
335                 } else
336                         k = TAKE_PTR(word);
337 
338                 r = strv_consume(sv, TAKE_PTR(k));
339                 if (r < 0)
340                         return log_oom();
341         }
342 }
343 
config_parse_match_property(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)344 int config_parse_match_property(
345                 const char *unit,
346                 const char *filename,
347                 unsigned line,
348                 const char *section,
349                 unsigned section_line,
350                 const char *lvalue,
351                 int ltype,
352                 const char *rvalue,
353                 void *data,
354                 void *userdata) {
355 
356         const char *p = rvalue;
357         char ***sv = data;
358         bool invert;
359         int r;
360 
361         assert(filename);
362         assert(lvalue);
363         assert(rvalue);
364         assert(data);
365 
366         if (isempty(rvalue)) {
367                 *sv = strv_free(*sv);
368                 return 0;
369         }
370 
371         invert = *p == '!';
372         p += invert;
373 
374         for (;;) {
375                 _cleanup_free_ char *word = NULL, *k = NULL;
376 
377                 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
378                 if (r == 0)
379                         return 0;
380                 if (r == -ENOMEM)
381                         return log_oom();
382                 if (r < 0) {
383                         log_syntax(unit, LOG_WARNING, filename, line, 0,
384                                    "Invalid syntax, ignoring: %s", rvalue);
385                         return 0;
386                 }
387 
388                 if (!env_assignment_is_valid(word)) {
389                         log_syntax(unit, LOG_WARNING, filename, line, 0,
390                                    "Invalid property or value, ignoring assignment: %s", word);
391                         continue;
392                 }
393 
394                 if (invert) {
395                         k = strjoin("!", word);
396                         if (!k)
397                                 return log_oom();
398                 } else
399                         k = TAKE_PTR(word);
400 
401                 r = strv_consume(sv, TAKE_PTR(k));
402                 if (r < 0)
403                         return log_oom();
404         }
405 }
406