/src/dropbear/src/cli-readconf.c
Line | Count | Source |
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 | 8.04k | #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 | 354 | void read_config_file(char* filename, FILE* config_file, cli_runopts* options) { |
57 | 354 | char *line = NULL; |
58 | 354 | int linenum = 0; |
59 | 354 | buffer *buf = NULL; |
60 | 354 | char* cfg_key; |
61 | 354 | char* cfg_val; |
62 | 354 | char* saveptr; |
63 | 354 | int in_host_section = 0; |
64 | | |
65 | 354 | DEBUG1(("Reading '%.200s'", filename)); |
66 | | |
67 | 354 | buf = buf_new(MAX_CONF_LINE); |
68 | 354 | line = buf->data; |
69 | 5.34k | while (buf_getline(buf, config_file) == DROPBEAR_SUCCESS) { |
70 | 5.17k | char* commentStart = NULL; |
71 | 5.17k | cfg_option cfg_opt; |
72 | 5.17k | int found; |
73 | 5.17k | size_t i; |
74 | | /* Update line number counter. */ |
75 | 5.17k | linenum++; |
76 | | |
77 | | /* Add nul terminator */ |
78 | 5.17k | if (buf->len == buf->size) { |
79 | 0 | dropbear_exit("Long line %s:%d", filename, linenum); |
80 | 0 | } |
81 | 5.17k | buf_setpos(buf, buf->len); |
82 | 5.17k | buf_putbyte(buf, '\0'); |
83 | 5.17k | buf_setpos(buf, 0); |
84 | | |
85 | 5.17k | commentStart = strchr(line, '#'); |
86 | 5.17k | if (NULL != commentStart) { |
87 | 206 | *commentStart = '\0'; /* Drop the comments. */ |
88 | 206 | } |
89 | | |
90 | 5.17k | cfg_key = strtok_r(line, TOKEN_CHARS, &saveptr); |
91 | 5.17k | if (NULL == cfg_key) { |
92 | 2.12k | continue; |
93 | 2.12k | } |
94 | | |
95 | 3.04k | found = 0; |
96 | 11.9k | for (i = 0; i < ARRAY_SIZE(config_options); i++) { |
97 | 11.8k | if (0 == strcasecmp(cfg_key, config_options[i].name)) { |
98 | 2.87k | cfg_opt = config_options[i].option; |
99 | 2.87k | found = 1; |
100 | 2.87k | break; |
101 | 2.87k | } |
102 | 11.8k | } |
103 | | |
104 | 3.04k | if (!found) { |
105 | 175 | dropbear_exit("Unsupported option %s at %s:%d", cfg_key, filename, linenum); |
106 | 175 | } |
107 | | |
108 | | |
109 | 2.87k | cfg_val = strtok_r(NULL, TOKEN_CHARS, &saveptr); |
110 | 2.87k | if (NULL == cfg_val) { |
111 | 10 | dropbear_exit("Missing value for %s at %s:%d", cfg_key, filename, linenum); |
112 | 10 | } |
113 | | |
114 | 2.86k | if (in_host_section) { |
115 | 2.19k | switch (cfg_opt) { |
116 | 1 | case opHost: { |
117 | | /* Hit the next host section. Done reading config. */ |
118 | 1 | goto outloop; |
119 | 0 | } |
120 | 195 | 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 | 195 | m_free(options->remotehost); |
125 | 195 | options->remotehost = m_strdup(cfg_val); |
126 | 195 | options->remotehostfixed = 1; /* Subsequent command line parsing should leave it alone. */ |
127 | 195 | break; |
128 | 0 | } |
129 | | |
130 | 335 | case opHostPort: { |
131 | 335 | m_free(options->remoteport); |
132 | 335 | options->remoteport = m_strdup(cfg_val); |
133 | 335 | break; |
134 | 0 | } |
135 | | |
136 | 459 | case opLoginUser: { |
137 | 459 | m_free(options->username); |
138 | 459 | options->username = m_strdup(cfg_val); |
139 | 459 | break; |
140 | 0 | } |
141 | | |
142 | 1.20k | case opIdentityFile: { |
143 | 1.20k | #if DROPBEAR_CLI_PUBKEY_AUTH |
144 | 1.20k | char* key_file_path; |
145 | 1.20k | if (strncmp(cfg_val, "~/", 2) == 0) { |
146 | 260 | key_file_path = expand_homedir_path(cfg_val); |
147 | 949 | } else if (cfg_val[0] != '/') { |
148 | 499 | char* config_dir = dirname(filename); |
149 | 499 | int path_len = strlen(config_dir) + strlen(cfg_val) + 10; |
150 | 499 | key_file_path = m_malloc(path_len); |
151 | 499 | snprintf(key_file_path, path_len, "%s/%s", config_dir, cfg_val); |
152 | 499 | } else { |
153 | 450 | key_file_path = m_strdup(cfg_val); |
154 | 450 | } |
155 | 1.20k | loadidentityfile(key_file_path, 1); |
156 | 1.20k | m_free(key_file_path); |
157 | | #else |
158 | | dropbear_exit("identityfile isn't supported in %s", filename); |
159 | | #endif |
160 | 1.20k | break; |
161 | 0 | } |
162 | 2.19k | } |
163 | 2.19k | } |
164 | 664 | else |
165 | 664 | { |
166 | 664 | if (opHost != cfg_opt || 0 != strcmp(cfg_val, options->remotehost)) { |
167 | | /* Not our host section. */ |
168 | 560 | continue; |
169 | 560 | } |
170 | 104 | in_host_section = 1; |
171 | 104 | } |
172 | 2.86k | } |
173 | 169 | outloop: |
174 | 169 | buf_free(buf); |
175 | 169 | } |
176 | | |
177 | | #endif /* DROPBEAR_USE_SSH_CONFIG */ |