Coverage Report

Created: 2023-11-19 06:39

/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
0
#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_CONTROLMASTER},
131
  { "controlpersist", SOC_NA},
132
  { "controlpath", SOC_CONTROLPATH},
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
0
static enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword) {
187
0
  int i;
188
189
0
  for (i = 0; ssh_config_keyword_table[i].name != NULL; i++) {
190
0
    if (strcasecmp(keyword, ssh_config_keyword_table[i].name) == 0) {
191
0
      return ssh_config_keyword_table[i].opcode;
192
0
    }
193
0
  }
194
195
0
  return SOC_UNKNOWN;
196
0
}
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
0
{
274
0
    size_t i;
275
276
0
    for (i = 0; ssh_config_match_keyword_table[i].name != NULL; i++) {
277
0
        if (strcasecmp(keyword, ssh_config_match_keyword_table[i].name) == 0) {
278
0
            return ssh_config_match_keyword_table[i].opcode;
279
0
        }
280
0
    }
281
282
0
    return MATCH_UNKNOWN;
283
0
}
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
0
{
319
0
    char *shell = NULL;
320
0
    pid_t pid;
321
0
    int status, devnull, rc;
322
0
    char err_msg[SSH_ERRNO_MSG_MAX] = {0};
323
324
0
    shell = getenv("SHELL");
325
0
    if (shell == NULL || shell[0] == '\0') {
326
0
        shell = (char *)"/bin/sh";
327
0
    }
328
329
0
    rc = access(shell, X_OK);
330
0
    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
0
    devnull = open("/dev/null", O_RDWR);
337
0
    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
0
    SSH_LOG(SSH_LOG_DEBUG, "Running command '%s'", cmd);
344
0
    pid = fork();
345
0
    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
0
    close(devnull);
383
0
    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
0
    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
0
    if (!WIFEXITED(status)) {
398
0
        SSH_LOG(SSH_LOG_WARN, "Command %s exited abnormally", cmd);
399
0
        return -1;
400
0
    }
401
0
    SSH_LOG(SSH_LOG_TRACE, "Command '%s' returned %d", cmd, WEXITSTATUS(status));
402
0
    return WEXITSTATUS(status);
403
0
}
404
405
static int
406
ssh_match_exec(ssh_session session, const char *command, bool negate)
407
0
{
408
0
    int rv, result = 0;
409
0
    char *cmd = NULL;
410
411
    /* TODO There should be more supported expansions */
412
0
    cmd = ssh_path_expand_escape(session, command);
413
0
    if (cmd == NULL) {
414
0
        return 0;
415
0
    }
416
0
    rv = ssh_exec_shell(cmd);
417
0
    if (rv > 0 && negate == true) {
418
0
        result = 1;
419
0
    } else if (rv == 0 && negate == false) {
420
0
        result = 1;
421
0
    }
422
0
    SSH_LOG(SSH_LOG_TRACE, "%s 'exec' command '%s'%s (rv=%d)",
423
0
            result == 1 ? "Matched" : "Not matched", cmd,
424
0
            negate == true ? " (negated)" : "", rv);
425
0
    free(cmd);
426
0
    return result;
427
0
}
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
0
{
436
0
    char *c = NULL, *cp = NULL, *endp = NULL;
437
0
    char *username = NULL;
438
0
    char *hostname = NULL;
439
0
    char *port = NULL;
440
0
    char *next = NULL;
441
0
    int cmp, rv = SSH_ERROR;
442
0
    bool parse_entry = do_parsing;
443
444
    /* Special value none disables the proxy */
445
0
    cmp = strcasecmp(s, "none");
446
0
    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
0
    c = strdup(s);
453
0
    if (c == NULL) {
454
0
        ssh_set_error_oom(session);
455
0
        return SSH_ERROR;
456
0
    }
457
458
0
    cp = c;
459
0
    do {
460
0
        endp = strchr(cp, ',');
461
0
        if (endp != NULL) {
462
            /* Split out the token */
463
0
            *endp = '\0';
464
0
        }
465
0
        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
0
        } else {
477
            /* The rest is just sanity-checked to avoid failures later */
478
0
            rv = ssh_config_parse_uri(cp, NULL, NULL, NULL);
479
0
        }
480
0
        if (rv != SSH_OK) {
481
0
            goto out;
482
0
        }
483
0
        parse_entry = 0;
484
0
        if (endp != NULL) {
485
0
            cp = endp + 1;
486
0
        } else {
487
0
            cp = NULL; /* end */
488
0
        }
489
0
    } while (cp != NULL);
