/src/systemd/src/shared/parse-helpers.c
Line | Count | Source |
1 | | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2 | | |
3 | | #include <sys/socket.h> |
4 | | |
5 | | #include "af-list.h" |
6 | | #include "alloc-util.h" |
7 | | #include "extract-word.h" |
8 | | #include "ip-protocol-list.h" |
9 | | #include "log.h" |
10 | | #include "mountpoint-util.h" |
11 | | #include "parse-helpers.h" |
12 | | #include "parse-util.h" |
13 | | #include "path-util.h" |
14 | | #include "string-util.h" |
15 | | #include "utf8.h" |
16 | | |
17 | 46.8k | static bool validate_api_vfs(const char *path, PathSimplifyWarnFlags flags) { |
18 | | |
19 | 46.8k | assert(path); |
20 | | |
21 | 46.8k | if ((flags & (PATH_CHECK_NON_API_VFS|PATH_CHECK_NON_API_VFS_DEV_OK)) == 0) |
22 | 29.7k | return true; |
23 | | |
24 | 17.1k | if (!path_below_api_vfs(path)) |
25 | 15.6k | return true; |
26 | | |
27 | 1.46k | if (FLAGS_SET(flags, PATH_CHECK_NON_API_VFS_DEV_OK) && path_startswith(path, "/dev")) |
28 | 358 | return true; |
29 | | |
30 | 1.10k | return false; |
31 | 1.46k | } |
32 | | |
33 | | int path_simplify_and_warn( |
34 | | char *path, |
35 | | PathSimplifyWarnFlags flags, |
36 | | const char *unit, |
37 | | const char *filename, |
38 | | unsigned line, |
39 | 61.4k | const char *lvalue) { |
40 | | |
41 | 61.4k | bool fatal = flags & PATH_CHECK_FATAL; |
42 | 61.4k | int level = fatal ? LOG_ERR : LOG_WARNING; |
43 | | |
44 | 61.4k | assert(path); |
45 | 61.4k | assert(!FLAGS_SET(flags, PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)); |
46 | 61.4k | assert(!FLAGS_SET(flags, PATH_CHECK_NON_API_VFS | PATH_CHECK_NON_API_VFS_DEV_OK)); |
47 | 61.4k | assert(lvalue); |
48 | | |
49 | 61.4k | if (!utf8_is_valid(path)) |
50 | 1.68k | return log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, path); |
51 | | |
52 | 59.7k | if (flags & (PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)) { |
53 | 54.2k | bool absolute; |
54 | | |
55 | 54.2k | absolute = path_is_absolute(path); |
56 | | |
57 | 54.2k | if (!absolute && (flags & PATH_CHECK_ABSOLUTE)) |
58 | 9.12k | return log_syntax(unit, level, filename, line, SYNTHETIC_ERRNO(EINVAL), |
59 | 54.2k | "%s= path is not absolute%s: %s", |
60 | 54.2k | lvalue, fatal ? "" : ", ignoring", path); |
61 | | |
62 | 45.1k | if (absolute && (flags & PATH_CHECK_RELATIVE)) |
63 | 644 | return log_syntax(unit, level, filename, line, SYNTHETIC_ERRNO(EINVAL), |
64 | 45.1k | "%s= path is absolute%s: %s", |
65 | 45.1k | lvalue, fatal ? "" : ", ignoring", path); |
66 | 45.1k | } |
67 | | |
68 | 49.9k | path_simplify_full(path, flags & PATH_KEEP_TRAILING_SLASH ? PATH_SIMPLIFY_KEEP_TRAILING_SLASH : 0); |
69 | | |
70 | 49.9k | if (!path_is_valid(path)) |
71 | 1.42k | return log_syntax(unit, level, filename, line, SYNTHETIC_ERRNO(EINVAL), |
72 | 49.9k | "%s= path has invalid length (%zu bytes)%s.", |
73 | 49.9k | lvalue, strlen(path), fatal ? "" : ", ignoring"); |
74 | | |
75 | 48.5k | if (!path_is_normalized(path)) |
76 | 1.66k | return log_syntax(unit, level, filename, line, SYNTHETIC_ERRNO(EINVAL), |
77 | 48.5k | "%s= path is not normalized%s: %s", |
78 | 48.5k | lvalue, fatal ? "" : ", ignoring", path); |
79 | | |
80 | 46.8k | if (!validate_api_vfs(path, flags)) |
81 | 1.10k | return log_syntax(unit, level, filename, line, SYNTHETIC_ERRNO(EINVAL), |
82 | 46.8k | "%s= path is below API VFS%s: %s", |
83 | 46.8k | lvalue, fatal ? ", refusing" : ", ignoring", |
84 | 46.8k | path); |
85 | | |
86 | 45.7k | return 0; |
87 | 46.8k | } |
88 | | |
89 | | static int parse_af_token( |
90 | | const char *token, |
91 | | int *family, |
92 | | int *ip_protocol, |
93 | | uint16_t *nr_ports, |
94 | 5.35k | uint16_t *port_min) { |
95 | | |
96 | 5.35k | int af; |
97 | | |
98 | 5.35k | assert(token); |
99 | 5.35k | assert(family); |
100 | | |
101 | 5.35k | af = af_from_ipv4_ipv6(token); |
102 | 5.35k | if (af == AF_UNSPEC) |
103 | 4.82k | return -EINVAL; |
104 | | |
105 | 533 | *family = af; |
106 | 533 | return 0; |
107 | 5.35k | } |
108 | | |
109 | | static int parse_ip_protocol_token( |
110 | | const char *token, |
111 | | int *family, |
112 | | int *ip_protocol, |
113 | | uint16_t *nr_ports, |
114 | 5.09k | uint16_t *port_min) { |
115 | | |
116 | 5.09k | int proto; |
117 | | |
118 | 5.09k | assert(token); |
119 | 5.09k | assert(ip_protocol); |
120 | | |
121 | 5.09k | proto = ip_protocol_from_tcp_udp(token); |
122 | 5.09k | if (proto < 0) |
123 | 4.57k | return -EINVAL; |
124 | | |
125 | 519 | *ip_protocol = proto; |
126 | 519 | return 0; |
127 | 5.09k | } |
128 | | |
129 | | static int parse_ip_ports_token( |
130 | | const char *token, |
131 | | int *family, |
132 | | int *ip_protocol, |
133 | | uint16_t *nr_ports, |
134 | 4.57k | uint16_t *port_min) { |
135 | | |
136 | 4.57k | int r; |
137 | | |
138 | 4.57k | assert(token); |
139 | 4.57k | assert(nr_ports); |
140 | 4.57k | assert(port_min); |
141 | | |
142 | 4.57k | if (streq(token, "any")) |
143 | 588 | *nr_ports = *port_min = 0; |
144 | 3.98k | else { |
145 | 3.98k | uint16_t mn = 0, mx = 0; |
146 | 3.98k | r = parse_ip_port_range(token, &mn, &mx, /* allow_zero = */ true); |
147 | 3.98k | if (r < 0) |
148 | 1.39k | return r; |
149 | | |
150 | 2.59k | *nr_ports = mx - mn + 1; |
151 | 2.59k | *port_min = mn; |
152 | 2.59k | } |
153 | | |
154 | 3.18k | return 0; |
155 | 4.57k | } |
156 | | |
157 | | typedef int (*parse_token_f)( |
158 | | const char *, |
159 | | int *, |
160 | | int *, |
161 | | uint16_t *, |
162 | | uint16_t *); |
163 | | |
164 | | int parse_socket_bind_item( |
165 | | const char *str, |
166 | | int *address_family, |
167 | | int *ip_protocol, |
168 | | uint16_t *nr_ports, |
169 | 5.62k | uint16_t *port_min) { |
170 | | |
171 | | /* Order of token parsers is important. */ |
172 | 5.62k | const parse_token_f parsers[] = { |
173 | 5.62k | &parse_af_token, |
174 | 5.62k | &parse_ip_protocol_token, |
175 | 5.62k | &parse_ip_ports_token, |
176 | 5.62k | }; |
177 | 5.62k | parse_token_f const *parser_ptr = parsers; |
178 | 5.62k | int af = AF_UNSPEC, proto = 0, r; |
179 | 5.62k | uint16_t nr = 0, mn = 0; |
180 | 5.62k | const char *p = ASSERT_PTR(str); |
181 | | |
182 | 5.62k | assert(address_family); |
183 | 5.62k | assert(ip_protocol); |
184 | 5.62k | assert(nr_ports); |
185 | 5.62k | assert(port_min); |
186 | | |
187 | 5.62k | if (isempty(p)) |
188 | 8 | return -EINVAL; |
189 | | |
190 | 6.67k | for (;;) { |
191 | 6.67k | _cleanup_free_ char *token = NULL; |
192 | | |
193 | 6.67k | r = extract_first_word(&p, &token, ":", EXTRACT_DONT_COALESCE_SEPARATORS); |
194 | 6.67k | if (r == 0) |
195 | 513 | break; |
196 | 6.16k | if (r < 0) |
197 | 197 | return r; |
198 | | |
199 | 5.96k | if (isempty(token)) |
200 | 334 | return -EINVAL; |
201 | | |
202 | 16.4k | while (parser_ptr != parsers + ELEMENTSOF(parsers)) { |
203 | 15.0k | r = (*parser_ptr)(token, &af, &proto, &nr, &mn); |
204 | 15.0k | if (r == -ENOMEM) |
205 | 0 | return r; |
206 | | |
207 | 15.0k | ++parser_ptr; |
208 | | /* Continue to next token if parsing succeeded, |
209 | | * otherwise apply next parser to the same token. |
210 | | */ |
211 | 15.0k | if (r >= 0) |
212 | 4.23k | break; |
213 | 15.0k | } |
214 | 5.62k | if (parser_ptr == parsers + ELEMENTSOF(parsers)) |
215 | 4.57k | break; |
216 | 5.62k | } |
217 | | |
218 | | /* Failed to parse a token. */ |
219 | 5.09k | if (r < 0) |
220 | 1.39k | return r; |
221 | | |
222 | | /* Parsers applied successfully, but end of the string not reached. */ |
223 | 3.69k | if (p) |
224 | 212 | return -EINVAL; |
225 | | |
226 | 3.48k | *address_family = af; |
227 | 3.48k | *ip_protocol = proto; |
228 | 3.48k | *nr_ports = nr; |
229 | 3.48k | *port_min = mn; |
230 | | |
231 | 3.48k | return 0; |
232 | 3.69k | } |
233 | | |
234 | | int config_parse_path_or_ignore( |
235 | | const char *unit, |
236 | | const char *filename, |
237 | | unsigned line, |
238 | | const char *section, |
239 | | unsigned section_line, |
240 | | const char *lvalue, |
241 | | int ltype, |
242 | | const char *rvalue, |
243 | | void *data, |
244 | 0 | void *userdata) { |
245 | |
|
246 | 0 | _cleanup_free_ char *n = NULL; |
247 | 0 | bool fatal = ltype; |
248 | 0 | char **s = ASSERT_PTR(data); |
249 | 0 | int r; |
250 | |
|
251 | 0 | assert(filename); |
252 | 0 | assert(lvalue); |
253 | 0 | assert(rvalue); |
254 | |
|
255 | 0 | if (isempty(rvalue)) |
256 | 0 | goto finalize; |
257 | | |
258 | 0 | n = strdup(rvalue); |
259 | 0 | if (!n) |
260 | 0 | return log_oom(); |
261 | | |
262 | 0 | if (streq(n, "-")) |
263 | 0 | goto finalize; |
264 | | |
265 | 0 | r = path_simplify_and_warn(n, PATH_CHECK_ABSOLUTE | (fatal ? PATH_CHECK_FATAL : 0), unit, filename, line, lvalue); |
266 | 0 | if (r < 0) |
267 | 0 | return fatal ? -ENOEXEC : 0; |
268 | | |
269 | 0 | finalize: |
270 | | return free_and_replace(*s, n); |
271 | 0 | } |