Coverage Report

Created: 2025-07-11 06:12

/src/dropbear/src/cli-readconf.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Dropbear - a SSH2 server
3
 *
4
 * Copyright (c) 2023 TJ Kolev
5
 * All rights reserved.
6
 *
7
 * Permission is hereby granted, free of charge, to any person obtaining a copy
8
 * of this software and associated documentation files (the "Software"), to deal
9
 * in the Software without restriction, including without limitation the rights
10
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
 * copies of the Software, and to permit persons to whom the Software is
12
 * furnished to do so, subject to the following conditions:
13
 *
14
 * The above copyright notice and this permission notice shall be included in
15
 * all copies or substantial portions of the Software.
16
 *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
 * SOFTWARE. */
24
25
#include "dbutil.h"
26
#include "runopts.h"
27
28
#if DROPBEAR_USE_SSH_CONFIG
29
30
0
#define TOKEN_CHARS " =\t\n"
31
32
static const size_t MAX_CONF_LINE = 200;
33
34
typedef enum {
35
  opHost,
36
  opHostName,
37
  opHostPort,
38
  opLoginUser,
39
  opIdentityFile,
40
} cfg_option;
41
42
static const struct {
43
  const char *name;
44
  cfg_option option;
45
}
46
config_options[] = {
47
  /* Start of config section. */
48
  { "host", opHost },
49
50
  { "hostname", opHostName },
51
  { "port", opHostPort },
52
  { "user", opLoginUser },
53
  { "identityfile", opIdentityFile },
54
};
55
56
0
void read_config_file(char* filename, FILE* config_file, cli_runopts* options) {
57
0
  char *line = NULL;
58
0
  int linenum = 0;
59
0
  buffer *buf = NULL;
60
0
  char* cfg_key;
61
0
  char* cfg_val;
62
0
  char* saveptr;
63
0
  int in_host_section = 0;
64
65
0
  DEBUG1(("Reading '%.200s'", filename));
66
67
0
  buf = buf_new(MAX_CONF_LINE);
68
0
  line = buf->data;
69
0
  while (buf_getline(buf, config_file) == DROPBEAR_SUCCESS) {
70
0
    char* commentStart = NULL;
71
0
    cfg_option cfg_opt;
72
0
    int found;
73
0
    size_t i;
74
    /* Update line number counter. */
75
0
    linenum++;
76
77
    /* Add nul terminator */
78
0
    if (buf->len == buf->size) {
79
0
      dropbear_exit("Long line %s:%d", filename, linenum);
80
0
    }
81
0
    buf_setpos(buf, buf->len);
82
0
    buf_putbyte(buf, '\0');
83
0
    buf_setpos(buf, 0);
84
85
0
    commentStart = strchr(line, '#');
86
0
    if (NULL != commentStart) {
87
0
      *commentStart = '\0'; /* Drop the comments. */
88
0
    }
89
90
0
    cfg_key = strtok_r(line, TOKEN_CHARS, &saveptr);
91
0
    if (NULL == cfg_key) {
92
0
      continue;
93
0
    }
94
95
0
    found = 0;
96
0
    for (i = 0; i < ARRAY_SIZE(config_options); i++) {
97
0
      if (0 == strcasecmp(cfg_key, config_options[i].name)) {
98
0
        cfg_opt = config_options[i].option;
99
0
        found = 1;
100
0
        break;
101
0
      }
102
0
    }
103
104
0
    if (!found) {
105
0
      dropbear_exit("Unsupported option %s at %s:%d", cfg_key, filename, linenum);
106
0
    }
107
108
109
0
    cfg_val = strtok_r(NULL, TOKEN_CHARS, &saveptr);
110
0
    if (NULL == cfg_val) {
111
0
      dropbear_exit("Missing value for %s at %s:%d", cfg_key, filename, linenum);
112
0
    }
113
114
0
    if (in_host_section) {
115
0
      switch (cfg_opt) {
116
0
        case opHost: {
117
          /* Hit the next host section. Done reading config. */
118
0
          goto outloop;
119
0
        }
120
0
        case opHostName: {
121
          /* The host name is the alias given on the command line.
122
           * Set the actual remote host specified in the config.
123
           */
124
0
          m_free(options->remotehost);
125
0
          options->remotehost = m_strdup(cfg_val);
126
0
          options->remotehostfixed = 1; /* Subsequent command line parsing should leave it alone. */
127
0
          break;
128
0
        }
129
130
0
        case opHostPort: {
131
0
          m_free(options->remoteport);
132
0
          options->remoteport = m_strdup(cfg_val);
133
0
          break;
134
0
        }
135
136
0
        case opLoginUser: {
137
0
          m_free(options->username);
138
0
          options->username = m_strdup(cfg_val);
139
0
          break;
140
0
        }
141
142
0
        case opIdentityFile: {
143
0
#if DROPBEAR_CLI_PUBKEY_AUTH
144
0
          char* key_file_path;
145
0
          if (strncmp(cfg_val, "~/", 2) == 0) {
146
0
            key_file_path = expand_homedir_path(cfg_val);
147
0
          } else if (cfg_val[0] != '/') {
148
0
            char* config_dir = dirname(filename);
149
0
            int path_len = strlen(config_dir) + strlen(cfg_val) + 10;
150
0
            key_file_path = m_malloc(path_len);
151
0
            snprintf(key_file_path, path_len, "%s/%s", config_dir, cfg_val);
152
0
          } else {
153
0
            key_file_path = m_strdup(cfg_val);
154
0
          }
155
0
          loadidentityfile(key_file_path, 1);
156
0
          m_free(key_file_path);
157
#else
158
          dropbear_exit("identityfile isn't supported in %s", filename);
159
#endif
160
0
          break;
161
0
        }
162
0
      }
163
0
    }
164
0
    else
165
0
    {
166
0
      if (opHost != cfg_opt || 0 != strcmp(cfg_val, options->remotehost)) {
167
        /* Not our host section. */
168
0
        continue;
169
0
      }
170
0
      in_host_section = 1;
171
0
    }
172
0
  }
173
0
outloop:
174
0
  buf_free(buf);
175
0
}
176
177
#endif /* DROPBEAR_USE_SSH_CONFIG */