Coverage Report

Created: 2023-03-26 07:40

/src/libssh/src/config.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * config.c - parse the ssh config file
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
#ifdef HAVE_GLOB_H
31
# include <glob.h>
32
#endif
33
#include <stdbool.h>
34
#include <limits.h>
35
#ifndef _WIN32
36
# include <sys/types.h>
37
# include <sys/stat.h>
38
# include <fcntl.h>
39
# include <errno.h>
40
# include <signal.h>
41
# include <sys/wait.h>
42
#endif
43
44
#include "libssh/config_parser.h"
45
#include "libssh/config.h"
46
#include "libssh/priv.h"
47
#include "libssh/session.h"
48
#include "libssh/misc.h"
49
#include "libssh/options.h"
50
51
#ifndef MAX_LINE_SIZE
52
1.16k
#define MAX_LINE_SIZE 1024
53
#endif
54
55
struct ssh_config_keyword_table_s {
56
  const char *name;
57
  enum ssh_config_opcode_e opcode;
58
};
59
60
static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
61
  { "host", SOC_HOST },
62
  { "match", SOC_MATCH },
63
  { "hostname", SOC_HOSTNAME },
64
  { "port", SOC_PORT },
65
  { "user", SOC_USERNAME },
66
  { "identityfile", SOC_IDENTITY },
67
  { "ciphers", SOC_CIPHERS },
68
  { "macs", SOC_MACS },
69
  { "compression", SOC_COMPRESSION },
70
  { "connecttimeout", SOC_TIMEOUT },
71
  { "stricthostkeychecking", SOC_STRICTHOSTKEYCHECK },
72
  { "userknownhostsfile", SOC_KNOWNHOSTS },
73
  { "proxycommand", SOC_PROXYCOMMAND },
74
  { "gssapiserveridentity", SOC_GSSAPISERVERIDENTITY },
75
  { "gssapiclientidentity", SOC_GSSAPICLIENTIDENTITY },
76
  { "gssapidelegatecredentials", SOC_GSSAPIDELEGATECREDENTIALS },
77
  { "include", SOC_INCLUDE },
78
  { "bindaddress", SOC_BINDADDRESS},
79
  { "globalknownhostsfile", SOC_GLOBALKNOWNHOSTSFILE},
80
  { "loglevel", SOC_LOGLEVEL},
81
  { "hostkeyalgorithms", SOC_HOSTKEYALGORITHMS},
82
  { "kexalgorithms", SOC_KEXALGORITHMS},
83
  { "gssapiauthentication", SOC_GSSAPIAUTHENTICATION},
84
  { "kbdinteractiveauthentication", SOC_KBDINTERACTIVEAUTHENTICATION},
85
  { "passwordauthentication", SOC_PASSWORDAUTHENTICATION},
86
  { "pubkeyauthentication", SOC_PUBKEYAUTHENTICATION},
87
  { "addkeystoagent", SOC_UNSUPPORTED},
88
  { "addressfamily", SOC_UNSUPPORTED},
89
  { "batchmode", SOC_UNSUPPORTED},
90
  { "canonicaldomains", SOC_UNSUPPORTED},
91
  { "canonicalizefallbacklocal", SOC_UNSUPPORTED},
92
  { "canonicalizehostname", SOC_UNSUPPORTED},
93
  { "canonicalizemaxdots", SOC_UNSUPPORTED},
94
  { "canonicalizepermittedcnames", SOC_UNSUPPORTED},
95
  { "certificatefile", SOC_UNSUPPORTED},
96
  { "kbdinteractiveauthentication", SOC_UNSUPPORTED},
97
  { "checkhostip", SOC_UNSUPPORTED},
98
  { "connectionattempts", SOC_UNSUPPORTED},
99
  { "enablesshkeysign", SOC_UNSUPPORTED},
100
  { "fingerprinthash", SOC_UNSUPPORTED},
101
  { "forwardagent", SOC_UNSUPPORTED},
102
  { "hashknownhosts", SOC_UNSUPPORTED},
103
  { "hostbasedauthentication", SOC_UNSUPPORTED},
104
  { "hostbasedacceptedalgorithms", SOC_UNSUPPORTED},
105
  { "hostkeyalias", SOC_UNSUPPORTED},
106
  { "identitiesonly", SOC_IDENTITIESONLY},
107
  { "identityagent", SOC_IDENTITYAGENT},
108
  { "ipqos", SOC_UNSUPPORTED},
109
  { "kbdinteractivedevices", SOC_UNSUPPORTED},
110
  { "nohostauthenticationforlocalhost", SOC_UNSUPPORTED},
111
  { "numberofpasswordprompts", SOC_UNSUPPORTED},
112
  { "pkcs11provider", SOC_UNSUPPORTED},
113
  { "preferredauthentications", SOC_UNSUPPORTED},
114
  { "proxyjump", SOC_PROXYJUMP},
115
  { "proxyusefdpass", SOC_UNSUPPORTED},
116
  { "pubkeyacceptedalgorithms", SOC_PUBKEYACCEPTEDKEYTYPES},
117
  { "rekeylimit", SOC_REKEYLIMIT},
118
  { "remotecommand", SOC_UNSUPPORTED},
119
  { "revokedhostkeys", SOC_UNSUPPORTED},
120
  { "serveralivecountmax", SOC_UNSUPPORTED},
121
  { "serveraliveinterval", SOC_UNSUPPORTED},
122
  { "streamlocalbindmask", SOC_UNSUPPORTED},
123
  { "streamlocalbindunlink", SOC_UNSUPPORTED},
124
  { "syslogfacility", SOC_UNSUPPORTED},
125
  { "tcpkeepalive", SOC_UNSUPPORTED},
126
  { "updatehostkeys", SOC_UNSUPPORTED},
127
  { "verifyhostkeydns", SOC_UNSUPPORTED},
128
  { "visualhostkey", SOC_UNSUPPORTED},
129
  { "clearallforwardings", SOC_NA},
130
  { "controlmaster", SOC_NA},
131
  { "controlpersist", SOC_NA},
132
  { "controlpath", SOC_NA},
133
  { "dynamicforward", SOC_NA},
134
  { "escapechar", SOC_NA},
135
  { "exitonforwardfailure", SOC_NA},
136
  { "forwardx11", SOC_NA},
137
  { "forwardx11timeout", SOC_NA},
138
  { "forwardx11trusted", SOC_NA},
139
  { "gatewayports", SOC_NA},
140
  { "ignoreunknown", SOC_NA},
141
  { "localcommand", SOC_NA},
142
  { "localforward", SOC_NA},
143
  { "permitlocalcommand", SOC_NA},
144
  { "remoteforward", SOC_NA},
145
  { "requesttty", SOC_NA},