490
491
0
    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
0
    rv = SSH_OK;
510
511
0
out:
512
0
    SAFE_FREE(username);
513
0
    SAFE_FREE(hostname);
514
0
    SAFE_FREE(port);
515
0
    SAFE_FREE(next);
516
0
    SAFE_FREE(c);
517
0
    return rv;
518
0
}
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
0
{
583
0
  enum ssh_config_opcode_e opcode;
584
0
  const char *p = NULL, *p2 = NULL;
585
0
  char *s = NULL, *x = NULL;
586
0
  char *keyword = NULL;
587
0
  char *lowerhost = NULL;
588
0
  size_t len;
589
0
  int i, rv;
590
0
  uint8_t *seen = session->opts.options_seen;
591
0
  long l;
592
0
  int64_t ll;
593
594
  /* Ignore empty lines */
595
0
  if (line == NULL || *line == '\0') {
596
0
    return 0;
597
0
  }
598
599
0
  x = s = strdup(line);
600
0
  if (s == NULL) {
601
0
    ssh_set_error_oom(session);
602
0
    return -1;
603
0
  }
604
605
  /* Remove trailing spaces */
606
0
  for (len = strlen(s) - 1; len > 0; len--) {
607
0
    if (! isspace(s[len])) {
608
0
      break;
609
0
    }
610
0
    s[len] = '\0';
611
0
  }
612
613
0
  keyword = ssh_config_get_token(&s);
614
0
  if (keyword == NULL || *keyword == '#' ||
615
0
      *keyword == '\0' || *keyword == '\n') {
616
0
    SAFE_FREE(x);
617
0
    return 0;
618
0
  }
619
620
0
  opcode = ssh_config_get_opcode(keyword);
621
0
  if (*parsing == 1 &&
622
0
      opcode != SOC_HOST &&
623
0
      opcode != SOC_MATCH &&
624
0
      opcode != SOC_INCLUDE &&
625
0
      opcode != SOC_IDENTITY &&
626
0
      opcode > SOC_UNSUPPORTED &&
627
0
      opcode < SOC_MAX) { /* Ignore all unknown types here */
628
      /* Skip all the options that were already applied */
629
0
      if (seen[opcode] != 0) {
630
0
          SAFE_FREE(x);
631
0
          return 0;
632
0
      }
633
0
      seen[opcode] = 1;
634
0
  }
635
636
0
  switch (opcode) {
637
0
    case SOC_INCLUDE: /* recursive include of other files */
638
639
0
      p = ssh_config_get_str_tok(&s, NULL);
640
0
      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
0
      break;
656
657
0
    case SOC_MATCH: {
658
0
        bool negate;
659
0
        int result = 1;
660
0
        size_t args = 0;
661
0
        enum ssh_config_match_e opt;
662
0
        char *localuser = NULL;
663
664
0
        *parsing = 0;
665
0
        do {
666
0
            p = p2 = ssh_config_get_str_tok(&s, NULL);
667
0
            if (p == NULL || p[0] == '\0') {
668
0
                break;
669
0
            }
670
0
            args++;
671
0
            SSH_LOG(SSH_LOG_DEBUG, "line %d: Processing Match keyword '%s'",
672
0
                    count, p);
673
674
            /* If the option is prefixed with ! the result should be negated */
675
0
            negate = false;
676
0
            if (p[0] == '!') {
677
0
                negate = true;
678
0
                p++;
679
0
            }
680
681
0
            opt = ssh_config_get_match_opcode(p);
682
0
            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
0
            case MATCH_CANONICAL:
703
0
                SSH_LOG(SSH_LOG_DEBUG,
704
0
                        "line %d: Unsupported Match keyword '%s', skipping",
705
0
                        count,
706
0
                        p);
707
                /* Not set any result here -- the result is dependent on the
708
                 * following matches after this keyword */
709
0
                break;
710
711
0
            case MATCH_EXEC:
712
                /* Skip one argument (including in quotes) */
713
0
                p = ssh_config_get_token(&s);
714
0
                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
0
                if (result != 1) {
721
0
                    SSH_LOG(SSH_LOG_DEBUG, "line %d: Skipped match exec "
722
0
                            "'%s' as previous conditions already failed.",
723
0
                            count, p2);
724
0
                    continue;
725
0
                }
726
0
                result &= ssh_match_exec(session, p, negate);
727
0
                args++;
728
0
                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
0
            }
804
0
        } while (p != NULL && p[0] != '\0');
805
0
        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
0
        *parsing = result;
812
0
        break;
813
0
    }
814
0
    case SOC_HOST: {
815
0
        int ok = 0, result = -1;
816
817
0
        *parsing = 0;
818
0
        lowerhost = (session->opts.host) ? ssh_lowercase(session->opts.host) : NULL;
819
0
        for (p = ssh_config_get_str_tok(&s, NULL);
820
0
             p != NULL && p[0] != '\0';
821
0
             p = ssh_config_get_str_tok(&s, NULL)) {
822
0
             if (ok >= 0) {
823
0
               ok = match_hostname(lowerhost, p, strlen(p));
824
0
               if (result == -1 && ok < 0) {
825
0
                   result = 0;
826
0
               } else if (result == -1 && ok > 0) {
827
0
                   result = 1;
828
0
               }
829
0
            }
830
0
        }
831
0
        SAFE_FREE(lowerhost);
832
0
        if (result != -1) {
833
0
            *parsing = result;
834
0
        }
835
0
        break;
836
0
    }
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
0
    case SOC_PORT:
849
0
        p = ssh_config_get_str_tok(&s, NULL);
850
0
        if (p && *parsing) {
851
0
            ssh_options_set(session, SSH_OPTIONS_PORT_STR, p);
852
0
        }
853
0
        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
0
    case SOC_IDENTITY:
863
0
      p = ssh_config_get_str_tok(&s, NULL);
864
0
      if (p && *parsing) {
865
0
        ssh_options_set(session, SSH_OPTIONS_ADD_IDENTITY, p);
866
0
      }
867
0
      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
0
    case SOC_STRICTHOSTKEYCHECK:
899
0
      i = ssh_config_get_yesno(&s, -1);
900
0
      if (i >= 0 && *parsing) {
901
0
        ssh_options_set(session, SSH_OPTIONS_STRICTHOSTKEYCHECK, &i);
902
0
      }
903
0
      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
0
    case SOC_PROXYJUMP:
918
0
        p = ssh_config_get_str_tok(&s, NULL);
919
0
        if (p == NULL) {
920
0
            SAFE_FREE(x);
921
0
            return -1;
922
0
        }
923
        /* We share the seen value with the ProxyCommand */
924
0
        rv = ssh_config_parse_proxy_jump(session, p,
925
0
                                         (*parsing && !seen[SOC_PROXYCOMMAND]));
926
0
        if (rv != SSH_OK) {
927
0
            SAFE_FREE(x);
928
0
            return -1;
929
0
        }
930
0
        break;
931
0
    case SOC_GSSAPISERVERIDENTITY:
932
0
      p = ssh_config_get_str_tok(&s, NULL);
933
0
      if (p && *parsing) {
934
0
        ssh_options_set(session, SSH_OPTIONS_GSSAPI_SERVER_IDENTITY, p);
935
0
      }
936
0
      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
0
    case SOC_GSSAPIDELEGATECREDENTIALS:
944
0
      i = ssh_config_get_yesno(&s, -1);
945
0
      if (i >=0 && *parsing) {
946
0
        ssh_options_set(session, SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS, &i);
947
0
      }
948
0
      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
0
    case SOC_LOGLEVEL:
962
0
        p = ssh_config_get_str_tok(&s, NULL);
963
0
        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
0
        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
0
    case SOC_REKEYLIMIT:
1005
        /* Parse the data limit */
1006
0
        p = ssh_config_get_str_tok(&s, NULL);
1007
0
        if (p == NULL) {
1008
0
            break;
1009
0
        } else if (strcmp(p, "default") == 0) {
1010
            /* Default rekey limits enforced automatically */
1011
0
            ll = 0;
1012
0
        } else {
1013
0
            char *endp = NULL;
1014
0
            ll = strtoll(p, &endp, 10);
1015
0
            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
0
            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
0
            case '\0':
1047
                /* just the number */
1048
0
                break;
1049
0
            default:
1050
                /* Invalid suffix */
1051
0
                ll = -1;
1052
0
                break;
1053
0
            }
1054
0
            if (*endp != ' ' && *endp != '\0') {
1055
0
                SSH_LOG(SSH_LOG_TRACE,
1056
0
                        "Invalid trailing characters after the rekey limit: %s",
1057
0
                        endp);
1058
0
                break;
1059
0
            }
1060
0
        }
