/src/libssh/src/config_parser.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * config_parser.c - Common configuration file parser functions |
3 | | * |
4 | | * This file is part of the SSH Library |
5 | | * |
6 | | * Copyright (c) 2009-2013 by Andreas Schneider <asn@cryptomilk.org> |
7 | | * |
8 | | * The SSH Library is free software; you can redistribute it and/or modify |
9 | | * it under the terms of the GNU Lesser General Public License as published by |
10 | | * the Free Software Foundation; either version 2.1 of the License, or (at your |
11 | | * option) any later version. |
12 | | * |
13 | | * The SSH Library is distributed in the hope that it will be useful, but |
14 | | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
15 | | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public |
16 | | * License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU Lesser General Public License |
19 | | * along with the SSH Library; see the file COPYING. If not, write to |
20 | | * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, |
21 | | * MA 02111-1307, USA. |
22 | | */ |
23 | | |
24 | | #include "config.h" |
25 | | |
26 | | #include <ctype.h> |
27 | | #include <stdio.h> |
28 | | #include <string.h> |
29 | | #include <stdlib.h> |
30 | | |
31 | | #include "libssh/config_parser.h" |
32 | | #include "libssh/priv.h" |
33 | | #include "libssh/misc.h" |
34 | | |
35 | | /* Returns the original string after skipping the leading whitespace |
36 | | * until finding LF. |
37 | | * This is useful in case we need to get the rest of the line (for example |
38 | | * external command). |
39 | | */ |
40 | | char *ssh_config_get_cmd(char **str) |
41 | 0 | { |
42 | 0 | register char *c = NULL; |
43 | 0 | char *r = NULL; |
44 | | |
45 | | /* Ignore leading spaces */ |
46 | 0 | for (c = *str; *c; c++) { |
47 | 0 | if (! isblank(*c)) { |
48 | 0 | break; |
49 | 0 | } |
50 | 0 | } |
51 | |
|
52 | 0 | for (r = c; *c; c++) { |
53 | 0 | if (*c == '\n') { |
54 | 0 | *c = '\0'; |
55 | 0 | goto out; |
56 | 0 | } |
57 | 0 | } |
58 | | |
59 | 0 | out: |
60 | 0 | *str = c + 1; |
61 | |
|
62 | 0 | return r; |
63 | 0 | } |
64 | | |
65 | | /* Returns the next token delimited by whitespace or equal sign (=) |
66 | | * respecting the quotes creating separate token (including whitespaces). |
67 | | */ |
68 | | char *ssh_config_get_token(char **str) |
69 | 0 | { |
70 | 0 | register char *c = NULL; |
71 | 0 | bool had_equal = false; |
72 | 0 | char *r = NULL; |
73 | | |
74 | | /* Ignore leading spaces */ |
75 | 0 | for (c = *str; *c; c++) { |
76 | 0 | if (! isblank(*c)) { |
77 | 0 | break; |
78 | 0 | } |
79 | 0 | } |
80 | | |
81 | | /* If we start with quote, return the whole quoted block */ |
82 | 0 | if (*c == '\"') { |
83 | 0 | for (r = ++c; *c; c++) { |
84 | 0 | if (*c == '\"' || *c == '\n') { |
85 | 0 | if (*c == '\"' && r != c && *(c - 1) == '\\') { |
86 | | /* Escaped quote: Move the remaining one char left */ |
87 | 0 | int remaining_len = strlen(c); |
88 | 0 | memmove(c - 1, c, remaining_len); |
89 | 0 | c[remaining_len - 1] = '\0'; |
90 | 0 | continue; |
91 | 0 | } |
92 | 0 | *c = '\0'; |
93 | 0 | c++; |
94 | 0 | break; |
95 | 0 | } |
96 | | /* XXX Unmatched quotes extend to the end of line */ |
97 | 0 | } |
98 | 0 | } else { |
99 | | /* Otherwise terminate on space, equal or newline */ |
100 | 0 | for (r = c; *c; c++) { |
101 | 0 | if (*c == '\0') { |
102 | 0 | goto out; |
103 | 0 | } else if (isblank(*c) || *c == '=' || *c == '\n') { |
104 | 0 | had_equal = (*c == '='); |
105 | 0 | *c = '\0'; |
106 | 0 | c++; |
107 | 0 | break; |
108 | 0 | } |
109 | 0 | } |
110 | 0 | } |
111 | | |
112 | | /* Skip any other remaining whitespace */ |
113 | 0 | while (isblank(*c) || *c == '\n' || (!had_equal && *c == '=')) { |
114 | 0 | if (*c == '=') { |
115 | 0 | had_equal = true; |
116 | 0 | } |
117 | 0 | c++; |
118 | 0 | } |
119 | 0 | out: |
120 | 0 | *str = c; |
121 | 0 | return r; |
122 | 0 | } |
123 | | |
124 | | long ssh_config_get_long(char **str, long notfound) |
125 | 0 | { |
126 | 0 | char *p = NULL, *endp = NULL; |
127 | 0 | long i; |
128 | |
|
129 | 0 | p = ssh_config_get_token(str); |
130 | 0 | if (p && *p) { |
131 | 0 | i = strtol(p, &endp, 10); |
132 | 0 | if (p == endp) { |
133 | 0 | return notfound; |
134 | 0 | } |
135 | 0 | return i; |
136 | 0 | } |
137 | | |
138 | 0 | return notfound; |
139 | 0 | } |
140 | | |
141 | | const char *ssh_config_get_str_tok(char **str, const char *def) |
142 | 0 | { |
143 | 0 | char *p = NULL; |
144 | |
|
145 | 0 | p = ssh_config_get_token(str); |
146 | 0 | if (p && *p) { |
147 | 0 | return p; |
148 | 0 | } |
149 | | |
150 | 0 | return def; |
151 | 0 | } |
152 | | |
153 | | int ssh_config_get_yesno(char **str, int notfound) |
154 | 0 | { |
155 | 0 | const char *p = NULL; |
156 | |
|
157 | 0 | p = ssh_config_get_str_tok(str, NULL); |
158 | 0 | if (p == NULL) { |
159 | 0 | return notfound; |
160 | 0 | } |
161 | | |
162 | 0 | if (strncasecmp(p, "yes", 3) == 0) { |
163 | 0 | return 1; |
164 | 0 | } else if (strncasecmp(p, "no", 2) == 0) { |
165 | 0 | return 0; |
166 | 0 | } |
167 | | |
168 | 0 | return notfound; |
169 | 0 | } |
170 | | |
171 | | int ssh_config_parse_uri(const char *tok, |
172 | | char **username, |
173 | | char **hostname, |
174 | | char **port, |
175 | | bool ignore_port) |
176 | 0 | { |
177 | 0 | char *endp = NULL; |
178 | 0 | long port_n; |
179 | 0 | int rc; |
180 | | |
181 | | /* Sanitize inputs */ |
182 | 0 | if (username != NULL) { |
183 | 0 | *username = NULL; |
184 | 0 | } |
185 | 0 | if (hostname != NULL) { |
186 | 0 | *hostname = NULL; |
187 | 0 | } |
188 | 0 | if (port != NULL) { |
189 | 0 | *port = NULL; |
190 | 0 | } |
191 | | |
192 | | /* Username part (optional) */ |
193 | 0 | endp = strrchr(tok, '@'); |
194 | 0 | if (endp != NULL) { |
195 | | /* Zero-length username is not valid */ |
196 | 0 | if (tok == endp) { |
197 | 0 | goto error; |
198 | 0 | } |
199 | 0 | if (username != NULL) { |
200 | 0 | *username = strndup(tok, endp - tok); |
201 | 0 | if (*username == NULL) { |
202 | 0 | goto error; |
203 | 0 | } |
204 | 0 | rc = ssh_check_username_syntax(*username); |
205 | 0 | if (rc != SSH_OK) { |
206 | 0 | goto error; |
207 | 0 | } |
208 | 0 | } |
209 | 0 | tok = endp + 1; |
210 | | /* If there is second @ character, this does not look like our URI */ |
211 | 0 | endp = strchr(tok, '@'); |
212 | 0 | if (endp != NULL) { |
213 | 0 | goto error; |
214 | 0 | } |
215 | 0 | } |
216 | | |
217 | | /* Hostname */ |
218 | 0 | if (*tok == '[') { |
219 | | /* IPv6 address is enclosed with square brackets */ |
220 | 0 | tok++; |
221 | 0 | endp = strchr(tok, ']'); |
222 | 0 | if (endp == NULL) { |
223 | 0 | goto error; |
224 | 0 | } |
225 | 0 | } else if (!ignore_port) { |
226 | | /* Hostnames or aliases expand to the last colon (if port is requested) |
227 | | * or to the end */ |
228 | 0 | endp = strrchr(tok, ':'); |
229 | 0 | if (endp == NULL) { |
230 | 0 | endp = strchr(tok, '\0'); |
231 | 0 | } |
232 | 0 | } else { |
233 | | /* If no port is requested, expand to the end of line |
234 | | * (to accommodate the IPv6 addresses) */ |
235 | 0 | endp = strchr(tok, '\0'); |
236 | 0 | } |
237 | 0 | if (tok == endp) { |
238 | | /* Zero-length hostnames are not valid */ |
239 | 0 | goto error; |
240 | 0 | } |
241 | 0 | if (hostname != NULL) { |
242 | 0 | *hostname = strndup(tok, endp - tok); |
243 | 0 | if (*hostname == NULL) { |
244 | 0 | goto error; |
245 | 0 | } |
246 | | /* if not an ip, check syntax */ |
247 | 0 | rc = ssh_is_ipaddr(*hostname); |
248 | 0 | if (rc == 0) { |
249 | 0 | rc = ssh_check_hostname_syntax(*hostname); |
250 | 0 | if (rc != SSH_OK) { |
251 | 0 | goto error; |
252 | 0 | } |
253 | 0 | } |
254 | 0 | } |
255 | | /* Skip also the closing bracket */ |
256 | 0 | if (*endp == ']') { |
257 | 0 | endp++; |
258 | 0 | } |
259 | | |
260 | | /* Port (optional) */ |
261 | 0 | if (*endp != '\0') { |
262 | 0 | char *port_end = NULL; |
263 | | |
264 | | /* Verify the port is valid positive number */ |
265 | 0 | port_n = strtol(endp + 1, &port_end, 10); |
266 | 0 | if (port_n < 1 || *port_end != '\0') { |
267 | 0 | SSH_LOG(SSH_LOG_TRACE, "Failed to parse port number." |
268 | 0 | " The value '%ld' is invalid or there are some" |
269 | 0 | " trailing characters: '%s'", port_n, port_end); |
270 | 0 | goto error; |
271 | 0 | } |
272 | 0 | if (port != NULL) { |
273 | 0 | *port = strdup(endp + 1); |
274 | 0 | if (*port == NULL) { |
275 | 0 | goto error; |
276 | 0 | } |
277 | 0 | } |
278 | 0 | } |
279 | | |
280 | 0 | return SSH_OK; |
281 | | |
282 | 0 | error: |
283 | 0 | if (username != NULL) { |
284 | 0 | SAFE_FREE(*username); |
285 | 0 | } |
286 | 0 | if (hostname != NULL) { |
287 | 0 | SAFE_FREE(*hostname); |
288 | 0 | } |
289 | 0 | if (port != NULL) { |
290 | 0 | SAFE_FREE(*port); |
291 | 0 | } |
292 | 0 | return SSH_ERROR; |
293 | 0 | } |