146
  { "sendenv", SOC_NA},
147
  { "tunnel", SOC_NA},
148
  { "tunneldevice", SOC_NA},
149
  { "xauthlocation", SOC_NA},
150
  { "pubkeyacceptedkeytypes", SOC_PUBKEYACCEPTEDKEYTYPES},
151
  { NULL, SOC_UNKNOWN }
152
};
153
154
enum ssh_config_match_e {
155
    MATCH_UNKNOWN = -1,
156
    MATCH_ALL,
157
    MATCH_FINAL,
158
    MATCH_CANONICAL,
159
    MATCH_EXEC,
160
    MATCH_HOST,
161
    MATCH_ORIGINALHOST,
162
    MATCH_USER,
163
    MATCH_LOCALUSER
164
};
165
166
struct ssh_config_match_keyword_table_s {
167
    const char *name;
168
    enum ssh_config_match_e opcode;
169
};
170
171
static struct ssh_config_match_keyword_table_s ssh_config_match_keyword_table[] = {
172
    { "all", MATCH_ALL },
173
    { "canonical", MATCH_CANONICAL },
174
    { "final", MATCH_FINAL },
175
    { "exec", MATCH_EXEC },
176
    { "host", MATCH_HOST },
177
    { "originalhost", MATCH_ORIGINALHOST },
178
    { "user", MATCH_USER },
179
    { "localuser", MATCH_LOCALUSER },
180
    { NULL, MATCH_UNKNOWN },
181
};
182
183
static int ssh_config_parse_line(ssh_session session, const char *line,
184
    unsigned int count, int *parsing, unsigned int depth, bool global);
185
186
824
static enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword) {
187
824
  int i;
188
189
58.2k
  for (i = 0; ssh_config_keyword_table[i].name != NULL; i++) {
190
57.6k
    if (strcasecmp(keyword, ssh_config_keyword_table[i].name) == 0) {
191
232
      return ssh_config_keyword_table[i].opcode;
192
232
    }
193
57.6k
  }
194
195
592
  return SOC_UNKNOWN;
196
824
}
197
198
0
#define LIBSSH_CONF_MAX_DEPTH 16
199
static void
200
local_parse_file(ssh_session session,
201
                 const char *filename,
202
                 int *parsing,
203
                 unsigned int depth,
204
                 bool global)
205
0
{
206
0
    FILE *f;
207
0
    char line[MAX_LINE_SIZE] = {0};
208
0
    unsigned int count = 0;
209
0
    int rv;
210
211
0
    if (depth > LIBSSH_CONF_MAX_DEPTH) {
212
0
        ssh_set_error(session, SSH_FATAL,
213
0
                      "ERROR - Too many levels of configuration includes "
214
0
                      "when processing file '%s'", filename);
215
0
        return;
216
0
    }
217
218
0
    f = fopen(filename, "r");
219
0
    if (f == NULL) {
220
0
        SSH_LOG(SSH_LOG_RARE, "Cannot find file %s to load",
221
0
                filename);
222
0
        return;
223
0
    }
224
225
0
    SSH_LOG(SSH_LOG_PACKET, "Reading additional configuration data from %s", filename);
226
0
    while (fgets(line, sizeof(line), f)) {
227
0
        count++;
228
0
        rv = ssh_config_parse_line(session, line, count, parsing, depth, global);
229
0
        if (rv < 0) {
230
0
            fclose(f);
231
0
            return;
232
0
        }
233
0
    }
234
235
0
    fclose(f);
236
0
    return;
237
0
}
238
239
#if defined(HAVE_GLOB) && defined(HAVE_GLOB_GL_FLAGS_MEMBER)
240
static void local_parse_glob(ssh_session session,
241
                             const char *fileglob,
242
                             int *parsing,
243
                             unsigned int depth,
244
                             bool global)