1061
0
        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
0
        p = ssh_config_get_str_tok(&s, NULL);
1067
0
        if (p == NULL) {
1068
0
            break;
1069
0
        } else if (strcmp(p, "none") == 0) {
1070
0
            ll = 0;
1071
0
        } else {
1072
0
            char *endp = NULL;
1073
0
            ll = strtoll(p, &endp, 10);
1074
0
            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
0
            switch (*endp) {
1080
0
            case 'w':
1081
0
            case 'W':
1082
0
                if (ll > LLONG_MAX / 7) {
1083
0
                    SSH_LOG(SSH_LOG_TRACE, "Possible overflow of rekey limit");
1084
0
                    ll = -1;
1085
0
                    break;
1086
0
                }
1087
0
                ll = ll * 7;
1088
0
                FALL_THROUGH;
1089
0
            case 'd':
1090
0
            case 'D':
1091
0
                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
0
                ll = ll * 24;
1097
0
                FALL_THROUGH;
1098
0
            case 'h':
1099
0
            case 'H':
1100
0
                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
0
                ll = ll * 60;
1106
0
                FALL_THROUGH;
1107
0
            case 'm':
1108
0
            case 'M':
1109
0
                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
0
                ll = ll * 60;
1115
0
                FALL_THROUGH;
1116
0
            case 's':
1117
0
            case 'S':
1118
0
                endp++;
1119
0
                FALL_THROUGH;
1120
0
            case '\0':
1121
                /* just the number */
1122
0
                break;
1123
0
            default:
1124
                /* Invalid suffix */
1125
0
                ll = -1;
1126
0
                break;
1127
0
            }
1128
0
            if (*endp != '\0') {
1129
0
                SSH_LOG(SSH_LOG_TRACE, "Invalid trailing characters after the"
1130
0
                        " rekey limit: %s", endp);
1131
0
                break;
1132
0
            }
1133
0
        }
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
0
    case SOC_NA:
