/*
 *  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.
 */

#ifndef MISC_H
#define MISC_H

#include "argv.h"
#include "basic.h"
#include "common.h"
#include "integer.h"
#include "buffer.h"
#include "platform.h"

/* socket descriptor passed by inetd/xinetd server to us */
#define INETD_SOCKET_DESCRIPTOR 0

/* forward declarations */
struct plugin_list;

/*
 * Handle environmental variable lists
 */

struct env_item {
    char *string;
    struct env_item *next;
};

struct env_set {
    struct gc_arena *gc;
    struct env_item *list;
};

/* system flags */
#define S_SCRIPT (1<<0)
#define S_FATAL  (1<<1)

const char *system_error_message(int, struct gc_arena *gc);

/* wrapper around the execve() call */
int openvpn_popen(const struct argv *a,  const struct env_set *es);

int openvpn_execve(const struct argv *a, const struct env_set *es, const unsigned int flags);

bool openvpn_execve_check(const struct argv *a, const struct env_set *es, const unsigned int flags, const char *error_message);

bool openvpn_execve_allowed(const unsigned int flags);

static inline bool
openvpn_run_script(const struct argv *a, const struct env_set *es, const unsigned int flags, const char *hook)
{
    char msg[256];

    openvpn_snprintf(msg, sizeof(msg), "WARNING: Failed running command (%s)", hook);
    return openvpn_execve_check(a, es, flags | S_SCRIPT, msg);
}


/* Set standard file descriptors to /dev/null */
void set_std_files_to_null(bool stdin_only);

/* dup inetd/xinetd socket descriptor and save */
extern int inetd_socket_descriptor;
void save_inetd_socket_descriptor(void);

/* set/delete environmental variable */
void setenv_str_ex(struct env_set *es,
                   const char *name,
                   const char *value,
                   const unsigned int name_include,
                   const unsigned int name_exclude,
                   const char name_replace,
                   const unsigned int value_include,
                   const unsigned int value_exclude,
                   const char value_replace);

void setenv_counter(struct env_set *es, const char *name, counter_type value);

void setenv_int(struct env_set *es, const char *name, int value);

void setenv_long_long(struct env_set *es, const char *name, long long value);

void setenv_str(struct env_set *es, const char *name, const char *value);

void setenv_str_safe(struct env_set *es, const char *name, const char *value);

void setenv_del(struct env_set *es, const char *name);

/**
 * Store the supplied name value pair in the env_set.  If the variable with the
 * supplied name  already exists, append _N to the name, starting at N=1.
 */
void setenv_str_incr(struct env_set *es, const char *name, const char *value);

void setenv_int_i(struct env_set *es, const char *name, const int value, const int i);

void setenv_str_i(struct env_set *es, const char *name, const char *value, const int i);

/* struct env_set functions */

struct env_set *env_set_create(struct gc_arena *gc);

void env_set_destroy(struct env_set *es);

bool env_set_del(struct env_set *es, const char *str);

void env_set_add(struct env_set *es, const char *str);

const char *env_set_get(const struct env_set *es, const char *name);

void env_set_print(int msglevel, const struct env_set *es);

void env_set_inherit(struct env_set *es, const struct env_set *src);

/* Make arrays of strings */

const char **make_env_array(const struct env_set *es,
                            const bool check_allowed,
                            struct gc_arena *gc);

const char **make_arg_array(const char *first, const char *parms, struct gc_arena *gc);

const char **make_extended_arg_array(char **p, struct gc_arena *gc);

/* an analogue to the random() function, but use OpenSSL functions if available */
long int get_random(void);

/* return true if filename can be opened for read */
bool test_file(const char *filename);

/* create a temporary file in directory, returns the filename of the created file */
const char *create_temp_file(const char *directory, const char *prefix, struct gc_arena *gc);

/* put a directory and filename together */
const char *gen_path(const char *directory, const char *filename, struct gc_arena *gc);

/* return true if pathname is absolute */
bool absolute_pathname(const char *pathname);

/* prepend a random prefix to hostname */
const char *hostname_randomize(const char *hostname, struct gc_arena *gc);

/*
 * Get and store a username/password
 */

struct user_pass
{
    bool defined;
    bool nocache;
    bool tokenized; /* true if password has been substituted by a token */
    bool wait_for_push; /* true if this object is waiting for a push-reply */

/* max length of username/password */
#ifdef ENABLE_PKCS11
#define USER_PASS_LEN 4096
#else
#define USER_PASS_LEN 128
#endif
    char username[USER_PASS_LEN];
    char password[USER_PASS_LEN];
};