245
0
{
246
0
    glob_t globbuf = {
247
0
        .gl_flags = 0,
248
0
    };
249
0
    int rt;
250
0
    size_t i;
251
252
0
    rt = glob(fileglob, GLOB_TILDE, NULL, &globbuf);
253
0
    if (rt == GLOB_NOMATCH) {
254
0
        globfree(&globbuf);
255
0
        return;
256
0
    } else if (rt != 0) {
257
0
        SSH_LOG(SSH_LOG_RARE, "Glob error: %s",
258
0
                fileglob);
259
0
        globfree(&globbuf);
260
0
        return;
261
0
    }
262
263
0
    for (i = 0; i < globbuf.gl_pathc; i++) {
264
0
        local_parse_file(session, globbuf.gl_pathv[i], parsing, depth, global);
265
0
    }
266
267
0
    globfree(&globbuf);
268
0
}
269
#endif /* HAVE_GLOB HAVE_GLOB_GL_FLAGS_MEMBER */
270
271
static enum ssh_config_match_e
272
ssh_config_get_match_opcode(const char *keyword)
273
80
{
274
80
    size_t i;
275
276
314
    for (i = 0; ssh_config_match_keyword_table[i].name != NULL; i++) {
277
314
        if (strcasecmp(keyword, ssh_config_match_keyword_table[i].name) == 0) {
278
80
            return ssh_config_match_keyword_table[i].opcode;
279
80
        }
280
314
    }
281
282
0
    return MATCH_UNKNOWN;
283
80
}
284
285
static int
286
ssh_config_match(char *value, const char *pattern, bool negate)
287
0
{
288
0
    int ok, result = 0;
289
290
0
    ok = match_pattern_list(value, pattern, strlen(pattern), 0);
291
0
    if (ok <= 0 && negate == true) {
292
0
        result = 1;
293
0
    } else if (ok > 0 && negate == false) {
294
0
        result = 1;
295
0
    }
296
0
    SSH_LOG(SSH_LOG_TRACE, "%s '%s' against pattern '%s'%s (ok=%d)",
297
0
            result == 1 ? "Matched" : "Not matched", value, pattern,
298
0
            negate == true ? " (negated)" : "", ok);
299
0
    return result;
300
0
}
301
302
#ifdef _WIN32
303
static int
304
ssh_match_exec(ssh_session session, const char *command, bool negate)
305
{
306
    (void) session;
307
    (void) command;
308
    (void) negate;
309
310
    SSH_LOG(SSH_LOG_TRACE, "Unsupported 'exec' command on Windows '%s'",
311
            command);
312
    return 0;
313
}
314
#else /* _WIN32 */
315
316
static int
317
ssh_exec_shell(char *cmd)
318
7
{
319
7
    char *shell = NULL;
320
7
    pid_t pid;
321
7
    int status, devnull, rc;
322
7
    char err_msg[SSH_ERRNO_MSG_MAX] = {0};
323
324
7
    shell = getenv("SHELL");
325
7
    if (shell == NULL || shell[0] == '\0') {
326
7
        shell = (char *)"/bin/sh";
327
7
    }
328
329
7
    rc = access(shell, X_OK);
330
7
    if (rc != 0) {
331
0
        SSH_LOG(SSH_LOG_WARN, "The shell '%s' is not executable", shell);
332
0
        return -1;
333
0
    }
334
335
    /* Need this to redirect subprocess stdin/out */
336
7
    devnull = open("/dev/null", O_RDWR);
337
7
    if (devnull == -1) {
338
0
        SSH_LOG(SSH_LOG_WARN, "Failed to open(/dev/null): %s",
339
0
                ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
340
0
        return -1;
341
0
    }
342
343
7
    SSH_LOG(SSH_LOG_DEBUG, "Running command '%s'", cmd);
344
7
    pid = fork();
345
7
    if (pid == 0) { /* Child */
346
0
        char *argv[4];
347
348
        /* Redirect child stdin and stdout. Leave stderr */
349
0
        rc = dup2(devnull, STDIN_FILENO);
350
0
        if (rc == -1) {
351
0
            SSH_LOG(SSH_LOG_WARN, "dup2: %s",
352
0
                    ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
353
0
            exit(1);
354
0
        }
355
0
        rc = dup2(devnull, STDOUT_FILENO);
356
0
        if (rc == -1) {
357
0
            SSH_LOG(SSH_LOG_WARN, "dup2: %s",
358
0
                    ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
359
0
            exit(1);
360
0
        }
361
0
        if (devnull > STDERR_FILENO) {
362
0
            close(devnull);
363
0
        }
364
365
0
        argv[0] = shell;
366
0
        argv[1] = (char *) "-c";
367
0
        argv[2] = strdup(cmd);
368
0
        argv[3] = NULL;
369
370
0
        rc = execv(argv[0], argv);
371
0
        if (rc == -1) {
372
0
            SSH_LOG(SSH_LOG_WARN, "Failed to execute command '%s': %s", cmd,
373
0
                    ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
374
            /* Die with signal to make this error apparent to parent. */
375
0
            signal(SIGTERM, SIG_DFL);
376
0
            kill(getpid(), SIGTERM);
377
0
            _exit(1);
378
0
        }
379
0
    }
380
381
    /* Parent */
382
7
    close(devnull);
383
7
    if (pid == -1) { /* Error */
384
0
        SSH_LOG(SSH_LOG_WARN, "Failed to fork child: %s",
385
0
                ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
386
0
        return -1;
387
388
0
    }
389
390
7
    while (waitpid(pid, &status, 0) == -1) {
391
0
        if (errno != EINTR) {
392
0
            SSH_LOG(SSH_LOG_WARN, "waitpid failed: %s",
393
0
                    ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
394
0
            return -1;
395
0
        }
396
0
    }
397
7
    if (!WIFEXITED(status)) {
398
0
        SSH_LOG(SSH_LOG_WARN, "Command %s exited abnormally", cmd);
399
0
        return -1;
400
0
    }
401
7
    SSH_LOG(SSH_LOG_TRACE, "Command '%s' returned %d", cmd, WEXITSTATUS(status));
402
7
    return WEXITSTATUS(status);
403
7
}
404
405
static int
406
ssh_match_exec(ssh_session session, const char *command, bool negate)
407
75
{
408
75
    int rv, result = 0;
409
75
    char *cmd = NULL;
410
411
    /* TODO There should be more supported expansions */
412
75
    cmd = ssh_path_expand_escape(session, command);
413
75
    if (cmd == NULL) {
414
68
        return 0;
415
68
    }
416
7
    rv = ssh_exec_shell(cmd);
417
7
    if (rv > 0 && negate == true) {
418
0
        result = 1;
419
7
    } else if (rv == 0 && negate == false) {
420
0
        result = 1;
421
0
    }
422
7
    SSH_LOG(SSH_LOG_TRACE, "%s 'exec' command '%s'%s (rv=%d)",
423
7
            result == 1 ? "Matched" : "Not matched", cmd,
424
7
            negate == true ? " (negated)" : "", rv);
425
7
    free(cmd);
426
7
    return result;
427
75
}
428
#endif  /* _WIN32 */
429
430
/* @brief: Parse the ProxyJump configuration line and if parsing,
431
 * stores the result in the configuration option
432
 */
433
static int
434
ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
435
6
{
436
6
    char *c = NULL, *cp = NULL, *endp = NULL;
437
6
    char *username = NULL;
438
6
    char *hostname = NULL;
439
6
    char *port = NULL;
440
6
    char *next = NULL;
441
6
    int cmp, rv = SSH_ERROR;
442
6
    bool parse_entry = do_parsing;
443
444
    /* Special value none disables the proxy */
445
6
    cmp = strcasecmp(s, "none");
446
6
    if (cmp == 0 && do_parsing) {
447
0
        ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, s);
448
0
        return SSH_OK;
449
0
    }
450
451
    /* This is comma-separated list of [user@]host[:port] entries */
452
6
    c = strdup(s);
453
6
    if (c == NULL) {
454
0
        ssh_set_error_oom(session);
455
0
        return SSH_ERROR;
456
0
    }
457
458
6
    cp = c;
459
6
    do {
460
6
        endp = strchr(cp, ',');
461
6
        if (endp != NULL) {
462
            /* Split out the token */
463
0
            *endp = '\0';
464
0
        }
465
6
        if (parse_entry) {
466
            /* We actually care only about the first item */
467
0
            rv = ssh_config_parse_uri(cp, &username, &hostname, &port);
468
            /* The rest of the list needs to be passed on */
469
0
            if (endp != NULL) {
470
0
                next = strdup(endp + 1);
471
0
                if (next == NULL) {
472
0
                    ssh_set_error_oom(session);
473
0
                    rv = SSH_ERROR;
474
0
                }
475
0
            }
476
6
        } else {
477
            /* The rest is just sanity-checked to avoid failures later */
478
6
            rv = ssh_config_parse_uri(cp, NULL, NULL, NULL);
479
6
        }
480
6
        if (rv != SSH_OK) {
481
0
            goto out;
482
0
        }
483
6
        parse_entry = 0;
484
6
        if (endp != NULL) {
485
0
            cp = endp + 1;
486
6
        } else {
487
6
            cp = NULL; /* end */
488
6
        }
489
6
    } while (cp != NULL);
490
491
6
    if (hostname != NULL && do_parsing) {
492
0
        char com[512] = {0};
493
494
0
        rv = snprintf(com, sizeof(com), "ssh%s%s%s%s%s%s -W '[%%h]:%%p' %s",
495
0
                      username ? " -l " : "",
496
0
                      username ? username : "",
497
0
                      port ? " -p " : "",
498
0
                      port ? port : "",
499
0
                      next ? " -J " : "",
500
0
                      next ? next : "",
501
0
                      hostname);
502
0
        if (rv < 0 || rv >= (int)sizeof(com)) {
503
0
            SSH_LOG(SSH_LOG_TRACE, "Too long ProxyJump configuration line");
504
0
            rv = SSH_ERROR;
505
0
            goto out;
506
0
        }
507
0
        ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, com);
508
0
    }
509
6
    rv = SSH_OK;
510
511
6
out:
512
6
    SAFE_FREE(username);
513
6
    SAFE_FREE(hostname);
514
6
    SAFE_FREE(port);
515
6
    SAFE_FREE(next);
516
6
    SAFE_FREE(c);
517
6
    return rv;
518
6
}
519
520
static char *
521
ssh_config_make_absolute(ssh_session session,
522
                         const char *path,
523
                         bool global)
524
0
{
525
0
    size_t outlen = 0;
526
0
    char *out = NULL;
527
0
    int rv;
528
529
    /* Looks like absolute path */
530
0
    if (path[0] == '/') {
531
0
        return strdup(path);
532
0
    }
533
534
    /* relative path */
535
0
    if (global) {
536
        /* Parsing global config */
537
0
        outlen = strlen(path) + strlen("/etc/ssh/") + 1;
538
0
        out = malloc(outlen);
539
0
        if (out == NULL) {
540
0
            ssh_set_error_oom(session);
541
0
            return NULL;
542
0
        }
543
0
        rv = snprintf(out, outlen, "/etc/ssh/%s", path);
544
0
        if (rv < 1) {
545
0
            free(out);
546
0
            return NULL;
547
0
        }
548
0
        return out;
549
0
    }
550
551
    /* paths starting with tilde are already absolute */
552
0
    if (path[0] == '~') {
553
0
        return ssh_path_expand_tilde(path);
554
0
    }
555
556
    /* Parsing user config relative to home directory (generally ~/.ssh) */
557
0
    if (session->opts.sshdir == NULL) {
558
0
        ssh_set_error_invalid(session);
559
0
        return NULL;
560
0
    }
561
0
    outlen = strlen(path) + strlen(session->opts.sshdir) + 1 + 1;
562
0
    out = malloc(outlen);
563
0
    if (out == NULL) {
564
0
        ssh_set_error_oom(session);
565
0
        return NULL;
566
0
    }
567
0
    rv = snprintf(out, outlen, "%s/%s", session->opts.sshdir, path);
568
0
    if (rv < 1) {
569
0
        free(out);
570
0
        return NULL;
571
0
    }
572
0
    return out;
573
0
}
574
575
static int
576
ssh_config_parse_line(ssh_session session,
577
                      const char *line,
578
                      unsigned int count,
579
                      int *parsing,
580
                      unsigned int depth,
581
                      bool global)
582
1.15k
{
583
1.15k
  enum ssh_config_opcode_e opcode;
584
1.15k
  const char *p = NULL, *p2 = NULL;
585
1.15k
  char *s = NULL, *x = NULL;
586
1.15k
  char *keyword = NULL;
587
1.15k
  char *lowerhost = NULL;
588
1.15k
  size_t len;
589
1.15k
  int i, rv;
590
1.15k
  uint8_t *seen = session->opts.options_seen;
591
1.15k
  long l;
592
1.15k
  int64_t ll;
593
594
  /* Ignore empty lines */
595
1.15k
  if (line == NULL || *line == '\0') {
596
327
    return 0;
597
327
  }
598
599
828
  x = s = strdup(line);
600
828
  if (s == NULL) {
601
0
    ssh_set_error_oom(session);
602
0
    return -1;
603
0
  }
604
605
  /* Remove trailing spaces */
606
843
  for (len = strlen(s) - 1; len > 0; len--) {
607
787
    if (! isspace(s[len])) {
608
772
      break;
609
772
    }
610
15
    s[len] = '\0';
611
15
  }
612
613
828
  keyword = ssh_config_get_token(&s);
614
828
  if (keyword == NULL || *keyword == '#' ||
615
828
      *keyword == '\0' || *keyword == '\n') {
616
4
    SAFE_FREE(x);
617
4
    return 0;
618
4
  }
619
620
824
  opcode = ssh_config_get_opcode(keyword);
621
824
  if (*parsing == 1 &&
622
824
      opcode != SOC_HOST &&
623
824
      opcode != SOC_MATCH &&
624
824
      opcode != SOC_INCLUDE &&
625
824
      opcode != SOC_IDENTITY &&
626
824
      opcode > SOC_UNSUPPORTED &&
627
824
      opcode < SOC_MAX) { /* Ignore all unknown types here */
628
      /* Skip all the options that were already applied */
629
7
      if (seen[opcode] != 0) {
630
3
          SAFE_FREE(x);
631
3
          return 0;
632
3
      }
633
4
      seen[opcode] = 1;
634
4
  }
635
636
821
  switch (opcode) {
637
4
    case SOC_INCLUDE: /* recursive include of other files */
638
639
4
      p = ssh_config_get_str_tok(&s, NULL);
640
4
      if (p && *parsing) {
641
0
        char *path = ssh_config_make_absolute(session, p, global);
642
0
        if (path == NULL) {
643
0
          SSH_LOG(SSH_LOG_WARN, "line %d: Failed to allocate memory "
644
0
                  "for the include path expansion", count);
645
0
          SAFE_FREE(x);
646
0
          return -1;
647
0
        }
648
0
#if defined(HAVE_GLOB) && defined(HAVE_GLOB_GL_FLAGS_MEMBER)
649
0
        local_parse_glob(session, path, parsing, depth + 1, global);
650
#else
651
        local_parse_file(session, path, parsing, depth + 1, global);
652
#endif /* HAVE_GLOB */
653
0
        free(path);
654
0
      }
655
4
      break;
656
657
75
    case SOC_MATCH: {
658
75
        bool negate;
659
75
        int result = 1;
660
75
        size_t args = 0;
661
75
        enum ssh_config_match_e opt;
662
75
        char *localuser = NULL;
663
664
75
        *parsing = 0;
665
155
        do {
666
155
            p = p2 = ssh_config_get_str_tok(&s, NULL);
667
155
            if (p == NULL || p[0] == '\0') {
668
75
                break;
669
75
            }
670
80
            args++;
671
80
            SSH_LOG(SSH_LOG_DEBUG, "line %d: Processing Match keyword '%s'",
672
80
                    count, p);
673
674
            /* If the option is prefixed with ! the result should be negated */
675
80
            negate = false;
676
80
            if (p[0] == '!') {
677
0
                negate = true;
678
0
                p++;
679
0
            }
680
681
80
            opt = ssh_config_get_match_opcode(p);
682
80
            switch (opt) {
683
0
            case MATCH_ALL:
684
0
                p = ssh_config_get_str_tok(&s, NULL);
685
0
                if (args <= 2 && (p == NULL || p[0] == '\0')) {
686
                    /* The first or second, but last argument. The "all" keyword
687
                     * can be prefixed with either "final" or "canonical"
688
                     * keywords which do not have any effect here. */
689
0
                    if (negate == true) {
690
0
                        result = 0;
691
0
                    }
692
0
                    break;
693
0
                }
694
695
0
                ssh_set_error(session, SSH_FATAL,
696
0
                              "line %d: ERROR - Match all cannot be combined with "
697
0
                              "other Match attributes", count);
698
0
                SAFE_FREE(x);
699
0
                return -1;
700
701
0
            case MATCH_FINAL:
702
3
            case MATCH_CANONICAL:
703
3
                SSH_LOG(SSH_LOG_DEBUG,
704
3
                        "line %d: Unsupported Match keyword '%s', skipping",
705
3
                        count,
706
3
                        p);
707
                /* Not set any result here -- the result is dependent on the
708
                 * following matches after this keyword */
709
3
                break;
710
711
77
            case MATCH_EXEC:
712
                /* Skip one argument (including in quotes) */
713
77
                p = ssh_config_get_token(&s);
714
77
                if (p == NULL || p[0] == '\0') {
715
0
                    SSH_LOG(SSH_LOG_TRACE, "line %d: Match keyword "
716
0
                            "'%s' requires argument", count, p2);
717
0
                    SAFE_FREE(x);
718
0
                    return -1;
719
0
                }
720
77
                if (result != 1) {
721
2
                    SSH_LOG(SSH_LOG_DEBUG, "line %d: Skipped match exec "
722
2
                            "'%s' as previous conditions already failed.",
723
2
                            count, p2);
724
2
                    continue;
725
2
                }
726
75
                result &= ssh_match_exec(session, p, negate);
727
75
                args++;
728
75
                break;
729
730
0
            case MATCH_LOCALUSER:
731
                /* Here we match only one argument */
732
0
                p = ssh_config_get_str_tok(&s, NULL);
733
0
                if (p == NULL || p[0] == '\0') {
734
0
                    ssh_set_error(session, SSH_FATAL,
735
0
                                  "line %d: ERROR - Match user keyword "
736
0
                                  "requires argument", count);
737
0
                    SAFE_FREE(x);
738
0
                    return -1;
739
0
                }
740
0
                localuser = ssh_get_local_username();
741
0
                if (localuser == NULL) {
742
0
                    SSH_LOG(SSH_LOG_TRACE, "line %d: Can not get local username "
743
0
                            "for conditional matching.", count);
744
0
                    SAFE_FREE(x);
745
0
                    return -1;
746
0
                }
747
0
                result &= ssh_config_match(localuser, p, negate);
748
0
                SAFE_FREE(localuser);
749
0
                args++;
750
0
                break;
751
752
0
            case MATCH_ORIGINALHOST:
753
                /* Skip one argument */
754
0
                p = ssh_config_get_str_tok(&s, NULL);
755
0
                if (p == NULL || p[0] == '\0') {
756
0
                    SSH_LOG(SSH_LOG_TRACE, "line %d: Match keyword "
757
0
                            "'%s' requires argument", count, p2);
758
0
                    SAFE_FREE(x);
759
0
                    return -1;
760
0
                }
761
0
                args++;
762
0
                SSH_LOG(SSH_LOG_TRACE,
763
0
                        "line %d: Unsupported Match keyword '%s', ignoring",
764
0
                        count,
765
0
                        p2);
766
0
                result = 0;
767
0
                break;
768
769
0
            case MATCH_HOST:
770
                /* Here we match only one argument */
771
0
                p = ssh_config_get_str_tok(&s, NULL);
772
0
                if (p == NULL || p[0] == '\0') {
773
0
                    ssh_set_error(session, SSH_FATAL,
774
0
                                  "line %d: ERROR - Match host keyword "
775
0
                                  "requires argument", count);
776
0
                    SAFE_FREE(x);
777
0
                    return -1;
778
0
                }
779
0
                result &= ssh_config_match(session->opts.host, p, negate);
780
0
                args++;
781
0
                break;
782
783
0
            case MATCH_USER:
784
                /* Here we match only one argument */
785
0
                p = ssh_config_get_str_tok(&s, NULL);
786
0
                if (p == NULL || p[0] == '\0') {
787
0
                    ssh_set_error(session, SSH_FATAL,
788
0
                                  "line %d: ERROR - Match user keyword "
789
0
                                  "requires argument", count);
790
0
                    SAFE_FREE(x);
791
0
                    return -1;
792
0
                }
793
0
                result &= ssh_config_match(session->opts.username, p, negate);
794
0
                args++;
795
0
                break;
796
797
0
            case MATCH_UNKNOWN:
798
0
            default:
799
0
                ssh_set_error(session, SSH_FATAL,
800
0
                              "ERROR - Unknown argument '%s' for Match keyword", p);
801
0
                SAFE_FREE(x);
802
0
                return -1;
803
80
            }
804
80
        } while (p != NULL && p[0] != '\0');
805
75
        if (args == 0) {
806
0
            ssh_set_error(session, SSH_FATAL,
807
0
                          "ERROR - Match keyword requires an argument");
808
0
            SAFE_FREE(x);
809
0
            return -1;
810
0
        }
811
75
        *parsing = result;
812
75
        break;
813
75
    }
814
14
    case SOC_HOST: {
815
14
        int ok = 0, result = -1;
816
817
14
        *parsing = 0;
818
14
        lowerhost = (session->opts.host) ? ssh_lowercase(session->opts.host) : NULL;
819
14
        for (p = ssh_config_get_str_tok(&s, NULL);
820
83
             p != NULL && p[0] != '\0';
821
69
             p = ssh_config_get_str_tok(&s, NULL)) {
822
69
             if (ok >= 0) {
823
69
               ok = match_hostname(lowerhost, p, strlen(p));
824
69
               if (result == -1 && ok < 0) {
825
0
                   result = 0;
826
69
               } else if (result == -1 && ok > 0) {
827
3
                   result = 1;
828
3
               }
829
69
            }
830
69
        }
831
14
        SAFE_FREE(lowerhost);
832
14
        if (result != -1) {
833
3
            *parsing = result;
834
3
        }
835
14
        break;
836
75
    }
837
0
    case SOC_HOSTNAME:
838
0
      p = ssh_config_get_str_tok(&s, NULL);
839
0
      if (p && *parsing) {
840
0
        char *z = ssh_path_expand_escape(session, p);
841
0
        if (z == NULL) {
842
0
            z = strdup(p);
843
0
        }
844
0
        ssh_options_set(session, SSH_OPTIONS_HOST, z);
845
0
        free(z);
846
0
      }
847
0
      break;
848
34
    case SOC_PORT:
849
34
        p = ssh_config_get_str_tok(&s, NULL);
850
34
        if (p && *parsing) {
851
1
            ssh_options_set(session, SSH_OPTIONS_PORT_STR, p);
852
1
        }
853
34
        break;
854
0
    case SOC_USERNAME:
855
0
      if (session->opts.username == NULL) {
856
0
          p = ssh_config_get_str_tok(&s, NULL);
857
0
          if (p && *parsing) {
858
0
            ssh_options_set(session, SSH_OPTIONS_USER, p);
859
0
         }
860
0
      }
861
0
      break;
862
23
    case SOC_IDENTITY:
863
23
      p = ssh_config_get_str_tok(&s, NULL);
864
23
      if (p && *parsing) {
865
5
        ssh_options_set(session, SSH_OPTIONS_ADD_IDENTITY, p);
866
5
      }
867
23
      break;
868
0
    case SOC_CIPHERS:
869
0
      p = ssh_config_get_str_tok(&s, NULL);
870
0
      if (p && *parsing) {
871
0
        ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, p);
872
0
        ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, p);
873
0
      }
874
0
      break;
875
0
    case SOC_MACS:
876
0
      p = ssh_config_get_str_tok(&s, NULL);
877
0
      if (p && *parsing) {
878
0
        ssh_options_set(session, SSH_OPTIONS_HMAC_C_S, p);
879
0
        ssh_options_set(session, SSH_OPTIONS_HMAC_S_C, p);
880
0
      }
881
0
      break;
882
0
    case SOC_COMPRESSION:
883
0
      i = ssh_config_get_yesno(&s, -1);
884
0
      if (i >= 0 && *parsing) {
885
0
        if (i) {
886
0
          ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes");
887
0
        } else {
888
0
          ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "no");
889
0
        }
890
0
      }
891
0
      break;
892
0
    case SOC_TIMEOUT:
893
0
      l = ssh_config_get_long(&s, -1);
894
0
      if (l >= 0 && *parsing) {
895
0
        ssh_options_set(session, SSH_OPTIONS_TIMEOUT, &l);
896
0
      }
897
0
      break;
898
3
    case SOC_STRICTHOSTKEYCHECK:
899
3
      i = ssh_config_get_yesno(&s, -1);
900
3
      if (i >= 0 && *parsing) {
901
0
        ssh_options_set(session, SSH_OPTIONS_STRICTHOSTKEYCHECK, &i);
902
0
      }
903
3
      break;
904
0
    case SOC_KNOWNHOSTS:
905
0
      p = ssh_config_get_str_tok(&s, NULL);
906
0
      if (p && *parsing) {
907
0
        ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, p);
908
0
      }
909
0
      break;
910
0
    case SOC_PROXYCOMMAND:
911
0
      p = ssh_config_get_cmd(&s);
912
      /* We share the seen value with the ProxyJump */
913
0
      if (p && *parsing && !seen[SOC_PROXYJUMP]) {
914
0
        ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, p);
915
0
      }
916
0
      break;
917
6
    case SOC_PROXYJUMP:
918
6
        p = ssh_config_get_str_tok(&s, NULL);
919
6
        if (p == NULL) {
920
0
            SAFE_FREE(x);
921
0
            return -1;
922
0
        }
923
        /* We share the seen value with the ProxyCommand */
924
6
        rv = ssh_config_parse_proxy_jump(session, p,
925
6
                                         (*parsing && !seen[SOC_PROXYCOMMAND]));
926
6
        if (rv != SSH_OK) {
927
0
            SAFE_FREE(x);
928
0
            return -1;
929
0
        }
930
6
        break;
931
6
    case SOC_GSSAPISERVERIDENTITY:
932
4
      p = ssh_config_get_str_tok(&s, NULL);
933
4
      if (p && *parsing) {
934
0
        ssh_options_set(session, SSH_OPTIONS_GSSAPI_SERVER_IDENTITY, p);
935
0
      }
936
4
      break;
937
0
    case SOC_GSSAPICLIENTIDENTITY:
938
0
      p = ssh_config_get_str_tok(&s, NULL);
939
0
      if (p && *parsing) {
940
0
        ssh_options_set(session, SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY, p);
941
0
      }
942
0
      break;
943
3
    case SOC_GSSAPIDELEGATECREDENTIALS:
944
3
      i = ssh_config_get_yesno(&s, -1);
945
3
      if (i >=0 && *parsing) {
946
0
        ssh_options_set(session, SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS, &i);
947
0
      }
948
3
      break;
949
0
    case SOC_BINDADDRESS:
950
0
        p = ssh_config_get_str_tok(&s, NULL);
951
0
        if (p && *parsing) {
952
0
            ssh_options_set(session, SSH_OPTIONS_BINDADDR, p);
953
0
        }
954
0
        break;
955
0
    case SOC_GLOBALKNOWNHOSTSFILE:
956
0
        p = ssh_config_get_str_tok(&s, NULL);
957
0
        if (p && *parsing) {
958
0
            ssh_options_set(session, SSH_OPTIONS_GLOBAL_KNOWNHOSTS, p);
959
0
        }
960
0
        break;
961
4
    case SOC_LOGLEVEL:
962
4
        p = ssh_config_get_str_tok(&s, NULL);
963
4
        if (p && *parsing) {
964
0
            int value = -1;
965
966
0
            if (strcasecmp(p, "quiet") == 0) {
967
0
                value = SSH_LOG_NONE;
968
0
            } else if (strcasecmp(p, "fatal") == 0 ||
969
0
                    strcasecmp(p, "error")== 0) {
970
0
                value = SSH_LOG_WARN;
971
0
            } else if (strcasecmp(p, "verbose") == 0 ||
972
0
                    strcasecmp(p, "info") == 0) {
973
0
                value = SSH_LOG_INFO;
974
0
            } else if (strcasecmp(p, "DEBUG") == 0 ||
975
0
                    strcasecmp(p, "DEBUG1") == 0) {
976
0
                value = SSH_LOG_DEBUG;
977
0
            } else if (strcasecmp(p, "DEBUG2") == 0 ||
978
0
                    strcasecmp(p, "DEBUG3") == 0) {
979
0
                value = SSH_LOG_TRACE;
980
0
            }
981
0
            if (value != -1) {
982
0
                ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &value);
983
0
            }
984
0
        }
