| /* |
| * OpenVPN -- An application to securely tunnel IP networks |
| * over a single TCP/UDP port, with support for SSL/TLS-based |
| * session authentication and key exchange, |
| * packet encryption, packet authentication, and |
| * packet compression. |
| * |
| * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 |
| * as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with this program; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * |
| * A printf-like function (that only recognizes a subset of standard printf |
| * format operators) that prints arguments to an argv list instead |
| * of a standard string. This is used to build up argv arrays for passing |
| * to execve. |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #elif defined(_MSC_VER) |
| #include "config-msvc.h" |
| #endif |
| |
| #include "syshead.h" |
| |
| #include "argv.h" |
| #include "integer.h" |
| #include "env_set.h" |
| #include "options.h" |
| |
| /** |
| * Resizes the list of arguments struct argv can carry. This resize |
| * operation will only increase the size, never decrease the size. |
| * |
| * @param *a Valid pointer to a struct argv to resize |
| * @param newcap size_t with the new size of the argument list. |
| */ |
| static void |
| argv_extend(struct argv *a, const size_t newcap) |
| { |
| if (newcap > a->capacity) |
| { |
| char **newargv; |
| size_t i; |
| ALLOC_ARRAY_CLEAR_GC(newargv, char *, newcap, &a->gc); |
| for (i = 0; i < a->argc; ++i) |
| { |
| newargv[i] = a->argv[i]; |
| } |
| a->argv = newargv; |
| a->capacity = newcap; |
| } |
| } |
| |
| /** |
| * Initialise an already allocated struct argv. |
| * It is expected that the input argument is a valid pointer. |
| * |
| * @param *a Pointer to a struct argv to initialise |
| */ |
| static void |
| argv_init(struct argv *a) |
| { |
| a->capacity = 0; |
| a->argc = 0; |
| a->argv = NULL; |
| a->gc = gc_new(); |
| argv_extend(a, 8); |
| } |
| |
| /** |
| * Allocates a new struct argv and ensures it is initialised. |
| * Note that it does not return a pointer, but a struct argv directly. |
| * |
| * @returns Returns an initialised and empty struct argv. |
| */ |
| struct argv |
| argv_new(void) |
| { |
| struct argv ret; |
| argv_init(&ret); |
| return ret; |
| } |
| |
| /** |
| * Frees all memory allocations allocated by the struct argv |
| * related functions. |
| * |
| * @param *a Valid pointer to a struct argv to release memory from |
| */ |
| void |
| argv_free(struct argv *a) |
| { |
| gc_free(&a->gc); |
| } |
| |
| /** |
| * Resets the struct argv to an initial state. No memory buffers |
| * will be released by this call. |
| * |
| * @param *a Valid pointer to a struct argv to resize |
| */ |
| static void |
| argv_reset(struct argv *a) |
| { |
| if (a->argc) |
| { |
| size_t i; |
| for (i = 0; i < a->argc; ++i) |
| { |
| a->argv[i] = NULL; |
| } |
| a->argc = 0; |
| } |
| } |
| |
| /** |
| * Extends an existing struct argv to carry minimum 'add' number |
| * of new arguments. This builds on argv_extend(), which ensures the |
| * new size will only be higher than the current capacity. |
| * |
| * The new size is also calculated based on the result of adjust_power_of_2(). |
| * This approach ensures that the list does grow bulks and only when the |
| * current limit is reached. |
| * |
| * @param *a Valid pointer to the struct argv to extend |
| * @param add size_t with the number of elements to add. |
| * |
| */ |
| static void |
| argv_grow(struct argv *a, const size_t add) |
| { |
| const size_t newargc = a->argc + add + 1; |
| ASSERT(newargc > a->argc); |
| argv_extend(a, adjust_power_of_2(newargc)); |
| } |
| |
| /** |
| * Appends a string to to the list of arguments stored in a struct argv |
| * This will ensure the list size in struct argv has the needed capacity to |
| * store the value. |
| * |
| * @param *a struct argv where to append the new string value |
| * @param *str Pointer to string to append. The provided string *MUST* have |
| * been malloc()ed or NULL. |
| */ |
| static void |
| argv_append(struct argv *a, char *str) |
| { |
| argv_grow(a, 1); |
| a->argv[a->argc++] = str; |
| } |
| |
| /** |
| * Clones a struct argv with all the contents to a new allocated struct argv. |
| * If 'headroom' is larger than 0, it will create a head-room in front of the |
| * values being copied from the source input. |
| * |
| * |
| * @param *source Valid pointer to the source struct argv to clone. It may |
| * be NULL. |
| * @param headroom Number of slots to leave empty in front of the slots |
| * copied from the source. |
| * |
| * @returns Returns a new struct argv containing a copy of the source |
| * struct argv, with the given headroom in front of the copy. |
| * |
| */ |
| static struct argv |
| argv_clone(const struct argv *source, const size_t headroom) |
| { |
| struct argv r; |
| argv_init(&r); |
| |
| for (size_t i = 0; i < headroom; ++i) |
| { |
| argv_append(&r, NULL); |
| } |
| if (source) |
| { |
| for (size_t i = 0; i < source->argc; ++i) |
| { |
| argv_append(&r, string_alloc(source->argv[i], &r.gc)); |
| } |
| } |
| return r; |
| } |
| |
| /** |
| * Inserts an argument string in front of all other argument slots. |
| * |
| * @param *a Valid pointer to the struct argv to insert the argument into |
| * @param *head Pointer to the char * string with the argument to insert |
| * |
| * @returns Returns a new struct argv with the inserted argument in front |
| */ |
| struct argv |
| argv_insert_head(const struct argv *a, const char *head) |
| { |
| struct argv r; |
| r = argv_clone(a, 1); |
| r.argv[0] = string_alloc(head, &r.gc); |
| return r; |
| } |
| |
| /** |
| * Generate a single string with all the arguments in a struct argv |
| * concatenated. |
| * |
| * @param *a Valid pointer to the struct argv with the arguments to list |
| * @param *gc Pointer to a struct gc_arena managed buffer |
| * @param flags Flags passed to the print_argv() function. |
| * |
| * @returns Returns a string generated by print_argv() with all the arguments |
| * concatenated. If the argument count is 0, it will return an empty |
| * string. The return string is allocated in the gc_arena managed |
| * buffer. If the gc_arena pointer is NULL, the returned string |
| * must be free()d explicitly to avoid memory leaks. |
| */ |
| const char * |
| argv_str(const struct argv *a, struct gc_arena *gc, const unsigned int flags) |
| { |
| return print_argv((const char **)a->argv, gc, flags); |
| } |
| |
| /** |
| * Write the arguments stored in a struct argv via the msg() command. |
| * |
| * @param msglev Integer with the message level used by msg(). |
| * @param *a Valid pointer to the struct argv with the arguments to write. |
| */ |
| void |
| argv_msg(const int msglev, const struct argv *a) |
| { |
| struct gc_arena gc = gc_new(); |
| msg(msglev, "%s", argv_str(a, &gc, 0)); |
| gc_free(&gc); |
| } |
| |
| /** |
| * Similar to argv_msg() but prefixes the messages being written with a |
| * given string. |
| * |
| * @param msglev Integer with the message level used by msg(). |
| * @param *a Valid pointer to the struct argv with the arguments to write |
| * @param *prefix Valid char * pointer to the prefix string |
| * |
| */ |
| void |
| argv_msg_prefix(const int msglev, const struct argv *a, const char *prefix) |
| { |
| struct gc_arena gc = gc_new(); |
| msg(msglev, "%s: %s", prefix, argv_str(a, &gc, 0)); |
| gc_free(&gc); |
| } |
| |
| /** |
| * Prepares argv format string for further processing |
| * |
| * Individual argument must be separated by space. Ignores leading and |
| * trailing spaces. Consecutive spaces count as one. Returns prepared |
| * format string, with space replaced by delim and adds the number of |
| * arguments to the count parameter. |
| * |
| * @param *format Pointer to a the format string to process |
| * @param delim Char with the delimiter to use |
| * @param *count size_t pointer used to return the number of |
| * tokens (argument slots) found in the format string. |
| * @param *gc Pointer to a gc_arena managed buffer. |
| * |
| * @returns Returns a parsed format string (char *), together with the |
| * number of tokens parts found (via *count). The result string |
| * is allocated within the gc_arena managed buffer. If the |
| * gc_arena pointer is NULL, the returned string must be explicitly |
| * free()d to avoid memory leaks. |
| */ |
| static char * |
| argv_prep_format(const char *format, const char delim, size_t *count, |
| struct gc_arena *gc) |
| { |
| if (format == NULL) |
| { |
| return NULL; |
| } |
| |
| bool in_token = false; |
| char *f = gc_malloc(strlen(format) + 1, true, gc); |
| for (int i = 0, j = 0; i < strlen(format); i++) |
| { |
| if (format[i] == ' ') |
| { |
| in_token = false; |
| continue; |
| } |
| |
| if (!in_token) |
| { |
| (*count)++; |
| |
| /* |
| * We don't add any delimiter to the output string if |
| * the string is empty; the resulting format string |
| * will never start with a delimiter. |
| */ |
| if (j > 0) /* Has anything been written to the output string? */ |
| { |
| f[j++] = delim; |
| } |
| } |
| |
| f[j++] = format[i]; |
| in_token = true; |
| } |
| |
| return f; |
| } |
| |
| /** |
| * Create a struct argv based on a format string |
| * |
| * Instead of parsing the format string ourselves place delimiters via |
| * argv_prep_format() before we let libc's printf() do the parsing. |
| * Then split the resulting string at the injected delimiters. |
| * |
| * @param *argres Valid pointer to a struct argv where the resulting parsed |
| * arguments, based on the format string. |
| * @param *format Char* string with a printf() compliant format string |
| * @param arglist A va_list with the arguments to be consumed by the format |
| * string |
| * |
| * @returns Returns true if the parsing and processing was successfully. If |
| * the resulting number of arguments does not match the expected |
| * number of arguments (based on the format string), it is |
| * considered a failure, which returns false. This can happen if |
| * the ASCII Group Separator (GS - 0x1D) is put into the arguments |
| * list or format string. |
| */ |
| static bool |
| argv_printf_arglist(struct argv *argres, const char *format, va_list arglist) |
| { |
| const char delim = 0x1D; /* ASCII Group Separator (GS) */ |
| bool res = false; |
| |
| /* |
| * Prepare a format string which will be used by vsnprintf() later on. |
| * |
| * This means all space separators in the input format string will be |
| * replaced by the GS (0x1D), so we can split this up again after the |
| * the vsnprintf() call into individual arguments again which will be |
| * saved in the struct argv. |
| * |
| */ |
| size_t argc = argres->argc; |
| char *f = argv_prep_format(format, delim, &argc, &argres->gc); |
| if (f == NULL) |
| { |
| goto out; |
| } |
| |
| /* |
| * Determine minimum buffer size. |
| * |
| * With C99, vsnprintf(NULL, 0, ...) will return the number of bytes |
| * it would have written, had the buffer been large enough. |
| */ |
| va_list tmplist; |
| va_copy(tmplist, arglist); |
| int len = vsnprintf(NULL, 0, f, tmplist); |
| va_end(tmplist); |
| if (len < 0) |
| { |
| goto out; |
| } |
| |
| /* |
| * Do the actual vsnprintf() operation, which expands the format |
| * string with the provided arguments. |
| */ |
| size_t size = len + 1; |
| char *buf = gc_malloc(size, false, &argres->gc); |
| len = vsnprintf(buf, size, f, arglist); |
| if (len < 0 || len >= size) |
| { |
| goto out; |
| } |
| |
| /* |
| * Split the string at the GS (0x1D) delimiters and put each elemen |
| * into the struct argv being returned to the caller. |
| */ |
| char *end = strchr(buf, delim); |
| while (end) |
| { |
| *end = '\0'; |
| argv_append(argres, buf); |
| buf = end + 1; |
| end = strchr(buf, delim); |
| } |
| argv_append(argres, buf); |
| |
| if (argres->argc != argc) |
| { |
| /* Someone snuck in a GS (0x1D), fail gracefully */ |
| argv_reset(argres); |
| goto out; |
| } |
| res = true; |
| |
| out: |
| return res; |
| } |
| |
| /** |
| * printf() variant which populates a struct argv. It processes the |
| * format string with the provided arguments. For each space separator found |
| * in the format string, a new argument will be added to the resulting |
| * struct argv. |
| * |
| * This will always reset and ensure the result is based on a pristine |
| * struct argv. |
| * |
| * @param *argres Valid pointer to a struct argv where the result will be put. |
| * @param *format printf() compliant (char *) format string. |
| * |
| * @returns Returns true if the parsing was successful. See |
| * argv_printf_arglist() for more details. The parsed result will |
| * be put into argres. |
| */ |
| bool |
| argv_printf(struct argv *argres, const char *format, ...) |
| { |
| va_list arglist; |
| va_start(arglist, format); |
| |
| argv_reset(argres); |
| bool res = argv_printf_arglist(argres, format, arglist); |
| va_end(arglist); |
| return res; |
| } |
| |
| /** |
| * printf() inspired argv concatenation. Adds arguments to an existing |
| * struct argv and populets the argument slots based on the printf() based |
| * format string. |
| * |
| * @param *argres Valid pointer to a struct argv where the result will be put. |
| * @param *format printf() compliant (char *) format string. |
| * |
| * @returns Returns true if the parsing was successful. See |
| * argv_printf_arglist() for more details. The parsed result will |
| * be put into argres. |
| */ |
| bool |
| argv_printf_cat(struct argv *argres, const char *format, ...) |
| { |
| va_list arglist; |
| va_start(arglist, format); |
| bool res = argv_printf_arglist(argres, format, arglist); |
| va_end(arglist); |
| return res; |
| } |
| |
| /** |
| * Parses a command string, tokenizes it and puts each element into a separate |
| * struct argv argument slot. |
| * |
| * @params *argres Valid pointer to a struct argv where the parsed result |
| * will be found. |
| * @params *cmdstr Char * based string to parse |
| * |
| */ |
| void |
| argv_parse_cmd(struct argv *argres, const char *cmdstr) |
| { |
| argv_reset(argres); |
| |
| char *parms[MAX_PARMS + 1] = { 0 }; |
| int nparms = parse_line(cmdstr, parms, MAX_PARMS, "SCRIPT-ARGV", 0, |
| D_ARGV_PARSE_CMD, &argres->gc); |
| if (nparms) |
| { |
| int i; |
| for (i = 0; i < nparms; ++i) |
| { |
| argv_append(argres, parms[i]); |
| } |
| } |
| else |
| { |
| argv_append(argres, string_alloc(cmdstr, &argres->gc)); |
| } |
| } |