#ifdef ENABLE_CLIENT_CR
/*
 * Challenge response info on client as pushed by server.
 */
struct auth_challenge_info {
#define CR_ECHO     (1<<0)  /* echo response when typed by user */
#define CR_RESPONSE (1<<1)  /* response needed */
    unsigned int flags;

    const char *user;
    const char *state_id;
    const char *challenge_text;
};

struct auth_challenge_info *get_auth_challenge(const char *auth_challenge, struct gc_arena *gc);

/*
 * Challenge response info on client as pushed by server.
 */
struct static_challenge_info {
#define SC_ECHO     (1<<0)  /* echo response when typed by user */
    unsigned int flags;

    const char *challenge_text;
};

#else  /* ifdef ENABLE_CLIENT_CR */
struct auth_challenge_info {};
struct static_challenge_info {};
#endif /* ifdef ENABLE_CLIENT_CR */

/*
 * Flags for get_user_pass and management_query_user_pass
 */
#define GET_USER_PASS_MANAGEMENT    (1<<0)
/* GET_USER_PASS_SENSITIVE     (1<<1)  not used anymore */
#define GET_USER_PASS_PASSWORD_ONLY (1<<2)
#define GET_USER_PASS_NEED_OK       (1<<3)
#define GET_USER_PASS_NOFATAL       (1<<4)
#define GET_USER_PASS_NEED_STR      (1<<5)
#define GET_USER_PASS_PREVIOUS_CREDS_FAILED (1<<6)

#define GET_USER_PASS_DYNAMIC_CHALLENGE      (1<<7) /* CRV1 protocol  -- dynamic challenge */
#define GET_USER_PASS_STATIC_CHALLENGE       (1<<8) /* SCRV1 protocol -- static challenge */
#define GET_USER_PASS_STATIC_CHALLENGE_ECHO  (1<<9) /* SCRV1 protocol -- echo response */

#define GET_USER_PASS_INLINE_CREDS (1<<10)  /* indicates that auth_file is actually inline creds */

bool get_user_pass_cr(struct user_pass *up,
                      const char *auth_file,
                      const char *prefix,
                      const unsigned int flags,
                      const char *auth_challenge);

static inline bool
get_user_pass(struct user_pass *up,
              const char *auth_file,
              const char *prefix,
              const unsigned int flags)
{
    return get_user_pass_cr(up, auth_file, prefix, flags, NULL);
}

void fail_user_pass(const char *prefix,
                    const unsigned int flags,
                    const char *reason);

void purge_user_pass(struct user_pass *up, const bool force);

void set_auth_token(struct user_pass *up, const char *token);

/*
 * Process string received by untrusted peer before
 * printing to console or log file.
 * Assumes that string has been null terminated.
 */
const char *safe_print(const char *str, struct gc_arena *gc);

/* returns true if environmental variable safe to print to log */
bool env_safe_to_print(const char *str);

/* returns true if environmental variable may be passed to an external program */
bool env_allowed(const char *str);

void configure_path(void);

const char *sanitize_control_message(const char *str, struct gc_arena *gc);

#if AUTO_USERID
void get_user_pass_auto_userid(struct user_pass *up, const char *tag);

#endif

/*
 * /sbin/ip path, may be overridden
 */
#ifdef ENABLE_IPROUTE
extern const char *iproute_path;
#endif

/* Script security */
#define SSEC_NONE      0 /* strictly no calling of external programs */
#define SSEC_BUILT_IN  1 /* only call built-in programs such as ifconfig, route, netsh, etc.*/
#define SSEC_SCRIPTS   2 /* allow calling of built-in programs and user-defined scripts */
#define SSEC_PW_ENV    3 /* allow calling of built-in programs and user-defined scripts that may receive a password as an environmental variable */
extern int script_security; /* GLOBAL */


#define COMPAT_FLAG_QUERY         0       /** compat_flags operator: Query for a flag */
#define COMPAT_FLAG_SET           (1<<0)  /** compat_flags operator: Set a compat flag */
#define COMPAT_NAMES              (1<<1)  /** compat flag: --compat-names set */
#define COMPAT_NO_NAME_REMAPPING  (1<<2)  /** compat flag: --compat-names without char remapping */
bool compat_flag(unsigned int flag);

#if P2MP_SERVER
/* helper to parse peer_info received from multi client, validate
 * (this is untrusted data) and put into environment */
bool validate_peer_info_line(char *line);

void output_peer_info_env(struct env_set *es, const char *peer_info);

#endif /* P2MP_SERVER */

#endif /* ifndef MISC_H */