985
4
        break;
986
0
    case SOC_HOSTKEYALGORITHMS:
987
0
        p = ssh_config_get_str_tok(&s, NULL);
988
0
        if (p && *parsing) {
989
0
            ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, p);
990
0
        }
991
0
        break;
992
0
    case SOC_PUBKEYACCEPTEDKEYTYPES:
993
0
        p = ssh_config_get_str_tok(&s, NULL);
994
0
        if (p && *parsing) {
995
0
            ssh_options_set(session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES, p);
996
0
        }
997
0
        break;
998
0
    case SOC_KEXALGORITHMS:
999
0
        p = ssh_config_get_str_tok(&s, NULL);
1000
0
        if (p && *parsing) {
1001
0
            ssh_options_set(session, SSH_OPTIONS_KEY_EXCHANGE, p);
1002
0
        }
1003
0
        break;
1004
44
    case SOC_REKEYLIMIT:
1005
        /* Parse the data limit */
1006
44
        p = ssh_config_get_str_tok(&s, NULL);
1007
44
        if (p == NULL) {
1008
0
            break;
1009
44
        } else if (strcmp(p, "default") == 0) {
1010
            /* Default rekey limits enforced automatically */
1011
0
            ll = 0;
1012
44
        } else {
1013
44
            char *endp = NULL;
1014
44
            ll = strtoll(p, &endp, 10);
1015
44
            if (p == endp || ll < 0) {
1016
                /* No number or negative */
1017
0
                SSH_LOG(SSH_LOG_TRACE, "Invalid argument to rekey limit");
1018
0
                break;
1019
0
            }
1020
44
            switch (*endp) {
1021
0
            case 'G':
1022
0
                if (ll > LLONG_MAX / 1024) {
1023
0
                    SSH_LOG(SSH_LOG_TRACE, "Possible overflow of rekey limit");
1024
0
                    ll = -1;
1025
0
                    break;
1026
0
                }
1027
0
                ll = ll * 1024;
1028
0
                FALL_THROUGH;
1029
0
            case 'M':
1030
0
                if (ll > LLONG_MAX / 1024) {
1031
0
                    SSH_LOG(SSH_LOG_TRACE, "Possible overflow of rekey limit");
1032
0
                    ll = -1;
1033
0
                    break;
1034
0
                }
1035
0
                ll = ll * 1024;
1036
0
                FALL_THROUGH;
1037
0
            case 'K':
1038
0
                if (ll > LLONG_MAX / 1024) {
1039
0
                    SSH_LOG(SSH_LOG_TRACE, "Possible overflow of rekey limit");
1040
0
                    ll = -1;
1041
0
                    break;
1042
0
                }
1043
0
                ll = ll * 1024;
1044
0
                endp++;
1045
0
                FALL_THROUGH;
1046
40
            case '\0':
1047
                /* just the number */
1048
40
                break;
1049
4
            default:
1050
                /* Invalid suffix */
1051
4
                ll = -1;
1052
4
                break;
1053
44
            }
1054
44
            if (*endp != ' ' && *endp != '\0') {
1055
4
                SSH_LOG(SSH_LOG_TRACE,
1056
4
                        "Invalid trailing characters after the rekey limit: %s",
1057
4
                        endp);
1058
4
                break;
1059
4
            }
1060
44
        }