1165
0
      SSH_LOG(SSH_LOG_TRACE, "Unapplicable option: %s, line: %d",
1166
0
              keyword, count);
1167
0
      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
0
    case SOC_UNKNOWN:
1173
0
      SSH_LOG(SSH_LOG_TRACE, "Unknown option: %s, line: %d",
1174
0
              keyword, count);
1175
0
      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
0
    case SOC_IDENTITIESONLY:
1183
0
      i = ssh_config_get_yesno(&s, -1);
1184
0
      if (i >= 0 && *parsing) {
1185
0
        bool b = i;
1186
0
        ssh_options_set(session, SSH_OPTIONS_IDENTITIES_ONLY, &b);
1187
0
      }
1188
0
      break;
1189
0
    case SOC_CONTROLMASTER:
1190
0
      p = ssh_config_get_str_tok(&s, NULL);
1191
0
      if (p && *parsing) {
1192
0
          int value = -1;
1193
1194
0
          if (strcasecmp(p, "auto") == 0) {
1195
0
              value = SSH_CONTROL_MASTER_AUTO;
1196
0
          } else if (strcasecmp(p, "yes") == 0) {
1197
0
              value = SSH_CONTROL_MASTER_YES;
1198
0
          } else if (strcasecmp(p, "no") == 0) {
1199
0
              value = SSH_CONTROL_MASTER_NO;
1200
0
          } else if (strcasecmp(p, "autoask") == 0) {
1201
0
              value = SSH_CONTROL_MASTER_AUTOASK;
1202
0
          } else if (strcasecmp(p, "ask") == 0) {
1203
0
              value = SSH_CONTROL_MASTER_ASK;
1204
0
          }
1205
1206
0
          if (value != -1) {
1207
0
              ssh_options_set(session, SSH_OPTIONS_CONTROL_MASTER, &value);
1208
0
          }
1209
0
      }
1210
0
      break;
1211
0
    case SOC_CONTROLPATH:
1212
0
      p = ssh_config_get_str_tok(&s, NULL);
1213
0
      if (p == NULL) {
1214
0
        SAFE_FREE(x);
1215
0
        return -1;
1216
0
      }
1217
0
      if (*parsing) {
1218
0
          ssh_options_set(session, SSH_OPTIONS_CONTROL_PATH, p);
1219
0
      }
