Coverage Report

Created: 2025-08-29 06:06

/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
}