1061
40
        if (ll > -1 && *parsing) {
1062
0
            uint64_t v = (uint64_t)ll;
1063
0
            ssh_options_set(session, SSH_OPTIONS_REKEY_DATA, &v);
1064
0
        }
1065
        /* Parse the time limit */
1066
40
        p = ssh_config_get_str_tok(&s, NULL);
1067
40
        if (p == NULL) {
1068
0
            break;
1069
40
        } else if (strcmp(p, "none") == 0) {
1070
0
            ll = 0;
1071
40
        } else {
1072
40
            char *endp = NULL;
1073
40
            ll = strtoll(p, &endp, 10);
1074
40
            if (p == endp || ll < 0) {
1075
                /* No number or negative */
1076
0
                SSH_LOG(SSH_LOG_TRACE, "Invalid argument to rekey limit");
1077
0
                break;
1078
0
            }
1079
40
            switch (*endp) {
1080
14
            case 'w':
1081
17
            case 'W':
1082
17
                if (ll > LLONG_MAX / 7) {
1083
14
                    SSH_LOG(SSH_LOG_TRACE, "Possible overflow of rekey limit");
1084
14
                    ll = -1;
1085
14
                    break;
1086
14
                }
1087
3
                ll = ll * 7;
1088
3
                FALL_THROUGH;
1089
3
            case 'd':
1090
3
            case 'D':
1091
3
                if (ll > LLONG_MAX / 24) {
1092
0
                    SSH_LOG(SSH_LOG_TRACE, "Possible overflow of rekey limit");
1093
0
                    ll = -1;
1094
0
                    break;
1095
0
                }
1096
3
                ll = ll * 24;
1097
3
                FALL_THROUGH;
1098
7
            case 'h':
1099
7
            case 'H':
1100
7
                if (ll > LLONG_MAX / 60) {
1101
0
                    SSH_LOG(SSH_LOG_TRACE, "Possible overflow of rekey limit");
1102
0
                    ll = -1;
1103
0
                    break;
1104
0
                }
1105
7
                ll = ll * 60;
1106
7
                FALL_THROUGH;
1107
7
            case 'm':
1108
7
            case 'M':
1109
7
                if (ll > LLONG_MAX / 60) {
1110
0
                    SSH_LOG(SSH_LOG_TRACE, "Possible overflow of rekey limit");
1111
0
                    ll = -1;
1112
0
                    break;
1113
0
                }
1114
7
                ll = ll * 60;
1115
7
                FALL_THROUGH;
1116
7
            case 's':
1117
9
            case 'S':
1118
9
                endp++;
1119
9
                FALL_THROUGH;
1120
9
            case '\0':
1121
                /* just the number */
1122
9
                break;
1123
17
            default:
1124
                /* Invalid suffix */
1125
17
                ll = -1;
1126
17
                break;
1127
40
            }
1128
40
            if (*endp != '\0') {
1129
40
                SSH_LOG(SSH_LOG_TRACE, "Invalid trailing characters after the"
1130
40
                        " rekey limit: %s", endp);
1131
40
                break;
1132
40
            }
1133
40
        }