1220
0
      break;
1221
0
    default:
1222
0
      ssh_set_error(session, SSH_FATAL, "ERROR - unimplemented opcode: %d",
1223
0
              opcode);
1224
0
      SAFE_FREE(x);
1225
0
      return -1;
1226
0
      break;
1227
0
  }
1228
1229
0
  SAFE_FREE(x);
1230
0
  return 0;
1231
0
}
1232
1233
/* @brief Parse configuration file and set the options to the given session
1234
 *
1235
 * @params[in] session   The ssh session
1236
 * @params[in] filename  The path to the ssh configuration file
1237
 *
1238
 * @returns    0 on successful parsing the configuration file, -1 on error
1239
 */
1240
int ssh_config_parse_file(ssh_session session, const char *filename)
1241
0
{
1242
0
    char line[MAX_LINE_SIZE] = {0};
1243
0
    unsigned int count = 0;
1244
0
    FILE *f;
1245
0
    int parsing, rv;
1246
0
    bool global = 0;
1247
1248
0
    f = fopen(filename, "r");
1249
0
    if (f == NULL) {
1250
0
        return 0;
1251
0
    }
1252
1253
0
    rv = strcmp(filename, GLOBAL_CLIENT_CONFIG);
1254
0
    if (rv == 0) {
1255
0
        global = true;
1256
0
    }
1257
1258
0
    SSH_LOG(SSH_LOG_PACKET, "Reading configuration data from %s", filename);
1259
1260
0
    parsing = 1;
1261
0
    while (fgets(line, sizeof(line), f)) {
1262
0
        count++;
1263
0
        rv = ssh_config_parse_line(session, line, count, &parsing, 0, global);
1264
0
        if (rv < 0) {
1265
0
            fclose(f);
1266
0
            return -1;
1267
0
        }
1268
0
    }
1269
1270
0
    fclose(f);
1271
0
    return 0;
1272
0
}
1273
1274
/* @brief Parse configuration string and set the options to the given session
1275
 *
1276
 * @params[in] session   The ssh session
1277
 * @params[in] input     Null terminated string containing the configuration
1278
 *
1279
 * @returns    SSH_OK on successful parsing the configuration string,
1280
 *             SSH_ERROR on error
1281
 */
1282
int ssh_config_parse_string(ssh_session session, const char *input)
1283
0
{
1284
0
    char line[MAX_LINE_SIZE] = {0};
1285
0
    const char *c = input, *line_start = input;
1286
0
    unsigned int line_num = 0, line_len;
1287
0
    int parsing, rv;
1288
1289
0
    SSH_LOG(SSH_LOG_DEBUG, "Reading configuration data from string:");
1290
0
    SSH_LOG(SSH_LOG_DEBUG, "START\n%s\nEND", input);
1291
1292
0
    parsing = 1;
1293
0
    while (1) {
1294
0
        line_num++;
1295
0
        line_start = c;
1296
0
        c = strchr(line_start, '\n');
1297
0
        if (c == NULL) {
1298
            /* if there is no newline at the end of the string */
1299
0
            c = strchr(line_start, '\0');
1300
0
        }
1301
0
        if (c == NULL) {
1302
            /* should not happen, would mean a string without trailing '\0' */
1303
0
            SSH_LOG(SSH_LOG_TRACE, "No trailing '\\0' in config string");
1304
0
            return SSH_ERROR;
1305
0
        }
1306
0
        line_len = c - line_start;
1307
0
        if (line_len > MAX_LINE_SIZE - 1) {
1308
0
            SSH_LOG(SSH_LOG_TRACE, "Line %u too long: %u characters",
1309
0
                    line_num, line_len);
1310
0
            return SSH_ERROR;
1311
0
        }
1312
0
        memcpy(line, line_start, line_len);
1313
0
        line[line_len] = '\0';
1314
0
        SSH_LOG(SSH_LOG_DEBUG, "Line %u: %s", line_num, line);
1315
0
        rv = ssh_config_parse_line(session, line, line_num, &parsing, 0, false);
1316
0
        if (rv < 0) {
1317
0
            return SSH_ERROR;
1318
0
        }
1319
0
        if (*c == '\0') {
1320
0
            break;
1321
0
        }
1322
0
        c++;
1323
0
    }
1324
1325
0
    return SSH_OK;
1326
0
}