For easier testability. And because everything that
reduces the length of that file in a sensible manner
is a good idea.
Change-Id: I18e38862df1318740928c6cfa21dc4dcd7d44b89
Signed-off-by: Frank Lichtenheld <frank@lichtenheld.com>
Acked-by: Gert Doering <gert@greenie.muc.de>
Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/1242
Message-Id: <20251007185110.19267-1-gert@greenie.muc.de>
URL: https://sourceforge.net/p/openvpn/mailman/message/59243506/
Signed-off-by: Gert Doering <gert@greenie.muc.de>
| ... | ... |
@@ -4838,7 +4838,7 @@ auth_retry_print(void) |
| 4838 | 4838 |
/* |
| 4839 | 4839 |
* Print the help message. |
| 4840 | 4840 |
*/ |
| 4841 |
-static void |
|
| 4841 |
+void |
|
| 4842 | 4842 |
usage(void) |
| 4843 | 4843 |
{
|
| 4844 | 4844 |
FILE *fp = msg_fp(0); |
| ... | ... |
@@ -4967,581 +4967,6 @@ atou(const char *str) |
| 4967 | 4967 |
} |
| 4968 | 4968 |
#endif |
| 4969 | 4969 |
|
| 4970 |
-static inline bool |
|
| 4971 |
-space(char c) |
|
| 4972 |
-{
|
|
| 4973 |
- return c == '\0' || isspace(c); |
|
| 4974 |
-} |
|
| 4975 |
- |
|
| 4976 |
-int |
|
| 4977 |
-parse_line(const char *line, char *p[], const int n, const char *file, const int line_num, |
|
| 4978 |
- msglvl_t msglevel, struct gc_arena *gc) |
|
| 4979 |
-{
|
|
| 4980 |
- const int STATE_INITIAL = 0; |
|
| 4981 |
- const int STATE_READING_QUOTED_PARM = 1; |
|
| 4982 |
- const int STATE_READING_UNQUOTED_PARM = 2; |
|
| 4983 |
- const int STATE_DONE = 3; |
|
| 4984 |
- const int STATE_READING_SQUOTED_PARM = 4; |
|
| 4985 |
- |
|
| 4986 |
- const char *error_prefix = ""; |
|
| 4987 |
- |
|
| 4988 |
- int ret = 0; |
|
| 4989 |
- const char *c = line; |
|
| 4990 |
- int state = STATE_INITIAL; |
|
| 4991 |
- bool backslash = false; |
|
| 4992 |
- char in, out; |
|
| 4993 |
- |
|
| 4994 |
- char parm[OPTION_PARM_SIZE]; |
|
| 4995 |
- unsigned int parm_len = 0; |
|
| 4996 |
- |
|
| 4997 |
- msglevel &= ~M_OPTERR; |
|
| 4998 |
- |
|
| 4999 |
- if (msglevel & M_MSG_VIRT_OUT) |
|
| 5000 |
- {
|
|
| 5001 |
- error_prefix = "ERROR: "; |
|
| 5002 |
- } |
|
| 5003 |
- |
|
| 5004 |
- do |
|
| 5005 |
- {
|
|
| 5006 |
- in = *c; |
|
| 5007 |
- out = 0; |
|
| 5008 |
- |
|
| 5009 |
- if (!backslash && in == '\\' && state != STATE_READING_SQUOTED_PARM) |
|
| 5010 |
- {
|
|
| 5011 |
- backslash = true; |
|
| 5012 |
- } |
|
| 5013 |
- else |
|
| 5014 |
- {
|
|
| 5015 |
- if (state == STATE_INITIAL) |
|
| 5016 |
- {
|
|
| 5017 |
- if (!space(in)) |
|
| 5018 |
- {
|
|
| 5019 |
- if (in == ';' || in == '#') /* comment */ |
|
| 5020 |
- {
|
|
| 5021 |
- break; |
|
| 5022 |
- } |
|
| 5023 |
- if (!backslash && in == '\"') |
|
| 5024 |
- {
|
|
| 5025 |
- state = STATE_READING_QUOTED_PARM; |
|
| 5026 |
- } |
|
| 5027 |
- else if (!backslash && in == '\'') |
|
| 5028 |
- {
|
|
| 5029 |
- state = STATE_READING_SQUOTED_PARM; |
|
| 5030 |
- } |
|
| 5031 |
- else |
|
| 5032 |
- {
|
|
| 5033 |
- out = in; |
|
| 5034 |
- state = STATE_READING_UNQUOTED_PARM; |
|
| 5035 |
- } |
|
| 5036 |
- } |
|
| 5037 |
- } |
|
| 5038 |
- else if (state == STATE_READING_UNQUOTED_PARM) |
|
| 5039 |
- {
|
|
| 5040 |
- if (!backslash && space(in)) |
|
| 5041 |
- {
|
|
| 5042 |
- state = STATE_DONE; |
|
| 5043 |
- } |
|
| 5044 |
- else |
|
| 5045 |
- {
|
|
| 5046 |
- out = in; |
|
| 5047 |
- } |
|
| 5048 |
- } |
|
| 5049 |
- else if (state == STATE_READING_QUOTED_PARM) |
|
| 5050 |
- {
|
|
| 5051 |
- if (!backslash && in == '\"') |
|
| 5052 |
- {
|
|
| 5053 |
- state = STATE_DONE; |
|
| 5054 |
- } |
|
| 5055 |
- else |
|
| 5056 |
- {
|
|
| 5057 |
- out = in; |
|
| 5058 |
- } |
|
| 5059 |
- } |
|
| 5060 |
- else if (state == STATE_READING_SQUOTED_PARM) |
|
| 5061 |
- {
|
|
| 5062 |
- if (in == '\'') |
|
| 5063 |
- {
|
|
| 5064 |
- state = STATE_DONE; |
|
| 5065 |
- } |
|
| 5066 |
- else |
|
| 5067 |
- {
|
|
| 5068 |
- out = in; |
|
| 5069 |
- } |
|
| 5070 |
- } |
|
| 5071 |
- if (state == STATE_DONE) |
|
| 5072 |
- {
|
|
| 5073 |
- /* ASSERT (parm_len > 0); */ |
|
| 5074 |
- p[ret] = gc_malloc(parm_len + 1, true, gc); |
|
| 5075 |
- memcpy(p[ret], parm, parm_len); |
|
| 5076 |
- p[ret][parm_len] = '\0'; |
|
| 5077 |
- state = STATE_INITIAL; |
|
| 5078 |
- parm_len = 0; |
|
| 5079 |
- ++ret; |
|
| 5080 |
- } |
|
| 5081 |
- |
|
| 5082 |
- if (backslash && out) |
|
| 5083 |
- {
|
|
| 5084 |
- if (!(out == '\\' || out == '\"' || space(out))) |
|
| 5085 |
- {
|
|
| 5086 |
-#ifdef ENABLE_SMALL |
|
| 5087 |
- msg(msglevel, "%sOptions warning: Bad backslash ('\\') usage in %s:%d",
|
|
| 5088 |
- error_prefix, file, line_num); |
|
| 5089 |
-#else |
|
| 5090 |
- msg(msglevel, |
|
| 5091 |
- "%sOptions warning: Bad backslash ('\\') usage in %s:%d: remember that backslashes are treated as shell-escapes and if you need to pass backslash characters as part of a Windows filename, you should use double backslashes such as \"c:\\\\" PACKAGE
|
|
| 5092 |
- "\\\\static.key\"", |
|
| 5093 |
- error_prefix, file, line_num); |
|
| 5094 |
-#endif |
|
| 5095 |
- return 0; |
|
| 5096 |
- } |
|
| 5097 |
- } |
|
| 5098 |
- backslash = false; |
|
| 5099 |
- } |
|
| 5100 |
- |
|
| 5101 |
- /* store parameter character */ |
|
| 5102 |
- if (out) |
|
| 5103 |
- {
|
|
| 5104 |
- if (parm_len >= SIZE(parm)) |
|
| 5105 |
- {
|
|
| 5106 |
- parm[SIZE(parm) - 1] = 0; |
|
| 5107 |
- msg(msglevel, "%sOptions error: Parameter at %s:%d is too long (%d chars max): %s", |
|
| 5108 |
- error_prefix, file, line_num, (int)SIZE(parm), parm); |
|
| 5109 |
- return 0; |
|
| 5110 |
- } |
|
| 5111 |
- parm[parm_len++] = out; |
|
| 5112 |
- } |
|
| 5113 |
- |
|
| 5114 |
- /* avoid overflow if too many parms in one config file line */ |
|
| 5115 |
- if (ret >= n) |
|
| 5116 |
- {
|
|
| 5117 |
- break; |
|
| 5118 |
- } |
|
| 5119 |
- |
|
| 5120 |
- } while (*c++ != '\0'); |
|
| 5121 |
- |
|
| 5122 |
- if (state == STATE_READING_QUOTED_PARM) |
|
| 5123 |
- {
|
|
| 5124 |
- msg(msglevel, "%sOptions error: No closing quotation (\") in %s:%d", error_prefix, file, |
|
| 5125 |
- line_num); |
|
| 5126 |
- return 0; |
|
| 5127 |
- } |
|
| 5128 |
- if (state == STATE_READING_SQUOTED_PARM) |
|
| 5129 |
- {
|
|
| 5130 |
- msg(msglevel, "%sOptions error: No closing single quotation (\') in %s:%d", error_prefix, |
|
| 5131 |
- file, line_num); |
|
| 5132 |
- return 0; |
|
| 5133 |
- } |
|
| 5134 |
- if (state != STATE_INITIAL) |
|
| 5135 |
- {
|
|
| 5136 |
- msg(msglevel, "%sOptions error: Residual parse state (%d) in %s:%d", error_prefix, state, |
|
| 5137 |
- file, line_num); |
|
| 5138 |
- return 0; |
|
| 5139 |
- } |
|
| 5140 |
-#if 0 |
|
| 5141 |
- {
|
|
| 5142 |
- int i; |
|
| 5143 |
- for (i = 0; i < ret; ++i) |
|
| 5144 |
- {
|
|
| 5145 |
- msg(M_INFO|M_NOPREFIX, "%s:%d ARG[%d] '%s'", file, line_num, i, p[i]); |
|
| 5146 |
- } |
|
| 5147 |
- } |
|
| 5148 |
-#endif |
|
| 5149 |
- return ret; |
|
| 5150 |
-} |
|
| 5151 |
- |
|
| 5152 |
-static void |
|
| 5153 |
-bypass_doubledash(char **p) |
|
| 5154 |
-{
|
|
| 5155 |
- if (strlen(*p) >= 3 && !strncmp(*p, "--", 2)) |
|
| 5156 |
- {
|
|
| 5157 |
- *p += 2; |
|
| 5158 |
- } |
|
| 5159 |
-} |
|
| 5160 |
- |
|
| 5161 |
-struct in_src |
|
| 5162 |
-{
|
|
| 5163 |
-#define IS_TYPE_FP 1 |
|
| 5164 |
-#define IS_TYPE_BUF 2 |
|
| 5165 |
- int type; |
|
| 5166 |
- union |
|
| 5167 |
- {
|
|
| 5168 |
- FILE *fp; |
|
| 5169 |
- struct buffer *multiline; |
|
| 5170 |
- } u; |
|
| 5171 |
-}; |
|
| 5172 |
- |
|
| 5173 |
-static bool |
|
| 5174 |
-in_src_get(const struct in_src *is, char *line, const int size) |
|
| 5175 |
-{
|
|
| 5176 |
- if (is->type == IS_TYPE_FP) |
|
| 5177 |
- {
|
|
| 5178 |
- return BOOL_CAST(fgets(line, size, is->u.fp)); |
|
| 5179 |
- } |
|
| 5180 |
- else if (is->type == IS_TYPE_BUF) |
|
| 5181 |
- {
|
|
| 5182 |
- bool status = buf_parse(is->u.multiline, '\n', line, size); |
|
| 5183 |
- if ((int)strlen(line) + 1 < size) |
|
| 5184 |
- {
|
|
| 5185 |
- strcat(line, "\n"); |
|
| 5186 |
- } |
|
| 5187 |
- return status; |
|
| 5188 |
- } |
|
| 5189 |
- else |
|
| 5190 |
- {
|
|
| 5191 |
- ASSERT(0); |
|
| 5192 |
- return false; |
|
| 5193 |
- } |
|
| 5194 |
-} |
|
| 5195 |
- |
|
| 5196 |
-static char * |
|
| 5197 |
-read_inline_file(struct in_src *is, const char *close_tag, int *num_lines, struct gc_arena *gc) |
|
| 5198 |
-{
|
|
| 5199 |
- char line[OPTION_LINE_SIZE]; |
|
| 5200 |
- struct buffer buf = alloc_buf(8 * OPTION_LINE_SIZE); |
|
| 5201 |
- char *ret; |
|
| 5202 |
- bool endtagfound = false; |
|
| 5203 |
- |
|
| 5204 |
- while (in_src_get(is, line, sizeof(line))) |
|
| 5205 |
- {
|
|
| 5206 |
- (*num_lines)++; |
|
| 5207 |
- char *line_ptr = line; |
|
| 5208 |
- /* Remove leading spaces */ |
|
| 5209 |
- while (isspace(*line_ptr)) |
|
| 5210 |
- {
|
|
| 5211 |
- line_ptr++; |
|
| 5212 |
- } |
|
| 5213 |
- if (!strncmp(line_ptr, close_tag, strlen(close_tag))) |
|
| 5214 |
- {
|
|
| 5215 |
- endtagfound = true; |
|
| 5216 |
- break; |
|
| 5217 |
- } |
|
| 5218 |
- if (!buf_safe(&buf, strlen(line) + 1)) |
|
| 5219 |
- {
|
|
| 5220 |
- /* Increase buffer size */ |
|
| 5221 |
- struct buffer buf2 = alloc_buf(buf.capacity * 2); |
|
| 5222 |
- ASSERT(buf_copy(&buf2, &buf)); |
|
| 5223 |
- buf_clear(&buf); |
|
| 5224 |
- free_buf(&buf); |
|
| 5225 |
- buf = buf2; |
|
| 5226 |
- } |
|
| 5227 |
- buf_printf(&buf, "%s", line); |
|
| 5228 |
- } |
|
| 5229 |
- if (!endtagfound) |
|
| 5230 |
- {
|
|
| 5231 |
- msg(M_FATAL, "ERROR: Endtag %s missing", close_tag); |
|
| 5232 |
- } |
|
| 5233 |
- ret = string_alloc(BSTR(&buf), gc); |
|
| 5234 |
- buf_clear(&buf); |
|
| 5235 |
- free_buf(&buf); |
|
| 5236 |
- secure_memzero(line, sizeof(line)); |
|
| 5237 |
- return ret; |
|
| 5238 |
-} |
|
| 5239 |
- |
|
| 5240 |
-static int |
|
| 5241 |
-check_inline_file(struct in_src *is, char *p[], struct gc_arena *gc) |
|
| 5242 |
-{
|
|
| 5243 |
- int num_inline_lines = 0; |
|
| 5244 |
- |
|
| 5245 |
- if (p[0] && !p[1]) |
|
| 5246 |
- {
|
|
| 5247 |
- char *arg = p[0]; |
|
| 5248 |
- if (arg[0] == '<' && arg[strlen(arg) - 1] == '>') |
|
| 5249 |
- {
|
|
| 5250 |
- struct buffer close_tag; |
|
| 5251 |
- |
|
| 5252 |
- arg[strlen(arg) - 1] = '\0'; |
|
| 5253 |
- p[0] = string_alloc(arg + 1, gc); |
|
| 5254 |
- close_tag = alloc_buf(strlen(p[0]) + 4); |
|
| 5255 |
- buf_printf(&close_tag, "</%s>", p[0]); |
|
| 5256 |
- p[1] = read_inline_file(is, BSTR(&close_tag), &num_inline_lines, gc); |
|
| 5257 |
- p[2] = NULL; |
|
| 5258 |
- free_buf(&close_tag); |
|
| 5259 |
- } |
|
| 5260 |
- } |
|
| 5261 |
- return num_inline_lines; |
|
| 5262 |
-} |
|
| 5263 |
- |
|
| 5264 |
-static int |
|
| 5265 |
-check_inline_file_via_fp(FILE *fp, char *p[], struct gc_arena *gc) |
|
| 5266 |
-{
|
|
| 5267 |
- struct in_src is; |
|
| 5268 |
- is.type = IS_TYPE_FP; |
|
| 5269 |
- is.u.fp = fp; |
|
| 5270 |
- return check_inline_file(&is, p, gc); |
|
| 5271 |
-} |
|
| 5272 |
- |
|
| 5273 |
-static int |
|
| 5274 |
-check_inline_file_via_buf(struct buffer *multiline, char *p[], struct gc_arena *gc) |
|
| 5275 |
-{
|
|
| 5276 |
- struct in_src is; |
|
| 5277 |
- is.type = IS_TYPE_BUF; |
|
| 5278 |
- is.u.multiline = multiline; |
|
| 5279 |
- return check_inline_file(&is, p, gc); |
|
| 5280 |
-} |
|
| 5281 |
- |
|
| 5282 |
-static void add_option(struct options *options, char *p[], bool is_inline, const char *file, |
|
| 5283 |
- int line, const int level, const msglvl_t msglevel, |
|
| 5284 |
- const unsigned int permission_mask, unsigned int *option_types_found, |
|
| 5285 |
- struct env_set *es); |
|
| 5286 |
- |
|
| 5287 |
-static void remove_option(struct context *c, struct options *options, char *p[], bool is_inline, |
|
| 5288 |
- const char *file, int line, const msglvl_t msglevel, |
|
| 5289 |
- const unsigned int permission_mask, unsigned int *option_types_found, |
|
| 5290 |
- struct env_set *es); |
|
| 5291 |
- |
|
| 5292 |
-static void update_option(struct context *c, struct options *options, char *p[], bool is_inline, |
|
| 5293 |
- const char *file, int line, const int level, const msglvl_t msglevel, |
|
| 5294 |
- const unsigned int permission_mask, unsigned int *option_types_found, |
|
| 5295 |
- struct env_set *es, unsigned int *update_options_found); |
|
| 5296 |
- |
|
| 5297 |
-static void |
|
| 5298 |
-read_config_file(struct options *options, const char *file, int level, const char *top_file, |
|
| 5299 |
- const int top_line, const msglvl_t msglevel, |
|
| 5300 |
- const unsigned int permission_mask, unsigned int *option_types_found, |
|
| 5301 |
- struct env_set *es) |
|
| 5302 |
-{
|
|
| 5303 |
- const int max_recursive_levels = 10; |
|
| 5304 |
- FILE *fp; |
|
| 5305 |
- int line_num; |
|
| 5306 |
- char line[OPTION_LINE_SIZE + 1]; |
|
| 5307 |
- char *p[MAX_PARMS + 1]; |
|
| 5308 |
- |
|
| 5309 |
- ++level; |
|
| 5310 |
- if (level <= max_recursive_levels) |
|
| 5311 |
- {
|
|
| 5312 |
- if (streq(file, "stdin")) |
|
| 5313 |
- {
|
|
| 5314 |
- fp = stdin; |
|
| 5315 |
- } |
|
| 5316 |
- else |
|
| 5317 |
- {
|
|
| 5318 |
- fp = platform_fopen(file, "r"); |
|
| 5319 |
- } |
|
| 5320 |
- if (fp) |
|
| 5321 |
- {
|
|
| 5322 |
- line_num = 0; |
|
| 5323 |
- while (fgets(line, sizeof(line), fp)) |
|
| 5324 |
- {
|
|
| 5325 |
- int offset = 0; |
|
| 5326 |
- CLEAR(p); |
|
| 5327 |
- ++line_num; |
|
| 5328 |
- if (strlen(line) == OPTION_LINE_SIZE) |
|
| 5329 |
- {
|
|
| 5330 |
- msg(msglevel, |
|
| 5331 |
- "In %s:%d: Maximum option line length (%d) exceeded, line starts with %s", |
|
| 5332 |
- file, line_num, OPTION_LINE_SIZE, line); |
|
| 5333 |
- } |
|
| 5334 |
- |
|
| 5335 |
- /* Ignore UTF-8 BOM at start of stream */ |
|
| 5336 |
- if (line_num == 1 && strncmp(line, "\xEF\xBB\xBF", 3) == 0) |
|
| 5337 |
- {
|
|
| 5338 |
- offset = 3; |
|
| 5339 |
- } |
|
| 5340 |
- if (parse_line(line + offset, p, SIZE(p) - 1, file, line_num, msglevel, |
|
| 5341 |
- &options->gc)) |
|
| 5342 |
- {
|
|
| 5343 |
- bypass_doubledash(&p[0]); |
|
| 5344 |
- int lines_inline = check_inline_file_via_fp(fp, p, &options->gc); |
|
| 5345 |
- add_option(options, p, lines_inline, file, line_num, level, msglevel, |
|
| 5346 |
- permission_mask, option_types_found, es); |
|
| 5347 |
- line_num += lines_inline; |
|
| 5348 |
- } |
|
| 5349 |
- } |
|
| 5350 |
- if (fp != stdin) |
|
| 5351 |
- {
|
|
| 5352 |
- fclose(fp); |
|
| 5353 |
- } |
|
| 5354 |
- } |
|
| 5355 |
- else |
|
| 5356 |
- {
|
|
| 5357 |
- msg(msglevel, "In %s:%d: Error opening configuration file: %s", top_file, top_line, |
|
| 5358 |
- file); |
|
| 5359 |
- } |
|
| 5360 |
- } |
|
| 5361 |
- else |
|
| 5362 |
- {
|
|
| 5363 |
- msg(msglevel, |
|
| 5364 |
- "In %s:%d: Maximum recursive include levels exceeded in include attempt of file %s -- probably you have a configuration file that tries to include itself.", |
|
| 5365 |
- top_file, top_line, file); |
|
| 5366 |
- } |
|
| 5367 |
- secure_memzero(line, sizeof(line)); |
|
| 5368 |
- CLEAR(p); |
|
| 5369 |
-} |
|
| 5370 |
- |
|
| 5371 |
-static void |
|
| 5372 |
-read_config_string(const char *prefix, struct options *options, const char *config, |
|
| 5373 |
- const msglvl_t msglevel, const unsigned int permission_mask, |
|
| 5374 |
- unsigned int *option_types_found, struct env_set *es) |
|
| 5375 |
-{
|
|
| 5376 |
- char line[OPTION_LINE_SIZE]; |
|
| 5377 |
- struct buffer multiline; |
|
| 5378 |
- int line_num = 0; |
|
| 5379 |
- |
|
| 5380 |
- buf_set_read(&multiline, (uint8_t *)config, strlen(config)); |
|
| 5381 |
- |
|
| 5382 |
- while (buf_parse(&multiline, '\n', line, sizeof(line))) |
|
| 5383 |
- {
|
|
| 5384 |
- char *p[MAX_PARMS + 1]; |
|
| 5385 |
- CLEAR(p); |
|
| 5386 |
- ++line_num; |
|
| 5387 |
- if (parse_line(line, p, SIZE(p) - 1, prefix, line_num, msglevel, &options->gc)) |
|
| 5388 |
- {
|
|
| 5389 |
- bypass_doubledash(&p[0]); |
|
| 5390 |
- int lines_inline = check_inline_file_via_buf(&multiline, p, &options->gc); |
|
| 5391 |
- add_option(options, p, lines_inline, prefix, line_num, 0, msglevel, permission_mask, |
|
| 5392 |
- option_types_found, es); |
|
| 5393 |
- line_num += lines_inline; |
|
| 5394 |
- } |
|
| 5395 |
- CLEAR(p); |
|
| 5396 |
- } |
|
| 5397 |
- secure_memzero(line, sizeof(line)); |
|
| 5398 |
-} |
|
| 5399 |
- |
|
| 5400 |
-void |
|
| 5401 |
-parse_argv(struct options *options, const int argc, char *argv[], const msglvl_t msglevel, |
|
| 5402 |
- const unsigned int permission_mask, unsigned int *option_types_found, struct env_set *es) |
|
| 5403 |
-{
|
|
| 5404 |
- /* usage message */ |
|
| 5405 |
- if (argc <= 1) |
|
| 5406 |
- {
|
|
| 5407 |
- usage(); |
|
| 5408 |
- } |
|
| 5409 |
- |
|
| 5410 |
- /* config filename specified only? */ |
|
| 5411 |
- if (argc == 2 && strncmp(argv[1], "--", 2)) |
|
| 5412 |
- {
|
|
| 5413 |
- char *p[MAX_PARMS + 1]; |
|
| 5414 |
- CLEAR(p); |
|
| 5415 |
- p[0] = "config"; |
|
| 5416 |
- p[1] = argv[1]; |
|
| 5417 |
- add_option(options, p, false, NULL, 0, 0, msglevel, permission_mask, option_types_found, |
|
| 5418 |
- es); |
|
| 5419 |
- } |
|
| 5420 |
- else |
|
| 5421 |
- {
|
|
| 5422 |
- /* parse command line */ |
|
| 5423 |
- for (int i = 1; i < argc; ++i) |
|
| 5424 |
- {
|
|
| 5425 |
- char *p[MAX_PARMS + 1]; |
|
| 5426 |
- CLEAR(p); |
|
| 5427 |
- p[0] = argv[i]; |
|
| 5428 |
- if (strncmp(p[0], "--", 2)) |
|
| 5429 |
- {
|
|
| 5430 |
- msg(msglevel, |
|
| 5431 |
- "I'm trying to parse \"%s\" as an --option parameter but I don't see a leading '--'", |
|
| 5432 |
- p[0]); |
|
| 5433 |
- } |
|
| 5434 |
- else |
|
| 5435 |
- {
|
|
| 5436 |
- p[0] += 2; |
|
| 5437 |
- } |
|
| 5438 |
- |
|
| 5439 |
- int j; |
|
| 5440 |
- for (j = 1; j < MAX_PARMS; ++j) |
|
| 5441 |
- {
|
|
| 5442 |
- if (i + j < argc) |
|
| 5443 |
- {
|
|
| 5444 |
- char *arg = argv[i + j]; |
|
| 5445 |
- if (strncmp(arg, "--", 2)) |
|
| 5446 |
- {
|
|
| 5447 |
- p[j] = arg; |
|
| 5448 |
- } |
|
| 5449 |
- else |
|
| 5450 |
- {
|
|
| 5451 |
- break; |
|
| 5452 |
- } |
|
| 5453 |
- } |
|
| 5454 |
- } |
|
| 5455 |
- add_option(options, p, false, NULL, 0, 0, msglevel, permission_mask, option_types_found, |
|
| 5456 |
- es); |
|
| 5457 |
- i += j - 1; |
|
| 5458 |
- } |
|
| 5459 |
- } |
|
| 5460 |
-} |
|
| 5461 |
- |
|
| 5462 |
-bool |
|
| 5463 |
-apply_push_options(struct context *c, struct options *options, struct buffer *buf, |
|
| 5464 |
- unsigned int permission_mask, unsigned int *option_types_found, |
|
| 5465 |
- struct env_set *es, bool is_update) |
|
| 5466 |
-{
|
|
| 5467 |
- char line[OPTION_PARM_SIZE]; |
|
| 5468 |
- int line_num = 0; |
|
| 5469 |
- const char *file = "[PUSH-OPTIONS]"; |
|
| 5470 |
- const msglvl_t msglevel = D_PUSH_ERRORS | M_OPTERR; |
|
| 5471 |
- unsigned int update_options_found = 0; |
|
| 5472 |
- |
|
| 5473 |
- while (buf_parse(buf, ',', line, sizeof(line))) |
|
| 5474 |
- {
|
|
| 5475 |
- char *p[MAX_PARMS + 1]; |
|
| 5476 |
- CLEAR(p); |
|
| 5477 |
- ++line_num; |
|
| 5478 |
- unsigned int push_update_option_flags = 0; |
|
| 5479 |
- int i = 0; |
|
| 5480 |
- |
|
| 5481 |
- /* skip leading spaces matching the behaviour of parse_line */ |
|
| 5482 |
- while (isspace(line[i])) |
|
| 5483 |
- {
|
|
| 5484 |
- i++; |
|
| 5485 |
- } |
|
| 5486 |
- |
|
| 5487 |
- /* If we are not in a 'PUSH_UPDATE' we just check `apply_pull_filter()` |
|
| 5488 |
- * otherwise we must call `check_push_update_option_flags()` first |
|
| 5489 |
- */ |
|
| 5490 |
- if ((is_update && !check_push_update_option_flags(line, &i, &push_update_option_flags)) |
|
| 5491 |
- || !apply_pull_filter(options, &line[i])) |
|
| 5492 |
- {
|
|
| 5493 |
- /* In case we are in a `PUSH_UPDATE` and `check_push_update_option_flags()` |
|
| 5494 |
- * or `apply_pull_filter()` fail but the option is flagged by `PUSH_OPT_OPTIONAL`, |
|
| 5495 |
- * instead of restarting, we just ignore the option and we process the next one |
|
| 5496 |
- */ |
|
| 5497 |
- if (push_update_option_flags & PUSH_OPT_OPTIONAL) |
|
| 5498 |
- {
|
|
| 5499 |
- continue; /* Ignoring this option */ |
|
| 5500 |
- } |
|
| 5501 |
- return false; /* Cause push/pull error and stop push processing */ |
|
| 5502 |
- } |
|
| 5503 |
- |
|
| 5504 |
- if (parse_line(&line[i], p, SIZE(p) - 1, file, line_num, msglevel, &options->gc)) |
|
| 5505 |
- {
|
|
| 5506 |
- if (!is_update) |
|
| 5507 |
- {
|
|
| 5508 |
- add_option(options, p, false, file, line_num, 0, msglevel, permission_mask, |
|
| 5509 |
- option_types_found, es); |
|
| 5510 |
- } |
|
| 5511 |
- else if (push_update_option_flags & PUSH_OPT_TO_REMOVE) |
|
| 5512 |
- {
|
|
| 5513 |
- remove_option(c, options, p, false, file, line_num, msglevel, permission_mask, |
|
| 5514 |
- option_types_found, es); |
|
| 5515 |
- } |
|
| 5516 |
- else |
|
| 5517 |
- {
|
|
| 5518 |
- update_option(c, options, p, false, file, line_num, 0, msglevel, permission_mask, |
|
| 5519 |
- option_types_found, es, &update_options_found); |
|
| 5520 |
- } |
|
| 5521 |
- } |
|
| 5522 |
- } |
|
| 5523 |
- return true; |
|
| 5524 |
-} |
|
| 5525 |
- |
|
| 5526 |
-void |
|
| 5527 |
-options_server_import(struct options *o, const char *filename, msglvl_t msglevel, |
|
| 5528 |
- unsigned int permission_mask, unsigned int *option_types_found, |
|
| 5529 |
- struct env_set *es) |
|
| 5530 |
-{
|
|
| 5531 |
- msg(D_PUSH, "OPTIONS IMPORT: reading client specific options from: %s", filename); |
|
| 5532 |
- read_config_file(o, filename, 0, filename, 0, msglevel, permission_mask, option_types_found, |
|
| 5533 |
- es); |
|
| 5534 |
-} |
|
| 5535 |
- |
|
| 5536 |
-void |
|
| 5537 |
-options_string_import(struct options *options, const char *config, const msglvl_t msglevel, |
|
| 5538 |
- const unsigned int permission_mask, unsigned int *option_types_found, |
|
| 5539 |
- struct env_set *es) |
|
| 5540 |
-{
|
|
| 5541 |
- read_config_string("[CONFIG-STRING]", options, config, msglevel, permission_mask,
|
|
| 5542 |
- option_types_found, es); |
|
| 5543 |
-} |
|
| 5544 |
- |
|
| 5545 | 4970 |
#define VERIFY_PERMISSION(mask) \ |
| 5546 | 4971 |
{ \
|
| 5547 | 4972 |
if (!verify_permission(p[0], file, line, (mask), permission_mask, option_types_found, \ |
| ... | ... |
@@ -5642,27 +5067,7 @@ msglevel_forward_compatible(struct options *options, const msglvl_t msglevel) |
| 5642 | 5642 |
option_ptr->flags = 0; \ |
| 5643 | 5643 |
} |
| 5644 | 5644 |
|
| 5645 |
-/** |
|
| 5646 |
- * @brief Resets options found in the PUSH_UPDATE message that are preceded by the `-` flag. |
|
| 5647 |
- * This function is used in push-updates to reset specified options. |
|
| 5648 |
- * The number of parameters `p` must always be 1. If the permission is verified, |
|
| 5649 |
- * all related options are erased or reset to their default values. |
|
| 5650 |
- * Upon successful permission verification (by VERIFY_PERMISSION()), |
|
| 5651 |
- * `option_types_found` is filled with the flag corresponding to the option. |
|
| 5652 |
- * |
|
| 5653 |
- * @param c The context structure. |
|
| 5654 |
- * @param options A pointer to the options structure. |
|
| 5655 |
- * @param p An array of strings containing the options and their parameters. |
|
| 5656 |
- * @param is_inline A boolean indicating if the option is inline. |
|
| 5657 |
- * @param file The file where the function is called. |
|
| 5658 |
- * @param line The line number where the function is called. |
|
| 5659 |
- * @param msglevel The message level. |
|
| 5660 |
- * @param permission_mask The permission mask used by VERIFY_PERMISSION(). |
|
| 5661 |
- * @param option_types_found A pointer to the variable where the flags corresponding to the options |
|
| 5662 |
- * found are stored. |
|
| 5663 |
- * @param es The environment set structure. |
|
| 5664 |
- */ |
|
| 5665 |
-static void |
|
| 5645 |
+void |
|
| 5666 | 5646 |
remove_option(struct context *c, struct options *options, char *p[], bool is_inline, |
| 5667 | 5647 |
const char *file, int line, const msglvl_t msglevel, |
| 5668 | 5648 |
const unsigned int permission_mask, unsigned int *option_types_found, |
| ... | ... |
@@ -5982,30 +5387,7 @@ check_dns_option(struct options *options, char *p[], const msglvl_t msglevel, bo |
| 5982 | 5982 |
return true; |
| 5983 | 5983 |
} |
| 5984 | 5984 |
|
| 5985 |
-/** |
|
| 5986 |
- * @brief Processes an option to update. It first checks whether it has already |
|
| 5987 |
- * received an option of the same type within the same update message. |
|
| 5988 |
- * If the option has already been received, it calls add_option(). |
|
| 5989 |
- * Otherwise, it deletes all existing values related to that option before calling |
|
| 5990 |
- * add_option(). |
|
| 5991 |
- * |
|
| 5992 |
- * @param c The context structure. |
|
| 5993 |
- * @param options A pointer to the options structure. |
|
| 5994 |
- * @param p An array of strings containing the options and their parameters. |
|
| 5995 |
- * @param is_inline A boolean indicating if the option is inline. |
|
| 5996 |
- * @param file The file where the function is called. |
|
| 5997 |
- * @param line The line number where the function is called. |
|
| 5998 |
- * @param level The level of the option. |
|
| 5999 |
- * @param msglevel The message level for logging. |
|
| 6000 |
- * @param permission_mask The permission mask used by VERIFY_PERMISSION(). |
|
| 6001 |
- * @param option_types_found A pointer to the variable where the flags corresponding to the options |
|
| 6002 |
- * found are stored. |
|
| 6003 |
- * @param es The environment set structure. |
|
| 6004 |
- * @param update_options_found A pointer to the variable where the flags corresponding to the update |
|
| 6005 |
- * options found are stored, used to check if an option of the same type has already been processed |
|
| 6006 |
- * by update_option() within the same push-update message. |
|
| 6007 |
- */ |
|
| 6008 |
-static void |
|
| 5985 |
+void |
|
| 6009 | 5986 |
update_option(struct context *c, struct options *options, char *p[], bool is_inline, |
| 6010 | 5987 |
const char *file, int line, const int level, const msglvl_t msglevel, |
| 6011 | 5988 |
const unsigned int permission_mask, unsigned int *option_types_found, |
| ... | ... |
@@ -6190,7 +5572,7 @@ key_is_external(const struct options *options) |
| 6190 | 6190 |
return ret; |
| 6191 | 6191 |
} |
| 6192 | 6192 |
|
| 6193 |
-static void |
|
| 6193 |
+void |
|
| 6194 | 6194 |
add_option(struct options *options, char *p[], bool is_inline, const char *file, int line, |
| 6195 | 6195 |
const int level, const msglvl_t msglevel, const unsigned int permission_mask, |
| 6196 | 6196 |
unsigned int *option_types_found, struct env_set *es) |
| ... | ... |
@@ -822,14 +822,83 @@ struct pull_filter_list |
| 822 | 822 |
struct pull_filter *tail; |
| 823 | 823 |
}; |
| 824 | 824 |
|
| 825 |
+void add_option(struct options *options, char *p[], bool is_inline, const char *file, |
|
| 826 |
+ int line, const int level, const msglvl_t msglevel, |
|
| 827 |
+ const unsigned int permission_mask, unsigned int *option_types_found, |
|
| 828 |
+ struct env_set *es); |
|
| 829 |
+ |
|
| 830 |
+/** |
|
| 831 |
+ * @brief Resets options found in the PUSH_UPDATE message that are preceded by the `-` flag. |
|
| 832 |
+ * This function is used in push-updates to reset specified options. |
|
| 833 |
+ * The number of parameters `p` must always be 1. If the permission is verified, |
|
| 834 |
+ * all related options are erased or reset to their default values. |
|
| 835 |
+ * Upon successful permission verification (by VERIFY_PERMISSION()), |
|
| 836 |
+ * `option_types_found` is filled with the flag corresponding to the option. |
|
| 837 |
+ * |
|
| 838 |
+ * @param c The context structure. |
|
| 839 |
+ * @param options A pointer to the options structure. |
|
| 840 |
+ * @param p An array of strings containing the options and their parameters. |
|
| 841 |
+ * @param is_inline A boolean indicating if the option is inline. |
|
| 842 |
+ * @param file The file where the function is called. |
|
| 843 |
+ * @param line The line number where the function is called. |
|
| 844 |
+ * @param msglevel The message level. |
|
| 845 |
+ * @param permission_mask The permission mask used by VERIFY_PERMISSION(). |
|
| 846 |
+ * @param option_types_found A pointer to the variable where the flags corresponding to the options |
|
| 847 |
+ * found are stored. |
|
| 848 |
+ * @param es The environment set structure. |
|
| 849 |
+ */ |
|
| 850 |
+void remove_option(struct context *c, struct options *options, char *p[], bool is_inline, |
|
| 851 |
+ const char *file, int line, const msglvl_t msglevel, |
|
| 852 |
+ const unsigned int permission_mask, unsigned int *option_types_found, |
|
| 853 |
+ struct env_set *es); |
|
| 854 |
+ |
|
| 855 |
+/** |
|
| 856 |
+ * @brief Processes an option to update. It first checks whether it has already |
|
| 857 |
+ * received an option of the same type within the same update message. |
|
| 858 |
+ * If the option has already been received, it calls add_option(). |
|
| 859 |
+ * Otherwise, it deletes all existing values related to that option before calling |
|
| 860 |
+ * add_option(). |
|
| 861 |
+ * |
|
| 862 |
+ * @param c The context structure. |
|
| 863 |
+ * @param options A pointer to the options structure. |
|
| 864 |
+ * @param p An array of strings containing the options and their parameters. |
|
| 865 |
+ * @param is_inline A boolean indicating if the option is inline. |
|
| 866 |
+ * @param file The file where the function is called. |
|
| 867 |
+ * @param line The line number where the function is called. |
|
| 868 |
+ * @param level The level of the option. |
|
| 869 |
+ * @param msglevel The message level for logging. |
|
| 870 |
+ * @param permission_mask The permission mask used by VERIFY_PERMISSION(). |
|
| 871 |
+ * @param option_types_found A pointer to the variable where the flags corresponding to the options |
|
| 872 |
+ * found are stored. |
|
| 873 |
+ * @param es The environment set structure. |
|
| 874 |
+ * @param update_options_found A pointer to the variable where the flags corresponding to the update |
|
| 875 |
+ * options found are stored, used to check if an option of the same type has already been processed |
|
| 876 |
+ * by update_option() within the same push-update message. |
|
| 877 |
+ */ |
|
| 878 |
+void update_option(struct context *c, struct options *options, char *p[], bool is_inline, |
|
| 879 |
+ const char *file, int line, const int level, const msglvl_t msglevel, |
|
| 880 |
+ const unsigned int permission_mask, unsigned int *option_types_found, |
|
| 881 |
+ struct env_set *es, unsigned int *update_options_found); |
|
| 882 |
+ |
|
| 825 | 883 |
void parse_argv(struct options *options, const int argc, char *argv[], const msglvl_t msglevel, |
| 826 | 884 |
const unsigned int permission_mask, unsigned int *option_types_found, |
| 827 | 885 |
struct env_set *es); |
| 828 | 886 |
|
| 887 |
+void read_config_file(struct options *options, const char *file, int level, const char *top_file, |
|
| 888 |
+ const int top_line, const msglvl_t msglevel, |
|
| 889 |
+ const unsigned int permission_mask, unsigned int *option_types_found, |
|
| 890 |
+ struct env_set *es); |
|
| 891 |
+ |
|
| 892 |
+void read_config_string(const char *prefix, struct options *options, const char *config, |
|
| 893 |
+ const msglvl_t msglevel, const unsigned int permission_mask, |
|
| 894 |
+ unsigned int *option_types_found, struct env_set *es); |
|
| 895 |
+ |
|
| 829 | 896 |
void notnull(const char *arg, const char *description); |
| 830 | 897 |
|
| 831 | 898 |
void usage_small(void); |
| 832 | 899 |
|
| 900 |
+void usage(void); |
|
| 901 |
+ |
|
| 833 | 902 |
void show_library_versions(const unsigned int flags); |
| 834 | 903 |
|
| 835 | 904 |
#ifdef _WIN32 |
| 836 | 905 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,592 @@ |
| 0 |
+/* |
|
| 1 |
+ * OpenVPN -- An application to securely tunnel IP networks |
|
| 2 |
+ * over a single UDP port, with support for SSL/TLS-based |
|
| 3 |
+ * session authentication and key exchange, |
|
| 4 |
+ * packet encryption, packet authentication, and |
|
| 5 |
+ * packet compression. |
|
| 6 |
+ * |
|
| 7 |
+ * Copyright (C) 2002-2025 OpenVPN Inc <sales@openvpn.net> |
|
| 8 |
+ * Copyright (C) 2008-2025 David Sommerseth <dazo@eurephia.org> |
|
| 9 |
+ * |
|
| 10 |
+ * This program is free software; you can redistribute it and/or modify |
|
| 11 |
+ * it under the terms of the GNU General Public License version 2 |
|
| 12 |
+ * as published by the Free Software Foundation. |
|
| 13 |
+ * |
|
| 14 |
+ * This program is distributed in the hope that it will be useful, |
|
| 15 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 16 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 17 |
+ * GNU General Public License for more details. |
|
| 18 |
+ * |
|
| 19 |
+ * You should have received a copy of the GNU General Public License along |
|
| 20 |
+ * with this program; if not, see <https://www.gnu.org/licenses/>. |
|
| 21 |
+ */ |
|
| 22 |
+ |
|
| 23 |
+#ifdef HAVE_CONFIG_H |
|
| 24 |
+#include "config.h" |
|
| 25 |
+#endif |
|
| 26 |
+ |
|
| 27 |
+#include <string.h> |
|
| 28 |
+ |
|
| 29 |
+#include "options.h" |
|
| 30 |
+#include "options_util.h" |
|
| 31 |
+#include "push.h" |
|
| 32 |
+ |
|
| 33 |
+static void |
|
| 34 |
+bypass_doubledash(char **p) |
|
| 35 |
+{
|
|
| 36 |
+ if (strlen(*p) >= 3 && !strncmp(*p, "--", 2)) |
|
| 37 |
+ {
|
|
| 38 |
+ *p += 2; |
|
| 39 |
+ } |
|
| 40 |
+} |
|
| 41 |
+ |
|
| 42 |
+static inline bool |
|
| 43 |
+space(char c) |
|
| 44 |
+{
|
|
| 45 |
+ return c == '\0' || isspace(c); |
|
| 46 |
+} |
|
| 47 |
+ |
|
| 48 |
+int |
|
| 49 |
+parse_line(const char *line, char *p[], const int n, const char *file, const int line_num, |
|
| 50 |
+ msglvl_t msglevel, struct gc_arena *gc) |
|
| 51 |
+{
|
|
| 52 |
+ const int STATE_INITIAL = 0; |
|
| 53 |
+ const int STATE_READING_QUOTED_PARM = 1; |
|
| 54 |
+ const int STATE_READING_UNQUOTED_PARM = 2; |
|
| 55 |
+ const int STATE_DONE = 3; |
|
| 56 |
+ const int STATE_READING_SQUOTED_PARM = 4; |
|
| 57 |
+ |
|
| 58 |
+ const char *error_prefix = ""; |
|
| 59 |
+ |
|
| 60 |
+ int ret = 0; |
|
| 61 |
+ const char *c = line; |
|
| 62 |
+ int state = STATE_INITIAL; |
|
| 63 |
+ bool backslash = false; |
|
| 64 |
+ char in, out; |
|
| 65 |
+ |
|
| 66 |
+ char parm[OPTION_PARM_SIZE]; |
|
| 67 |
+ unsigned int parm_len = 0; |
|
| 68 |
+ |
|
| 69 |
+ msglevel &= ~M_OPTERR; |
|
| 70 |
+ |
|
| 71 |
+ if (msglevel & M_MSG_VIRT_OUT) |
|
| 72 |
+ {
|
|
| 73 |
+ error_prefix = "ERROR: "; |
|
| 74 |
+ } |
|
| 75 |
+ |
|
| 76 |
+ do |
|
| 77 |
+ {
|
|
| 78 |
+ in = *c; |
|
| 79 |
+ out = 0; |
|
| 80 |
+ |
|
| 81 |
+ if (!backslash && in == '\\' && state != STATE_READING_SQUOTED_PARM) |
|
| 82 |
+ {
|
|
| 83 |
+ backslash = true; |
|
| 84 |
+ } |
|
| 85 |
+ else |
|
| 86 |
+ {
|
|
| 87 |
+ if (state == STATE_INITIAL) |
|
| 88 |
+ {
|
|
| 89 |
+ if (!space(in)) |
|
| 90 |
+ {
|
|
| 91 |
+ if (in == ';' || in == '#') /* comment */ |
|
| 92 |
+ {
|
|
| 93 |
+ break; |
|
| 94 |
+ } |
|
| 95 |
+ if (!backslash && in == '\"') |
|
| 96 |
+ {
|
|
| 97 |
+ state = STATE_READING_QUOTED_PARM; |
|
| 98 |
+ } |
|
| 99 |
+ else if (!backslash && in == '\'') |
|
| 100 |
+ {
|
|
| 101 |
+ state = STATE_READING_SQUOTED_PARM; |
|
| 102 |
+ } |
|
| 103 |
+ else |
|
| 104 |
+ {
|
|
| 105 |
+ out = in; |
|
| 106 |
+ state = STATE_READING_UNQUOTED_PARM; |
|
| 107 |
+ } |
|
| 108 |
+ } |
|
| 109 |
+ } |
|
| 110 |
+ else if (state == STATE_READING_UNQUOTED_PARM) |
|
| 111 |
+ {
|
|
| 112 |
+ if (!backslash && space(in)) |
|
| 113 |
+ {
|
|
| 114 |
+ state = STATE_DONE; |
|
| 115 |
+ } |
|
| 116 |
+ else |
|
| 117 |
+ {
|
|
| 118 |
+ out = in; |
|
| 119 |
+ } |
|
| 120 |
+ } |
|
| 121 |
+ else if (state == STATE_READING_QUOTED_PARM) |
|
| 122 |
+ {
|
|
| 123 |
+ if (!backslash && in == '\"') |
|
| 124 |
+ {
|
|
| 125 |
+ state = STATE_DONE; |
|
| 126 |
+ } |
|
| 127 |
+ else |
|
| 128 |
+ {
|
|
| 129 |
+ out = in; |
|
| 130 |
+ } |
|
| 131 |
+ } |
|
| 132 |
+ else if (state == STATE_READING_SQUOTED_PARM) |
|
| 133 |
+ {
|
|
| 134 |
+ if (in == '\'') |
|
| 135 |
+ {
|
|
| 136 |
+ state = STATE_DONE; |
|
| 137 |
+ } |
|
| 138 |
+ else |
|
| 139 |
+ {
|
|
| 140 |
+ out = in; |
|
| 141 |
+ } |
|
| 142 |
+ } |
|
| 143 |
+ if (state == STATE_DONE) |
|
| 144 |
+ {
|
|
| 145 |
+ /* ASSERT (parm_len > 0); */ |
|
| 146 |
+ p[ret] = gc_malloc(parm_len + 1, true, gc); |
|
| 147 |
+ memcpy(p[ret], parm, parm_len); |
|
| 148 |
+ p[ret][parm_len] = '\0'; |
|
| 149 |
+ state = STATE_INITIAL; |
|
| 150 |
+ parm_len = 0; |
|
| 151 |
+ ++ret; |
|
| 152 |
+ } |
|
| 153 |
+ |
|
| 154 |
+ if (backslash && out) |
|
| 155 |
+ {
|
|
| 156 |
+ if (!(out == '\\' || out == '\"' || space(out))) |
|
| 157 |
+ {
|
|
| 158 |
+#ifdef ENABLE_SMALL |
|
| 159 |
+ msg(msglevel, "%sOptions warning: Bad backslash ('\\') usage in %s:%d",
|
|
| 160 |
+ error_prefix, file, line_num); |
|
| 161 |
+#else |
|
| 162 |
+ msg(msglevel, |
|
| 163 |
+ "%sOptions warning: Bad backslash ('\\') usage in %s:%d: remember that backslashes are treated as shell-escapes and if you need to pass backslash characters as part of a Windows filename, you should use double backslashes such as \"c:\\\\" PACKAGE
|
|
| 164 |
+ "\\\\static.key\"", |
|
| 165 |
+ error_prefix, file, line_num); |
|
| 166 |
+#endif |
|
| 167 |
+ return 0; |
|
| 168 |
+ } |
|
| 169 |
+ } |
|
| 170 |
+ backslash = false; |
|
| 171 |
+ } |
|
| 172 |
+ |
|
| 173 |
+ /* store parameter character */ |
|
| 174 |
+ if (out) |
|
| 175 |
+ {
|
|
| 176 |
+ if (parm_len >= SIZE(parm)) |
|
| 177 |
+ {
|
|
| 178 |
+ parm[SIZE(parm) - 1] = 0; |
|
| 179 |
+ msg(msglevel, "%sOptions error: Parameter at %s:%d is too long (%d chars max): %s", |
|
| 180 |
+ error_prefix, file, line_num, (int)SIZE(parm), parm); |
|
| 181 |
+ return 0; |
|
| 182 |
+ } |
|
| 183 |
+ parm[parm_len++] = out; |
|
| 184 |
+ } |
|
| 185 |
+ |
|
| 186 |
+ /* avoid overflow if too many parms in one config file line */ |
|
| 187 |
+ if (ret >= n) |
|
| 188 |
+ {
|
|
| 189 |
+ break; |
|
| 190 |
+ } |
|
| 191 |
+ |
|
| 192 |
+ } while (*c++ != '\0'); |
|
| 193 |
+ |
|
| 194 |
+ if (state == STATE_READING_QUOTED_PARM) |
|
| 195 |
+ {
|
|
| 196 |
+ msg(msglevel, "%sOptions error: No closing quotation (\") in %s:%d", error_prefix, file, |
|
| 197 |
+ line_num); |
|
| 198 |
+ return 0; |
|
| 199 |
+ } |
|
| 200 |
+ if (state == STATE_READING_SQUOTED_PARM) |
|
| 201 |
+ {
|
|
| 202 |
+ msg(msglevel, "%sOptions error: No closing single quotation (\') in %s:%d", error_prefix, |
|
| 203 |
+ file, line_num); |
|
| 204 |
+ return 0; |
|
| 205 |
+ } |
|
| 206 |
+ if (state != STATE_INITIAL) |
|
| 207 |
+ {
|
|
| 208 |
+ msg(msglevel, "%sOptions error: Residual parse state (%d) in %s:%d", error_prefix, state, |
|
| 209 |
+ file, line_num); |
|
| 210 |
+ return 0; |
|
| 211 |
+ } |
|
| 212 |
+#if 0 |
|
| 213 |
+ {
|
|
| 214 |
+ int i; |
|
| 215 |
+ for (i = 0; i < ret; ++i) |
|
| 216 |
+ {
|
|
| 217 |
+ msg(M_INFO|M_NOPREFIX, "%s:%d ARG[%d] '%s'", file, line_num, i, p[i]); |
|
| 218 |
+ } |
|
| 219 |
+ } |
|
| 220 |
+#endif |
|
| 221 |
+ return ret; |
|
| 222 |
+} |
|
| 223 |
+ |
|
| 224 |
+struct in_src |
|
| 225 |
+{
|
|
| 226 |
+#define IS_TYPE_FP 1 |
|
| 227 |
+#define IS_TYPE_BUF 2 |
|
| 228 |
+ int type; |
|
| 229 |
+ union |
|
| 230 |
+ {
|
|
| 231 |
+ FILE *fp; |
|
| 232 |
+ struct buffer *multiline; |
|
| 233 |
+ } u; |
|
| 234 |
+}; |
|
| 235 |
+ |
|
| 236 |
+static bool |
|
| 237 |
+in_src_get(const struct in_src *is, char *line, const int size) |
|
| 238 |
+{
|
|
| 239 |
+ if (is->type == IS_TYPE_FP) |
|
| 240 |
+ {
|
|
| 241 |
+ return BOOL_CAST(fgets(line, size, is->u.fp)); |
|
| 242 |
+ } |
|
| 243 |
+ else if (is->type == IS_TYPE_BUF) |
|
| 244 |
+ {
|
|
| 245 |
+ bool status = buf_parse(is->u.multiline, '\n', line, size); |
|
| 246 |
+ if ((int)strlen(line) + 1 < size) |
|
| 247 |
+ {
|
|
| 248 |
+ strcat(line, "\n"); |
|
| 249 |
+ } |
|
| 250 |
+ return status; |
|
| 251 |
+ } |
|
| 252 |
+ else |
|
| 253 |
+ {
|
|
| 254 |
+ ASSERT(0); |
|
| 255 |
+ return false; |
|
| 256 |
+ } |
|
| 257 |
+} |
|
| 258 |
+ |
|
| 259 |
+static char * |
|
| 260 |
+read_inline_file(struct in_src *is, const char *close_tag, int *num_lines, struct gc_arena *gc) |
|
| 261 |
+{
|
|
| 262 |
+ char line[OPTION_LINE_SIZE]; |
|
| 263 |
+ struct buffer buf = alloc_buf(8 * OPTION_LINE_SIZE); |
|
| 264 |
+ char *ret; |
|
| 265 |
+ bool endtagfound = false; |
|
| 266 |
+ |
|
| 267 |
+ while (in_src_get(is, line, sizeof(line))) |
|
| 268 |
+ {
|
|
| 269 |
+ (*num_lines)++; |
|
| 270 |
+ char *line_ptr = line; |
|
| 271 |
+ /* Remove leading spaces */ |
|
| 272 |
+ while (isspace(*line_ptr)) |
|
| 273 |
+ {
|
|
| 274 |
+ line_ptr++; |
|
| 275 |
+ } |
|
| 276 |
+ if (!strncmp(line_ptr, close_tag, strlen(close_tag))) |
|
| 277 |
+ {
|
|
| 278 |
+ endtagfound = true; |
|
| 279 |
+ break; |
|
| 280 |
+ } |
|
| 281 |
+ if (!buf_safe(&buf, strlen(line) + 1)) |
|
| 282 |
+ {
|
|
| 283 |
+ /* Increase buffer size */ |
|
| 284 |
+ struct buffer buf2 = alloc_buf(buf.capacity * 2); |
|
| 285 |
+ ASSERT(buf_copy(&buf2, &buf)); |
|
| 286 |
+ buf_clear(&buf); |
|
| 287 |
+ free_buf(&buf); |
|
| 288 |
+ buf = buf2; |
|
| 289 |
+ } |
|
| 290 |
+ buf_printf(&buf, "%s", line); |
|
| 291 |
+ } |
|
| 292 |
+ if (!endtagfound) |
|
| 293 |
+ {
|
|
| 294 |
+ msg(M_FATAL, "ERROR: Endtag %s missing", close_tag); |
|
| 295 |
+ } |
|
| 296 |
+ ret = string_alloc(BSTR(&buf), gc); |
|
| 297 |
+ buf_clear(&buf); |
|
| 298 |
+ free_buf(&buf); |
|
| 299 |
+ secure_memzero(line, sizeof(line)); |
|
| 300 |
+ return ret; |
|
| 301 |
+} |
|
| 302 |
+ |
|
| 303 |
+static int |
|
| 304 |
+check_inline_file(struct in_src *is, char *p[], struct gc_arena *gc) |
|
| 305 |
+{
|
|
| 306 |
+ int num_inline_lines = 0; |
|
| 307 |
+ |
|
| 308 |
+ if (p[0] && !p[1]) |
|
| 309 |
+ {
|
|
| 310 |
+ char *arg = p[0]; |
|
| 311 |
+ if (arg[0] == '<' && arg[strlen(arg) - 1] == '>') |
|
| 312 |
+ {
|
|
| 313 |
+ struct buffer close_tag; |
|
| 314 |
+ |
|
| 315 |
+ arg[strlen(arg) - 1] = '\0'; |
|
| 316 |
+ p[0] = string_alloc(arg + 1, gc); |
|
| 317 |
+ close_tag = alloc_buf(strlen(p[0]) + 4); |
|
| 318 |
+ buf_printf(&close_tag, "</%s>", p[0]); |
|
| 319 |
+ p[1] = read_inline_file(is, BSTR(&close_tag), &num_inline_lines, gc); |
|
| 320 |
+ p[2] = NULL; |
|
| 321 |
+ free_buf(&close_tag); |
|
| 322 |
+ } |
|
| 323 |
+ } |
|
| 324 |
+ return num_inline_lines; |
|
| 325 |
+} |
|
| 326 |
+ |
|
| 327 |
+static int |
|
| 328 |
+check_inline_file_via_fp(FILE *fp, char *p[], struct gc_arena *gc) |
|
| 329 |
+{
|
|
| 330 |
+ struct in_src is; |
|
| 331 |
+ is.type = IS_TYPE_FP; |
|
| 332 |
+ is.u.fp = fp; |
|
| 333 |
+ return check_inline_file(&is, p, gc); |
|
| 334 |
+} |
|
| 335 |
+ |
|
| 336 |
+static int |
|
| 337 |
+check_inline_file_via_buf(struct buffer *multiline, char *p[], struct gc_arena *gc) |
|
| 338 |
+{
|
|
| 339 |
+ struct in_src is; |
|
| 340 |
+ is.type = IS_TYPE_BUF; |
|
| 341 |
+ is.u.multiline = multiline; |
|
| 342 |
+ return check_inline_file(&is, p, gc); |
|
| 343 |
+} |
|
| 344 |
+ |
|
| 345 |
+void |
|
| 346 |
+read_config_file(struct options *options, const char *file, int level, const char *top_file, |
|
| 347 |
+ const int top_line, const msglvl_t msglevel, |
|
| 348 |
+ const unsigned int permission_mask, unsigned int *option_types_found, |
|
| 349 |
+ struct env_set *es) |
|
| 350 |
+{
|
|
| 351 |
+ const int max_recursive_levels = 10; |
|
| 352 |
+ FILE *fp; |
|
| 353 |
+ int line_num; |
|
| 354 |
+ char line[OPTION_LINE_SIZE + 1]; |
|
| 355 |
+ char *p[MAX_PARMS + 1]; |
|
| 356 |
+ |
|
| 357 |
+ ++level; |
|
| 358 |
+ if (level <= max_recursive_levels) |
|
| 359 |
+ {
|
|
| 360 |
+ if (streq(file, "stdin")) |
|
| 361 |
+ {
|
|
| 362 |
+ fp = stdin; |
|
| 363 |
+ } |
|
| 364 |
+ else |
|
| 365 |
+ {
|
|
| 366 |
+ fp = platform_fopen(file, "r"); |
|
| 367 |
+ } |
|
| 368 |
+ if (fp) |
|
| 369 |
+ {
|
|
| 370 |
+ line_num = 0; |
|
| 371 |
+ while (fgets(line, sizeof(line), fp)) |
|
| 372 |
+ {
|
|
| 373 |
+ int offset = 0; |
|
| 374 |
+ CLEAR(p); |
|
| 375 |
+ ++line_num; |
|
| 376 |
+ if (strlen(line) == OPTION_LINE_SIZE) |
|
| 377 |
+ {
|
|
| 378 |
+ msg(msglevel, |
|
| 379 |
+ "In %s:%d: Maximum option line length (%d) exceeded, line starts with %s", |
|
| 380 |
+ file, line_num, OPTION_LINE_SIZE, line); |
|
| 381 |
+ } |
|
| 382 |
+ |
|
| 383 |
+ /* Ignore UTF-8 BOM at start of stream */ |
|
| 384 |
+ if (line_num == 1 && strncmp(line, "\xEF\xBB\xBF", 3) == 0) |
|
| 385 |
+ {
|
|
| 386 |
+ offset = 3; |
|
| 387 |
+ } |
|
| 388 |
+ if (parse_line(line + offset, p, SIZE(p) - 1, file, line_num, msglevel, |
|
| 389 |
+ &options->gc)) |
|
| 390 |
+ {
|
|
| 391 |
+ bypass_doubledash(&p[0]); |
|
| 392 |
+ int lines_inline = check_inline_file_via_fp(fp, p, &options->gc); |
|
| 393 |
+ add_option(options, p, lines_inline, file, line_num, level, msglevel, |
|
| 394 |
+ permission_mask, option_types_found, es); |
|
| 395 |
+ line_num += lines_inline; |
|
| 396 |
+ } |
|
| 397 |
+ } |
|
| 398 |
+ if (fp != stdin) |
|
| 399 |
+ {
|
|
| 400 |
+ fclose(fp); |
|
| 401 |
+ } |
|
| 402 |
+ } |
|
| 403 |
+ else |
|
| 404 |
+ {
|
|
| 405 |
+ msg(msglevel, "In %s:%d: Error opening configuration file: %s", top_file, top_line, |
|
| 406 |
+ file); |
|
| 407 |
+ } |
|
| 408 |
+ } |
|
| 409 |
+ else |
|
| 410 |
+ {
|
|
| 411 |
+ msg(msglevel, |
|
| 412 |
+ "In %s:%d: Maximum recursive include levels exceeded in include attempt of file %s -- probably you have a configuration file that tries to include itself.", |
|
| 413 |
+ top_file, top_line, file); |
|
| 414 |
+ } |
|
| 415 |
+ secure_memzero(line, sizeof(line)); |
|
| 416 |
+ CLEAR(p); |
|
| 417 |
+} |
|
| 418 |
+ |
|
| 419 |
+void |
|
| 420 |
+read_config_string(const char *prefix, struct options *options, const char *config, |
|
| 421 |
+ const msglvl_t msglevel, const unsigned int permission_mask, |
|
| 422 |
+ unsigned int *option_types_found, struct env_set *es) |
|
| 423 |
+{
|
|
| 424 |
+ char line[OPTION_LINE_SIZE]; |
|
| 425 |
+ struct buffer multiline; |
|
| 426 |
+ int line_num = 0; |
|
| 427 |
+ |
|
| 428 |
+ buf_set_read(&multiline, (uint8_t *)config, strlen(config)); |
|
| 429 |
+ |
|
| 430 |
+ while (buf_parse(&multiline, '\n', line, sizeof(line))) |
|
| 431 |
+ {
|
|
| 432 |
+ char *p[MAX_PARMS + 1]; |
|
| 433 |
+ CLEAR(p); |
|
| 434 |
+ ++line_num; |
|
| 435 |
+ if (parse_line(line, p, SIZE(p) - 1, prefix, line_num, msglevel, &options->gc)) |
|
| 436 |
+ {
|
|
| 437 |
+ bypass_doubledash(&p[0]); |
|
| 438 |
+ int lines_inline = check_inline_file_via_buf(&multiline, p, &options->gc); |
|
| 439 |
+ add_option(options, p, lines_inline, prefix, line_num, 0, msglevel, permission_mask, |
|
| 440 |
+ option_types_found, es); |
|
| 441 |
+ line_num += lines_inline; |
|
| 442 |
+ } |
|
| 443 |
+ CLEAR(p); |
|
| 444 |
+ } |
|
| 445 |
+ secure_memzero(line, sizeof(line)); |
|
| 446 |
+} |
|
| 447 |
+ |
|
| 448 |
+void |
|
| 449 |
+parse_argv(struct options *options, const int argc, char *argv[], const msglvl_t msglevel, |
|
| 450 |
+ const unsigned int permission_mask, unsigned int *option_types_found, struct env_set *es) |
|
| 451 |
+{
|
|
| 452 |
+ /* usage message */ |
|
| 453 |
+ if (argc <= 1) |
|
| 454 |
+ {
|
|
| 455 |
+ usage(); |
|
| 456 |
+ } |
|
| 457 |
+ |
|
| 458 |
+ /* config filename specified only? */ |
|
| 459 |
+ if (argc == 2 && strncmp(argv[1], "--", 2)) |
|
| 460 |
+ {
|
|
| 461 |
+ char *p[MAX_PARMS + 1]; |
|
| 462 |
+ CLEAR(p); |
|
| 463 |
+ p[0] = "config"; |
|
| 464 |
+ p[1] = argv[1]; |
|
| 465 |
+ add_option(options, p, false, NULL, 0, 0, msglevel, permission_mask, option_types_found, |
|
| 466 |
+ es); |
|
| 467 |
+ } |
|
| 468 |
+ else |
|
| 469 |
+ {
|
|
| 470 |
+ /* parse command line */ |
|
| 471 |
+ for (int i = 1; i < argc; ++i) |
|
| 472 |
+ {
|
|
| 473 |
+ char *p[MAX_PARMS + 1]; |
|
| 474 |
+ CLEAR(p); |
|
| 475 |
+ p[0] = argv[i]; |
|
| 476 |
+ if (strncmp(p[0], "--", 2)) |
|
| 477 |
+ {
|
|
| 478 |
+ msg(msglevel, |
|
| 479 |
+ "I'm trying to parse \"%s\" as an --option parameter but I don't see a leading '--'", |
|
| 480 |
+ p[0]); |
|
| 481 |
+ } |
|
| 482 |
+ else |
|
| 483 |
+ {
|
|
| 484 |
+ p[0] += 2; |
|
| 485 |
+ } |
|
| 486 |
+ |
|
| 487 |
+ int j; |
|
| 488 |
+ for (j = 1; j < MAX_PARMS; ++j) |
|
| 489 |
+ {
|
|
| 490 |
+ if (i + j < argc) |
|
| 491 |
+ {
|
|
| 492 |
+ char *arg = argv[i + j]; |
|
| 493 |
+ if (strncmp(arg, "--", 2)) |
|
| 494 |
+ {
|
|
| 495 |
+ p[j] = arg; |
|
| 496 |
+ } |
|
| 497 |
+ else |
|
| 498 |
+ {
|
|
| 499 |
+ break; |
|
| 500 |
+ } |
|
| 501 |
+ } |
|
| 502 |
+ } |
|
| 503 |
+ add_option(options, p, false, NULL, 0, 0, msglevel, permission_mask, option_types_found, |
|
| 504 |
+ es); |
|
| 505 |
+ i += j - 1; |
|
| 506 |
+ } |
|
| 507 |
+ } |
|
| 508 |
+} |
|
| 509 |
+ |
|
| 510 |
+bool |
|
| 511 |
+apply_push_options(struct context *c, struct options *options, struct buffer *buf, |
|
| 512 |
+ unsigned int permission_mask, unsigned int *option_types_found, |
|
| 513 |
+ struct env_set *es, bool is_update) |
|
| 514 |
+{
|
|
| 515 |
+ char line[OPTION_PARM_SIZE]; |
|
| 516 |
+ int line_num = 0; |
|
| 517 |
+ const char *file = "[PUSH-OPTIONS]"; |
|
| 518 |
+ const msglvl_t msglevel = D_PUSH_ERRORS | M_OPTERR; |
|
| 519 |
+ unsigned int update_options_found = 0; |
|
| 520 |
+ |
|
| 521 |
+ while (buf_parse(buf, ',', line, sizeof(line))) |
|
| 522 |
+ {
|
|
| 523 |
+ char *p[MAX_PARMS + 1]; |
|
| 524 |
+ CLEAR(p); |
|
| 525 |
+ ++line_num; |
|
| 526 |
+ unsigned int push_update_option_flags = 0; |
|
| 527 |
+ int i = 0; |
|
| 528 |
+ |
|
| 529 |
+ /* skip leading spaces matching the behaviour of parse_line */ |
|
| 530 |
+ while (isspace(line[i])) |
|
| 531 |
+ {
|
|
| 532 |
+ i++; |
|
| 533 |
+ } |
|
| 534 |
+ |
|
| 535 |
+ /* If we are not in a 'PUSH_UPDATE' we just check `apply_pull_filter()` |
|
| 536 |
+ * otherwise we must call `check_push_update_option_flags()` first |
|
| 537 |
+ */ |
|
| 538 |
+ if ((is_update && !check_push_update_option_flags(line, &i, &push_update_option_flags)) |
|
| 539 |
+ || !apply_pull_filter(options, &line[i])) |
|
| 540 |
+ {
|
|
| 541 |
+ /* In case we are in a `PUSH_UPDATE` and `check_push_update_option_flags()` |
|
| 542 |
+ * or `apply_pull_filter()` fail but the option is flagged by `PUSH_OPT_OPTIONAL`, |
|
| 543 |
+ * instead of restarting, we just ignore the option and we process the next one |
|
| 544 |
+ */ |
|
| 545 |
+ if (push_update_option_flags & PUSH_OPT_OPTIONAL) |
|
| 546 |
+ {
|
|
| 547 |
+ continue; /* Ignoring this option */ |
|
| 548 |
+ } |
|
| 549 |
+ return false; /* Cause push/pull error and stop push processing */ |
|
| 550 |
+ } |
|
| 551 |
+ |
|
| 552 |
+ if (parse_line(&line[i], p, SIZE(p) - 1, file, line_num, msglevel, &options->gc)) |
|
| 553 |
+ {
|
|
| 554 |
+ if (!is_update) |
|
| 555 |
+ {
|
|
| 556 |
+ add_option(options, p, false, file, line_num, 0, msglevel, permission_mask, |
|
| 557 |
+ option_types_found, es); |
|
| 558 |
+ } |
|
| 559 |
+ else if (push_update_option_flags & PUSH_OPT_TO_REMOVE) |
|
| 560 |
+ {
|
|
| 561 |
+ remove_option(c, options, p, false, file, line_num, msglevel, permission_mask, |
|
| 562 |
+ option_types_found, es); |
|
| 563 |
+ } |
|
| 564 |
+ else |
|
| 565 |
+ {
|
|
| 566 |
+ update_option(c, options, p, false, file, line_num, 0, msglevel, permission_mask, |
|
| 567 |
+ option_types_found, es, &update_options_found); |
|
| 568 |
+ } |
|
| 569 |
+ } |
|
| 570 |
+ } |
|
| 571 |
+ return true; |
|
| 572 |
+} |
|
| 573 |
+ |
|
| 574 |
+void |
|
| 575 |
+options_server_import(struct options *o, const char *filename, msglvl_t msglevel, |
|
| 576 |
+ unsigned int permission_mask, unsigned int *option_types_found, |
|
| 577 |
+ struct env_set *es) |
|
| 578 |
+{
|
|
| 579 |
+ msg(D_PUSH, "OPTIONS IMPORT: reading client specific options from: %s", filename); |
|
| 580 |
+ read_config_file(o, filename, 0, filename, 0, msglevel, permission_mask, option_types_found, |
|
| 581 |
+ es); |
|
| 582 |
+} |
|
| 583 |
+ |
|
| 584 |
+void |
|
| 585 |
+options_string_import(struct options *options, const char *config, const msglvl_t msglevel, |
|
| 586 |
+ const unsigned int permission_mask, unsigned int *option_types_found, |
|
| 587 |
+ struct env_set *es) |
|
| 588 |
+{
|
|
| 589 |
+ read_config_string("[CONFIG-STRING]", options, config, msglevel, permission_mask,
|
|
| 590 |
+ option_types_found, es); |
|
| 591 |
+} |