1134
0
        if (ll > -1 && *parsing) {
1135
0
            uint32_t v = (uint32_t)ll;
1136
0
            ssh_options_set(session, SSH_OPTIONS_REKEY_TIME, &v);
1137
0
        }
1138
0
        break;
1139
0
    case SOC_GSSAPIAUTHENTICATION:
1140
0
    case SOC_KBDINTERACTIVEAUTHENTICATION:
1141
0
    case SOC_PASSWORDAUTHENTICATION:
1142
0
    case SOC_PUBKEYAUTHENTICATION:
1143
0
        i = ssh_config_get_yesno(&s, 0);
1144
0
        if (i>=0 && *parsing) {
1145
0
            switch(opcode){
1146
0
            case SOC_GSSAPIAUTHENTICATION:
1147
0
                ssh_options_set(session, SSH_OPTIONS_GSSAPI_AUTH, &i);
1148
0
                break;
1149
0
            case SOC_KBDINTERACTIVEAUTHENTICATION:
1150
0
                ssh_options_set(session, SSH_OPTIONS_KBDINT_AUTH, &i);
1151
0
                break;
1152
0
            case SOC_PASSWORDAUTHENTICATION:
1153
0
                ssh_options_set(session, SSH_OPTIONS_PASSWORD_AUTH, &i);
1154
0
                break;
1155
0
            case SOC_PUBKEYAUTHENTICATION:
1156
0
                ssh_options_set(session, SSH_OPTIONS_PUBKEY_AUTH, &i);
1157
0
                break;
1158
            /* make gcc happy */
1159
0
            default:
1160
0
                break;
1161
0
            }
1162
0
        }
1163
0
        break;
1164
3
    case SOC_NA:
1165
3
      SSH_LOG(SSH_LOG_TRACE, "Unapplicable option: %s, line: %d",
1166
3
              keyword, count);
1167
3
      break;
1168
0
    case SOC_UNSUPPORTED:
1169
0
      SSH_LOG(SSH_LOG_RARE, "Unsupported option: %s, line: %d",
1170
0
              keyword, count);
1171
0
      break;
1172
592
    case SOC_UNKNOWN:
1173
592
      SSH_LOG(SSH_LOG_TRACE, "Unknown option: %s, line: %d",
1174
592
              keyword, count);
1175
592
      break;
1176
0
    case SOC_IDENTITYAGENT:
1177
0
      p = ssh_config_get_str_tok(&s, NULL);
1178
0
      if (p && *parsing) {
1179
0
          ssh_options_set(session, SSH_OPTIONS_IDENTITY_AGENT, p);
1180
0
      }
1181
0
      break;
1182
12
    case SOC_IDENTITIESONLY:
1183
12
      i = ssh_config_get_yesno(&s, -1);
1184
12
      if (i >= 0 && *parsing) {
1185
0
        bool b = i;
1186
0
        ssh_options_set(session, SSH_OPTIONS_IDENTITIES_ONLY, &b);
1187
0
      }
1188
12
      break;
1189
0
    default:
1190
0
      ssh_set_error(session, SSH_FATAL, "ERROR - unimplemented opcode: %d",
1191
0
              opcode);
1192
0
      SAFE_FREE(x);
1193
0
      return -1;
1194
0
      break;
1195
821
  }
1196
1197
821
  SAFE_FREE(x);
1198
821
  return 0;
1199
821
}
1200
1201
/* @brief Parse configuration file and set the options to the given session
1202
 *
1203
 * @params[in] session   The ssh session
1204
 * @params[in] filename  The path to the ssh configuration file
1205
 *
1206
 * @returns    0 on successful parsing the configuration file, -1 on error
1207
 */
1208
int ssh_config_parse_file(ssh_session session, const char *filename)
1209
0
{
1210
0
    char line[MAX_LINE_SIZE] = {0};
1211
0
    unsigned int count = 0;
1212
0
    FILE *f;
1213
0
    int parsing, rv;
1214
0
    bool global = 0;
1215
1216
0
    f = fopen(filename, "r");
1217
0
    if (f == NULL) {
1218
0
        return 0;
1219
0
    }
1220
1221
0
    rv = strcmp(filename, GLOBAL_CLIENT_CONFIG);
1222
0
    if (rv == 0) {
1223
0
        global = true;
1224
0
    }
1225
1226
0
    SSH_LOG(SSH_LOG_PACKET, "Reading configuration data from %s", filename);
1227
1228
0
    parsing = 1;
1229
0
    while (fgets(line, sizeof(line), f)) {
1230
0
        count++;
1231
0
        rv = ssh_config_parse_line(session, line, count, &parsing, 0, global);
1232
0
        if (rv < 0) {
1233
0
            fclose(f);
1234
0
            return -1;
1235
0
        }
1236
0
    }
1237
1238
0
    fclose(f);
1239
0
    return 0;
1240
0
}
1241
1242
/* @brief Parse configuration string and set the options to the given session
1243
 *
1244
 * @params[in] session   The ssh session
1245
 * @params[in] input     Null terminated string containing the configuration
1246
 *
1247
 * @returns    SSH_OK on successful parsing the configuration string,
1248
 *             SSH_ERROR on error
1249
 */
1250
int ssh_config_parse_string(ssh_session session, const char *input)
1251
10
{
1252
10
    char line[MAX_LINE_SIZE] = {0};
1253
10
    const char *c = input, *line_start = input;
1254
10
    unsigned int line_num = 0, line_len;
1255
10
    int parsing, rv;
1256
1257
10
    SSH_LOG(SSH_LOG_DEBUG, "Reading configuration data from string:");
1258
10
    SSH_LOG(SSH_LOG_DEBUG, "START\n%s\nEND", input);
1259
1260
10
    parsing = 1;
1261
1.16k
    while (1) {
1262
1.16k
        line_num++;
1263
1.16k
        line_start = c;
1264
1.16k
        c = strchr(line_start, '\n');
1265
1.16k
        if (c == NULL) {
1266
            /* if there is no newline at the end of the string */
1267
10
            c = strchr(line_start, '\0');
1268
10
        }
1269
1.16k
        if (c == NULL) {
1270
            /* should not happen, would mean a string without trailing '\0' */
1271
0
            SSH_LOG(SSH_LOG_TRACE, "No trailing '\\0' in config string");
1272
0
            return SSH_ERROR;
1273
0
        }
1274
1.16k
        line_len = c - line_start;
1275
1.16k
        if (line_len > MAX_LINE_SIZE - 1) {
1276
8
            SSH_LOG(SSH_LOG_TRACE, "Line %u too long: %u characters",
1277
8
                    line_num, line_len);
1278
8
            return SSH_ERROR;
1279
8
        }
1280
1.15k
        memcpy(line, line_start, line_len);
1281
1.15k
        line[line_len] = '\0';
1282
1.15k
        SSH_LOG(SSH_LOG_DEBUG, "Line %u: %s", line_num, line);
1283
1.15k
        rv = ssh_config_parse_line(session, line, line_num, &parsing, 0, false);
1284
1.15k
        if (rv < 0) {
1285
0
            return SSH_ERROR;
1286
0
        }
1287
1.15k
        if (*c == '\0') {
1288
2
            break;
1289
2
        }
1290
1.15k
        c++;
1291
1.15k
    }
1292
1293
2
    return SSH_OK;
1294
10
}