If someone would like to test it - you can get a Windows binary from here:
https://ci.appveyor.com/api/buildjobs/w20knnbut89uoeuw/artifacts/msvc%2Fimage%2Fbin.zip

Note that interactive service doesn't (yet) work with Wintun, you need to
run openvpn as a privileged process. For example, from Administrator
command prompt:

> c:\Temp\ovpn2_wintun>openvpn.exe --config lev.ovpn --windows-driver wintun

ke 5. kesäk. 2019 klo 17.59 Lev Stipakov (lstipa...@gmail.com) kirjoitti:

> From: Lev Stipakov <l...@openvpn.net>
>
> This adds experimental support for Wintun,
> an alternative to tap-windows6 on Windows.
>
> To use wintun, specify
>
>   --windows-driver wintun
>
> as config option. Default value is "tap-windows6".
>
> Unlike tap-windows6, Wintun returns up to 256 packets,
> which have to be decapsulated and processed one by one.
> We do it by reading data into a new buffer, processing
> single packet and returning control to event loop
> to give it a chance to write processed packet to link.
>
> If there are no pending writes and there is data in wintun
> buffer, event loop triggers tun read. There we read single
> packet from wintun buffer or, if it is empty, read from
> tun driver.
>
> Signed-off-by: Lev Stipakov <l...@openvpn.net>
> ---
>  src/openvpn/forward.c |  76 +++++++++++++++++-
>  src/openvpn/forward.h |  15 ++++
>  src/openvpn/init.c    |   7 ++
>  src/openvpn/openvpn.h |   4 +
>  src/openvpn/options.c |  36 ++++++++-
>  src/openvpn/options.h |   1 +
>  src/openvpn/socket.c  |   4 +-
>  src/openvpn/syshead.h |   1 +
>  src/openvpn/tun.c     | 212
> +++++++++++++++++++++++++++++++++-----------------
>  src/openvpn/tun.h     |  34 +++++++-
>  src/openvpn/win32.c   | 119 ++++++++++++++++++++++++++--
>  src/openvpn/win32.h   |  19 ++++-
>  12 files changed, 441 insertions(+), 87 deletions(-)
>
> diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c
> index ad4c1f5..8077835 100644
> --- a/src/openvpn/forward.c
> +++ b/src/openvpn/forward.c
> @@ -2182,6 +2182,52 @@ io_wait_dowork(struct context *c, const unsigned
> int flags)
>      dmsg(D_EVENT_WAIT, "I/O WAIT status=0x%04x", c->c2.event_set_status);
>  }
>
> +#ifdef _WIN32
> +/**
> + * This processes single packet from wintun buffer,
> + * which can contain up to 256 packets.
> + */
> +static void
> +process_incoming_wintun(struct context *c)
> +{
> +    struct buffer *buf = &c->c2.wintun_buf;
> +    uint32_t *size = NULL;
> +    int end_padding_len = 0;
> +
> +    /* get packet size */
> +    size = (uint32_t *)buf_read_alloc(buf, 4);
> +    if (size == NULL)
> +    {
> +        return;
> +    }
> +
> +    /* skip start padding */
> +    if (!buf_advance(buf, 12))
> +    {
> +        return;
> +    }
> +
> +    /* copy packet */
> +    c->c2.buf = c->c2.buffers->read_tun_buf;
> +    buf_init(&c->c2.buf, FRAME_HEADROOM(&c->c2.frame));
> +    buf_safe(&c->c2.buf, MAX_RW_SIZE_TUN(&c->c2.frame));
> +    if (!buf_copy_n(&c->c2.buf, buf, (int)*size))
> +    {
> +        return;
> +    }
> +
> +    /* this encrypts packet and puts it to c->c2.to_link buffer */
> +    process_incoming_tun(c);
> +
> +    /* skip end padding */
> +    end_padding_len = (16 - (*size & 15)) % 16;
> +    if (!buf_advance(buf, end_padding_len))
> +    {
> +        return;
> +    }
> +}
> +#endif
> +
>  void
>  process_io(struct context *c)
>  {
> @@ -2217,10 +2263,34 @@ process_io(struct context *c)
>      /* Incoming data on TUN device */
>      else if (status & TUN_READ)
>      {
> -        read_incoming_tun(c);
> -        if (!IS_SIG(c))
> +#ifdef _WIN32
> +        if (c->options.wintun)
> +        {
> +            /* only read from driver if there are no packets in buffer */
> +            if (BLEN(&c->c2.wintun_buf) == 0)
> +            {
> +                read_incoming_tun(c);
> +                c->c2.wintun_buf = c->c2.buf;
> +            }
> +            if (!IS_SIG(c))
> +            {
> +                process_incoming_wintun(c);
> +            }
> +
> +            /*
> +             * We need to write processed wintun packet to link,
> +             * hence we give control back to event loop, which will
> +             * call us again if wintun buffer is not empty
> +             */
> +        }
> +        else
> +#endif
>          {
> -            process_incoming_tun(c);
> +            read_incoming_tun(c);
> +            if (!IS_SIG(c))
> +            {
> +                process_incoming_tun(c);
> +            }
>          }
>      }
>  }
> diff --git a/src/openvpn/forward.h b/src/openvpn/forward.h
> index 48202c0..ac3e932 100644
> --- a/src/openvpn/forward.h
> +++ b/src/openvpn/forward.h
> @@ -61,6 +61,7 @@
>  #define IOW_MBUF            (1<<7)
>  #define IOW_READ_TUN_FORCE  (1<<8)
>  #define IOW_WAIT_SIGNAL     (1<<9)
> +#define IOW_READ_WINTUN_BUF (1<<10)
>
>  #define IOW_READ            (IOW_READ_TUN|IOW_READ_LINK)
>
> @@ -375,6 +376,12 @@ p2p_iow_flags(const struct context *c)
>      {
>          flags |= IOW_TO_TUN;
>      }
> +#ifdef _WIN32
> +    if (c->c2.wintun_buf.len > 0)
> +    {
> +        flags |= IOW_READ_WINTUN_BUF;
> +    }
> +#endif
>      return flags;
>  }
>
> @@ -401,6 +408,14 @@ io_wait(struct context *c, const unsigned int flags)
>          }
>          c->c2.event_set_status = ret;
>      }
> +#ifdef _WIN32
> +    /* make sure we write to link before reading from wintun buffer,
> +     * otherwise we'll overwrite c2->to_link buffer and lose a packet */
> +    else if (!(flags & IOW_TO_LINK) && (flags & IOW_READ_WINTUN_BUF))
> +    {
> +        c->c2.event_set_status = TUN_READ;
> +    }
> +#endif
>      else
>      {
>          /* slow path */
> diff --git a/src/openvpn/init.c b/src/openvpn/init.c
> index ef26503..1625272 100644
> --- a/src/openvpn/init.c
> +++ b/src/openvpn/init.c
> @@ -1696,6 +1696,10 @@ do_init_tun(struct context *c)
>                              !c->options.ifconfig_nowarn,
>                              c->c2.es);
>
> +#ifdef _WIN32
> +    c->c1.tuntap->wintun = c->options.wintun;
> +#endif
> +
>      init_tun_post(c->c1.tuntap,
>                    &c->c2.frame,
>                    &c->options.tuntap_options);
> @@ -1738,6 +1742,9 @@ do_open_tun(struct context *c)
>      /* store (hide) interactive service handle in tuntap_options */
>      c->c1.tuntap->options.msg_channel = c->options.msg_channel;
>      msg(D_ROUTE, "interactive service msg_channel=%u", (unsigned int)
> c->options.msg_channel);
> +
> +    c->c1.tuntap->wintun = c->options.wintun;
> +
>  #endif
>
>      /* allocate route list structure */
> diff --git a/src/openvpn/openvpn.h b/src/openvpn/openvpn.h
> index d11f61d..d694666 100644
> --- a/src/openvpn/openvpn.h
> +++ b/src/openvpn/openvpn.h
> @@ -382,6 +382,10 @@ struct context_2
>      struct buffer buf;
>      struct buffer to_tun;
>      struct buffer to_link;
> +#ifdef _WIN32
> +    /* contains up to 256 packets */
> +    struct buffer wintun_buf;
> +#endif
>
>      /* should we print R|W|r|w to console on packet transfers? */
>      bool log_rw;
> diff --git a/src/openvpn/options.c b/src/openvpn/options.c
> index e34b65b..534a6be 100644
> --- a/src/openvpn/options.c
> +++ b/src/openvpn/options.c
> @@ -845,6 +845,7 @@ init_options(struct options *o, const bool init_gc)
>      o->tuntap_options.dhcp_masq_offset = 0;     /* use network address as
> internal DHCP server address */
>      o->route_method = ROUTE_METHOD_ADAPTIVE;
>      o->block_outside_dns = false;
> +    o->wintun = false;
>  #endif
>  #if P2MP_SERVER
>      o->real_hash_size = 256;
> @@ -2937,6 +2938,12 @@ options_postprocess_mutate_invariant(struct options
> *options)
>          options->ifconfig_noexec = false;
>      }
>
> +    /* for wintun kernel doesn't send DHCP requests, so use ipapi to set
> IP address and netmask */
> +    if (options->wintun)
> +    {
> +        options->tuntap_options.ip_win32_type = IPW32_SET_IPAPI;
> +    }
> +
>      remap_redirect_gateway_flags(options);
>  #endif
>
> @@ -3980,6 +3987,26 @@ foreign_option(struct options *o, char *argv[], int
> len, struct env_set *es)
>      }
>  }
>
> +#ifdef _WIN32
> +bool
> +parse_windows_driver(const char *str, const int msglevel)
> +{
> +    if (streq(str, "tap"))
> +    {
> +        return false;
> +    }
> +    else if (streq(str, "wintun"))
> +    {
> +        return true;
> +    }
> +    else
> +    {
> +        msg(msglevel, "--windows-driver must be tap-windows6 or wintun");
> +        return false;
> +    }
> +}
> +#endif
> +
>  /*
>   * parse/print topology coding
>   */
> @@ -5220,6 +5247,13 @@ add_option(struct options *options,
>          VERIFY_PERMISSION(OPT_P_GENERAL);
>          options->dev_type = p[1];
>      }
> +#ifdef _WIN32
> +    else if (streq(p[0], "windows-driver") && p[1] && !p[2])
> +    {
> +        VERIFY_PERMISSION(OPT_P_GENERAL);
> +        options->wintun = parse_windows_driver(p[1], M_FATAL);
> +    }
> +#endif
>      else if (streq(p[0], "dev-node") && p[1] && !p[2])
>      {
>          VERIFY_PERMISSION(OPT_P_GENERAL);
> @@ -7217,7 +7251,7 @@ add_option(struct options *options,
>      else if (streq(p[0], "show-adapters") && !p[1])
>      {
>          VERIFY_PERMISSION(OPT_P_GENERAL);
> -        show_tap_win_adapters(M_INFO|M_NOPREFIX, M_WARN|M_NOPREFIX);
> +        show_tap_win_adapters(M_INFO|M_NOPREFIX, M_WARN|M_NOPREFIX,
> options->wintun);
>          openvpn_exit(OPENVPN_EXIT_STATUS_GOOD); /* exit point */
>      }
>      else if (streq(p[0], "show-net") && !p[1])
> diff --git a/src/openvpn/options.h b/src/openvpn/options.h
> index e2b3893..670b70a 100644
> --- a/src/openvpn/options.h
> +++ b/src/openvpn/options.h
> @@ -614,6 +614,7 @@ struct options
>      bool show_net_up;
>      int route_method;
>      bool block_outside_dns;
> +    bool wintun;
>  #endif
>
>      bool use_peer_id;
> diff --git a/src/openvpn/socket.c b/src/openvpn/socket.c
> index c472cf1..4352869 100644
> --- a/src/openvpn/socket.c
> +++ b/src/openvpn/socket.c
> @@ -1635,8 +1635,8 @@ static void
>  socket_frame_init(const struct frame *frame, struct link_socket *sock)
>  {
>  #ifdef _WIN32
> -    overlapped_io_init(&sock->reads, frame, FALSE, false);
> -    overlapped_io_init(&sock->writes, frame, TRUE, false);
> +    overlapped_io_init(&sock->reads, frame, true, false, false);
> +    overlapped_io_init(&sock->writes, frame, false, false, false);
>      sock->rw_handle.read = sock->reads.overlapped.hEvent;
>      sock->rw_handle.write = sock->writes.overlapped.hEvent;
>  #endif
> diff --git a/src/openvpn/syshead.h b/src/openvpn/syshead.h
> index 2b4c49f..e7cbf25 100644
> --- a/src/openvpn/syshead.h
> +++ b/src/openvpn/syshead.h
> @@ -39,6 +39,7 @@
>  #ifdef _WIN32
>  #include <windows.h>
>  #include <winsock2.h>
> +#include <tlhelp32.h>
>  #define sleep(x) Sleep((x)*1000)
>  #define random rand
>  #define srandom srand
> diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
> index e929b50..5fd8339 100644
> --- a/src/openvpn/tun.c
> +++ b/src/openvpn/tun.c
> @@ -788,8 +788,8 @@ init_tun_post(struct tuntap *tt,
>  {
>      tt->options = *options;
>  #ifdef _WIN32
> -    overlapped_io_init(&tt->reads, frame, FALSE, true);
> -    overlapped_io_init(&tt->writes, frame, TRUE, true);
> +    overlapped_io_init(&tt->reads, frame, true, true, tt->wintun);
> +    overlapped_io_init(&tt->writes, frame, false, true, tt->wintun);
>      tt->rw_handle.read = tt->reads.overlapped.hEvent;
>      tt->rw_handle.write = tt->writes.overlapped.hEvent;
>      tt->adapter_index = TUN_ADAPTER_INDEX_INVALID;
> @@ -3430,7 +3430,7 @@ tun_finalize(
>  }
>
>  const struct tap_reg *
> -get_tap_reg(struct gc_arena *gc)
> +get_tap_reg(struct gc_arena *gc, bool wintun)
>  {
>      HKEY adapter_key;
>      LONG status;
> @@ -3461,6 +3461,20 @@ get_tap_reg(struct gc_arena *gc)
>          char net_cfg_instance_id_string[] = "NetCfgInstanceId";
>          char net_cfg_instance_id[256];
>          DWORD data_type;
> +        char expected_component_id[256];
> +        char expected_root_component_id[256];
> +        char net_luid_index_string[] = "NetLuidIndex";
> +        DWORD luid_index;
> +
> +        if (wintun)
> +        {
> +            strcpy(expected_component_id, WINTUN_COMPONENT_ID);
> +        }
> +        else
> +        {
> +            strcpy(expected_component_id, TAP_WIN_COMPONENT_ID);
> +        }
> +        openvpn_snprintf(expected_root_component_id,
> sizeof(expected_root_component_id), "root\\%s", expected_component_id);
>
>          len = sizeof(enum_name);
>          status = RegEnumKeyEx(
> @@ -3525,13 +3539,27 @@ get_tap_reg(struct gc_arena *gc)
>
>                  if (status == ERROR_SUCCESS && data_type == REG_SZ)
>                  {
> -                    if (!strcmp(component_id, TAP_WIN_COMPONENT_ID) ||
> -                        !strcmp(component_id, "root\\"
> TAP_WIN_COMPONENT_ID))
> +                    if (!strcmp(component_id, expected_component_id) ||
> +                        !strcmp(component_id, expected_root_component_id))
>                      {
>                          struct tap_reg *reg;
>                          ALLOC_OBJ_CLEAR_GC(reg, struct tap_reg, gc);
>                          reg->guid = string_alloc(net_cfg_instance_id, gc);
>
> +                        len = sizeof(luid_index);
> +                        status = RegQueryValueEx(
> +                            unit_key,
> +                            net_luid_index_string,
> +                            NULL,
> +                            &data_type,
> +                            (LPBYTE)&luid_index,
> +                            &len);
> +
> +                        if (status == ERROR_SUCCESS && data_type ==
> REG_DWORD)
> +                        {
> +                            reg->luid_index = luid_index;
> +                        }
> +
>                          /* link into return list */
>                          if (!first)
>                          {
> @@ -3744,7 +3772,7 @@ show_valid_win32_tun_subnets(void)
>  }
>
>  void
> -show_tap_win_adapters(int msglev, int warnlev)
> +show_tap_win_adapters(int msglev, int warnlev, bool wintun)
>  {
>      struct gc_arena gc = gc_new();
>
> @@ -3758,10 +3786,10 @@ show_tap_win_adapters(int msglev, int warnlev)
>      const struct tap_reg *tr1;
>      const struct panel_reg *pr;
>
> -    const struct tap_reg *tap_reg = get_tap_reg(&gc);
> +    const struct tap_reg *tap_reg = get_tap_reg(&gc, wintun);
>      const struct panel_reg *panel_reg = get_panel_reg(&gc);
>
> -    msg(msglev, "Available TAP-WIN32 adapters [name, GUID]:");
> +    msg(msglev, "Available %s adapters [name, GUID]:", wintun ? "WinTun"
> : "TAP-WIN32");
>
>      /* loop through each TAP-Windows adapter registry entry */
>      for (tr = tap_reg; tr != NULL; tr = tr->next)
> @@ -3883,13 +3911,14 @@ at_least_one_tap_win(const struct tap_reg *tap_reg)
>  }
>
>  /*
> - * Get an adapter GUID and optional actual_name from the
> + * Get an adapter GUID, LUID index and optional actual_name from the
>   * registry for the TAP device # = device_number.
>   */
>  static const char *
>  get_unspecified_device_guid(const int device_number,
>                              char *actual_name,
>                              int actual_name_size,
> +                            int *luid_index,
>                              const struct tap_reg *tap_reg_src,
>                              const struct panel_reg *panel_reg_src,
>                              struct gc_arena *gc)
> @@ -3938,6 +3967,11 @@ get_unspecified_device_guid(const int device_number,
>          }
>      }
>
> +    if (luid_index)
> +    {
> +        *luid_index = tap_reg->luid_index;
> +    }
> +
>      /* Save GUID for return value */
>      ret = alloc_buf_gc(256, gc);
>      buf_printf(&ret, "%s", tap_reg->guid);
> @@ -4536,7 +4570,7 @@ get_adapter_index_flexible(const char *name)  /*
> actual name or GUID */
>      }
>      if (index == TUN_ADAPTER_INDEX_INVALID)
>      {
> -        const struct tap_reg *tap_reg = get_tap_reg(&gc);
> +        const struct tap_reg *tap_reg = get_tap_reg(&gc, false);
>          const struct panel_reg *panel_reg = get_panel_reg(&gc);
>          const char *guid = name_to_guid(name, tap_reg, panel_reg);
>          index = get_adapter_index_method_1(guid);
> @@ -4663,7 +4697,7 @@ void
>  tap_allow_nonadmin_access(const char *dev_node)
>  {
>      struct gc_arena gc = gc_new();
> -    const struct tap_reg *tap_reg = get_tap_reg(&gc);
> +    const struct tap_reg *tap_reg = get_tap_reg(&gc, false);
>      const struct panel_reg *panel_reg = get_panel_reg(&gc);
>      const char *device_guid = NULL;
>      HANDLE hand;
> @@ -4717,6 +4751,7 @@ tap_allow_nonadmin_access(const char *dev_node)
>                                                        actual_buffer,
>
>  sizeof(actual_buffer),
>                                                        tap_reg,
> +                                                      NULL,
>                                                        panel_reg,
>                                                        &gc);
>
> @@ -5239,7 +5274,7 @@ out:
>  static const char *
>  netsh_get_id(const char *dev_node, struct gc_arena *gc)
>  {
> -    const struct tap_reg *tap_reg = get_tap_reg(gc);
> +    const struct tap_reg *tap_reg = get_tap_reg(gc, false);
>      const struct panel_reg *panel_reg = get_panel_reg(gc);
>      struct buffer actual = alloc_buf_gc(256, gc);
>      const char *guid;
> @@ -5252,9 +5287,9 @@ netsh_get_id(const char *dev_node, struct gc_arena
> *gc)
>      }
>      else
>      {
> -        guid = get_unspecified_device_guid(0, BPTR(&actual),
> BCAP(&actual), tap_reg, panel_reg, gc);
> +        guid = get_unspecified_device_guid(0, BPTR(&actual),
> BCAP(&actual), NULL, tap_reg, panel_reg, gc);
>
> -        if (get_unspecified_device_guid(1, NULL, 0, tap_reg, panel_reg,
> gc)) /* ambiguous if more than one TAP-Windows adapter */
> +        if (get_unspecified_device_guid(1, NULL, 0, NULL, tap_reg,
> panel_reg, gc)) /* ambiguous if more than one TAP-Windows adapter */
>          {
>              guid = NULL;
>          }
> @@ -5528,6 +5563,7 @@ open_tun(const char *dev, const char *dev_type,
> const char *dev_node, struct tun
>      struct gc_arena gc = gc_new();
>      char device_path[256];
>      const char *device_guid = NULL;
> +    DWORD device_luid_index = 0;
>      DWORD len;
>      bool dhcp_masq = false;
>      bool dhcp_masq_post = false;
> @@ -5554,7 +5590,7 @@ open_tun(const char *dev, const char *dev_type,
> const char *dev_node, struct tun
>       * Lookup the device name in the registry, using the --dev-node high
> level name.
>       */
>      {
> -        const struct tap_reg *tap_reg = get_tap_reg(&gc);
> +        const struct tap_reg *tap_reg = get_tap_reg(&gc, tt->wintun);
>          const struct panel_reg *panel_reg = get_panel_reg(&gc);
>          char actual_buffer[256];
>
> @@ -5601,6 +5637,7 @@ open_tun(const char *dev, const char *dev_type,
> const char *dev_node, struct tun
>                  device_guid = get_unspecified_device_guid(device_number,
>                                                            actual_buffer,
>
>  sizeof(actual_buffer),
> +
> &device_luid_index,
>                                                            tap_reg,
>                                                            panel_reg,
>                                                            &gc);
> @@ -5610,11 +5647,28 @@ open_tun(const char *dev, const char *dev_type,
> const char *dev_node, struct tun
>                      msg(M_FATAL, "All TAP-Windows adapters on this system
> are currently in use.");
>                  }
>
> -                /* Open Windows TAP-Windows adapter */
> -                openvpn_snprintf(device_path, sizeof(device_path),
> "%s%s%s",
> -                                 USERMODEDEVICEDIR,
> -                                 device_guid,
> -                                 TAP_WIN_SUFFIX);
> +                /* Open wintun / tap-windows6 adapter */
> +                if (tt->wintun)
> +                {
> +                    openvpn_snprintf(device_path, sizeof(device_path),
> "%sWINTUN%lu",
> +                                     USERMODEDEVICEDIR,
> +                                     device_luid_index);
> +                }
> +                else
> +                {
> +                    openvpn_snprintf(device_path, sizeof(device_path),
> "%s%s%s",
> +                                     USERMODEDEVICEDIR,
> +                                     device_guid,
> +                                     TAP_WIN_SUFFIX);
> +                }
> +
> +                if (tt->wintun)
> +                {
> +                    if (!impersonate_as_system())
> +                    {
> +                        msg(M_FATAL, "ERROR:  Failed to impersonate as
> SYSTEM, make sure process is running under privileged account");
> +                    }
> +                }
>
>                  tt->hand = CreateFile(
>                      device_path,
> @@ -5626,6 +5680,14 @@ open_tun(const char *dev, const char *dev_type,
> const char *dev_node, struct tun
>                      0
>                      );
>
> +                if (tt->wintun)
> +                {
> +                    if (!RevertToSelf())
> +                    {
> +                        msg(M_FATAL, "ERROR:  RevertToSelf error: %lu",
> GetLastError());
> +                    }
> +                }
> +
>                  if (tt->hand == INVALID_HANDLE_VALUE)
>                  {
>                      msg(D_TUNTAP_INFO, "CreateFile failed on TAP device:
> %s", device_path);
> @@ -5644,12 +5706,14 @@ open_tun(const char *dev, const char *dev_type,
> const char *dev_node, struct tun
>          tt->actual_name = string_alloc(actual_buffer, NULL);
>      }
>
> -    msg(M_INFO, "TAP-WIN32 device [%s] opened: %s", tt->actual_name,
> device_path);
> +    msg(M_INFO, "%s device device [%s] opened: %s", tt->wintun ? "Wintun"
> : "TAP - WIN32", tt->actual_name, device_path);
>      tt->adapter_index = get_adapter_index(device_guid);
>
> -    /* get driver version info */
> +    if (!tt->wintun)
>      {
> +        /* get driver version info */
>          ULONG info[3];
> +        ULONG mtu;
>          CLEAR(info);
>          if (DeviceIoControl(tt->hand, TAP_WIN_IOCTL_GET_VERSION,
>                              &info, sizeof(info),
> @@ -5684,11 +5748,8 @@ open_tun(const char *dev, const char *dev_type,
> const char *dev_node, struct tun
>          {
>              msg( M_FATAL, "ERROR:  Tap-Win32 driver version %d.%d is
> buggy regarding small IPv4 packets in TUN mode. Upgrade your Tap-Win32
> driver.", (int) info[0], (int) info[1] );
>          }
> -    }
>
> -    /* get driver MTU */
> -    {
> -        ULONG mtu;
> +        /* get driver MTU */
>          if (DeviceIoControl(tt->hand, TAP_WIN_IOCTL_GET_MTU,
>                              &mtu, sizeof(mtu),
>                              &mtu, sizeof(mtu), &len, NULL))
> @@ -5746,7 +5807,7 @@ open_tun(const char *dev, const char *dev_type,
> const char *dev_node, struct tun
>
>      /* set point-to-point mode if TUN device */
>
> -    if (tt->type == DEV_TYPE_TUN)
> +    if (tt->type == DEV_TYPE_TUN && !tt->wintun)
>      {
>          if (!tt->did_ifconfig_setup && !tt->did_ifconfig_ipv6_setup)
>          {
> @@ -5834,71 +5895,80 @@ open_tun(const char *dev, const char *dev_type,
> const char *dev_node, struct tun
>              ep[2] = dhcp_masq_addr(tt->local, tt->adapter_netmask,
> tt->options.dhcp_masq_custom_offset ? tt->options.dhcp_masq_offset : 0);
>          }
>
> -        /* lease time in seconds */
> -        ep[3] = (uint32_t) tt->options.dhcp_lease_time;
> +        if (!tt->wintun)
> +        {
> +            /* lease time in seconds */
> +            ep[3] = (uint32_t)tt->options.dhcp_lease_time;
>
> -        ASSERT(ep[3] > 0);
> +            ASSERT(ep[3] > 0);
>
>  #ifndef SIMULATE_DHCP_FAILED /* this code is disabled to simulate bad
> DHCP negotiation */
> -        if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_CONFIG_DHCP_MASQ,
> -                             ep, sizeof(ep),
> -                             ep, sizeof(ep), &len, NULL))
> -        {
> -            msg(M_FATAL, "ERROR: The TAP-Windows driver rejected a
> DeviceIoControl call to set TAP_WIN_IOCTL_CONFIG_DHCP_MASQ mode");
> -        }
> +            if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_CONFIG_DHCP_MASQ,
> +                ep, sizeof(ep),
> +                ep, sizeof(ep), &len, NULL))
> +            {
> +                msg(M_FATAL, "ERROR: The TAP-Windows driver rejected a
> DeviceIoControl call to set TAP_WIN_IOCTL_CONFIG_DHCP_MASQ mode");
> +            }
>
> -        msg(M_INFO, "Notified TAP-Windows driver to set a DHCP IP/netmask
> of %s/%s on interface %s [DHCP-serv: %s, lease-time: %d]",
> -            print_in_addr_t(tt->local, 0, &gc),
> -            print_in_addr_t(tt->adapter_netmask, 0, &gc),
> -            device_guid,
> -            print_in_addr_t(ep[2], IA_NET_ORDER, &gc),
> -            ep[3]
> +            msg(M_INFO, "Notified TAP-Windows driver to set a DHCP
> IP/netmask of %s/%s on interface %s [DHCP-serv: %s, lease-time: %d]",
> +                print_in_addr_t(tt->local, 0, &gc),
> +                print_in_addr_t(tt->adapter_netmask, 0, &gc),
> +                device_guid,
> +                print_in_addr_t(ep[2], IA_NET_ORDER, &gc),
> +                ep[3]
>              );
>
> -        /* user-supplied DHCP options capability */
> -        if (tt->options.dhcp_options)
> -        {
> -            struct buffer buf = alloc_buf(256);
> -            if (build_dhcp_options_string(&buf, &tt->options))
> +            /* user-supplied DHCP options capability */
> +            if (tt->options.dhcp_options)
>              {
> -                msg(D_DHCP_OPT, "DHCP option string: %s",
> format_hex(BPTR(&buf), BLEN(&buf), 0, &gc));
> -                if (!DeviceIoControl(tt->hand,
> TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT,
> -                                     BPTR(&buf), BLEN(&buf),
> -                                     BPTR(&buf), BLEN(&buf), &len, NULL))
> +                struct buffer buf = alloc_buf(256);
> +                if (build_dhcp_options_string(&buf, &tt->options))
>                  {
> -                    msg(M_FATAL, "ERROR: The TAP-Windows driver rejected
> a TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT DeviceIoControl call");
> +                    msg(D_DHCP_OPT, "DHCP option string: %s",
> format_hex(BPTR(&buf), BLEN(&buf), 0, &gc));
> +                    if (!DeviceIoControl(tt->hand,
> TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT,
> +                        BPTR(&buf), BLEN(&buf),
> +                        BPTR(&buf), BLEN(&buf), &len, NULL))
> +                    {
> +                        msg(M_FATAL, "ERROR: The TAP-Windows driver
> rejected a TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT DeviceIoControl call");
> +                    }
>                  }
> +                else
> +                {
> +                    msg(M_WARN, "DHCP option string not set due to
> error");
> +                }
> +                free_buf(&buf);
>              }
> -            else
> -            {
> -                msg(M_WARN, "DHCP option string not set due to error");
> -            }
> -            free_buf(&buf);
> -        }
>  #endif /* ifndef SIMULATE_DHCP_FAILED */
> +        }
> +
> +
>      }
>
> -    /* set driver media status to 'connected' */
> +    if (!tt->wintun)
>      {
> -        ULONG status = TRUE;
> -        if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_SET_MEDIA_STATUS,
> -                             &status, sizeof(status),
> -                             &status, sizeof(status), &len, NULL))
> +        /* set driver media status to 'connected' */
>          {
> -            msg(M_WARN, "WARNING: The TAP-Windows driver rejected a
> TAP_WIN_IOCTL_SET_MEDIA_STATUS DeviceIoControl call.");
> +            ULONG status = TRUE;
> +            if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_SET_MEDIA_STATUS,
> +                &status, sizeof(status),
> +                &status, sizeof(status), &len, NULL))
> +            {
> +              msg(M_WARN, "WARNING: The TAP-Windows driver rejected a
> TAP_WIN_IOCTL_SET_MEDIA_STATUS DeviceIoControl call.");
> +            }
>          }
> -    }
>
> -    /* possible wait for adapter to come up */
> -    {
> -        int s = tt->options.tap_sleep;
> -        if (s > 0)
> +        /* possible wait for adapter to come up */
>          {
> -            msg(M_INFO, "Sleeping for %d seconds...", s);
> -            management_sleep(s);
> +            int s = tt->options.tap_sleep;
> +            if (s > 0)
> +            {
> +                msg(M_INFO, "Sleeping for %d seconds...", s);
> +                management_sleep(s);
> +            }
>          }
>      }
>
> +
>      /* possibly use IP Helper API to set IP address on adapter */
>      {
>          const DWORD index = tt->adapter_index;
> diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h
> index 9ed8ef0..6591d59 100644
> --- a/src/openvpn/tun.h
> +++ b/src/openvpn/tun.h
> @@ -37,6 +37,10 @@
>  #include "proto.h"
>  #include "misc.h"
>
> +#ifdef _WIN32
> +#define WINTUN_COMPONENT_ID "wintun"
> +#endif
> +
>  #if defined(_WIN32) || defined(TARGET_ANDROID)
>
>  #define TUN_ADAPTER_INDEX_INVALID ((DWORD)-1)
> @@ -174,6 +178,9 @@ struct tuntap
>       * ~0 if undefined */
>      DWORD adapter_index;
>
> +    bool wintun; /* true if wintun is used instead of tap-windows6 */
> +    char wintun_padding[16];
> +
>      int standby_iter;
>  #else  /* ifdef _WIN32 */
>      int fd; /* file descriptor for TUN/TAP dev */
> @@ -337,6 +344,7 @@ route_order(void)
>  struct tap_reg
>  {
>      const char *guid;
> +    DWORD luid_index;
>      struct tap_reg *next;
>  };
>
> @@ -374,7 +382,7 @@ DWORD adapter_index_of_ip(const IP_ADAPTER_INFO *list,
>                            int *count,
>                            in_addr_t *netmask);
>
> -void show_tap_win_adapters(int msglev, int warnlev);
> +void show_tap_win_adapters(int msglev, int warnlev, bool wintun);
>
>  void show_adapters(int msglev);
>
> @@ -465,6 +473,30 @@ read_tun_buffered(struct tuntap *tt, struct buffer
> *buf)
>  static inline int
>  write_tun_buffered(struct tuntap *tt, struct buffer *buf)
>  {
> +    if (tt->wintun)
> +    {
> +        int len = BLEN(buf);
> +
> +        /* variable len end padding */
> +        int end_padding_len = 16 - (len & 15);
> +        if (!buf_write(buf, tt->wintun_padding, end_padding_len))
> +        {
> +            return -1;
> +        }
> +
> +        /* 12 bytes start padding */
> +        if (!buf_write_prepend(buf, tt->wintun_padding, 12))
> +        {
> +            return -1;
> +        }
> +
> +        /* 4 bytes size */
> +        if (!buf_write_prepend(buf, &len, 4))
> +        {
> +            return -1;
> +        }
> +    }
> +
>      return tun_write_win32(tt, buf);
>  }
>
> diff --git a/src/openvpn/win32.c b/src/openvpn/win32.c
> index eb4c030..4b4a2b1 100644
> --- a/src/openvpn/win32.c
> +++ b/src/openvpn/win32.c
> @@ -164,20 +164,34 @@ init_security_attributes_allow_all(struct
> security_attributes *obj)
>  void
>  overlapped_io_init(struct overlapped_io *o,
>                     const struct frame *frame,
> -                   BOOL event_state,
> -                   bool tuntap_buffer)  /* if true: tuntap buffer, if
> false: socket buffer */
> +                   bool reads, /* if true: reads buffer, if false: writes
> buffer */
> +                   bool tuntap_buffer,  /* if true: tuntap buffer, if
> false: socket buffer */
> +                   bool wintun)
>  {
>      CLEAR(*o);
>
>      /* manual reset event, initially set according to event_state */
> -    o->overlapped.hEvent = CreateEvent(NULL, TRUE, event_state, NULL);
> +    o->overlapped.hEvent = CreateEvent(NULL, TRUE, reads ? FALSE : TRUE,
> NULL);
>      if (o->overlapped.hEvent == NULL)
>      {
>          msg(M_ERR, "Error: overlapped_io_init: CreateEvent failed");
>      }
>
> -    /* allocate buffer for overlapped I/O */
> -    alloc_buf_sock_tun(&o->buf_init, frame, tuntap_buffer, 0);
> +    if (wintun && tuntap_buffer && reads)
> +    {
> +        /*
> +         * wintun could return up to 256 packets,
> +         * each packet is accompanied with size, start and end padding
> +        */
> +        int buf_size = (MAX_RW_SIZE_TUN(frame) + 4 + 12 + 15) * 256;
> +        o->buf_init = alloc_buf(buf_size);
> +        o->buf_init.len = buf_size;
> +    }
> +    else
> +    {
> +        /* allocate buffer for overlapped I/O */
> +        alloc_buf_sock_tun(&o->buf_init, frame, tuntap_buffer, 0);
> +    }
>  }
>
>  void
> @@ -1493,4 +1507,99 @@ send_msg_iservice(HANDLE pipe, const void *data,
> size_t size,
>      return ret;
>  }
>
> +bool
> +impersonate_as_system()
> +{
> +    HANDLE thread_token, process_snapshot, winlogon_process,
> winlogon_token, duplicated_token, file_handle;
> +    PROCESSENTRY32 entry;
> +    BOOL ret;
> +    DWORD pid = 0;
> +    TOKEN_PRIVILEGES privileges;
> +
> +    CLEAR(entry);
> +    CLEAR(privileges);
> +
> +    entry.dwSize = sizeof(PROCESSENTRY32);
> +
> +    privileges.PrivilegeCount = 1;
> +    privileges.Privileges->Attributes = SE_PRIVILEGE_ENABLED;
> +
> +    if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME,
> &privileges.Privileges[0].Luid))
> +    {
> +        return false;
> +    }
> +
> +    if (!ImpersonateSelf(SecurityImpersonation))
> +    {
> +        return false;
> +    }
> +
> +    if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES,
> FALSE, &thread_token))
> +    {
> +        RevertToSelf();
> +        return false;
> +    }
> +    if (!AdjustTokenPrivileges(thread_token, FALSE, &privileges,
> sizeof(privileges), NULL, NULL))
> +    {
> +        CloseHandle(thread_token);
> +        RevertToSelf();
> +        return false;
> +    }
> +    CloseHandle(thread_token);
> +
> +    process_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
> +    if (process_snapshot == INVALID_HANDLE_VALUE)
> +    {
> +        RevertToSelf();
> +        return false;
> +    }
> +    for (ret = Process32First(process_snapshot, &entry); ret; ret =
> Process32Next(process_snapshot, &entry))
> +    {
> +        if (!_stricmp(entry.szExeFile, "winlogon.exe"))
> +        {
> +            pid = entry.th32ProcessID;
> +            break;
> +        }
> +    }
> +    CloseHandle(process_snapshot);
> +    if (!pid)
> +    {
> +        RevertToSelf();
> +        return false;
> +    }
> +
> +    winlogon_process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
> +    if (!winlogon_process)
> +    {
> +        RevertToSelf();
> +        return false;
> +    }
> +
> +    if (!OpenProcessToken(winlogon_process, TOKEN_IMPERSONATE |
> TOKEN_DUPLICATE, &winlogon_token))
> +    {
> +        CloseHandle(winlogon_process);
> +        RevertToSelf();
> +        return false;
> +    }
> +    CloseHandle(winlogon_process);
> +
> +    if (!DuplicateToken(winlogon_token, SecurityImpersonation,
> &duplicated_token))
> +    {
> +        CloseHandle(winlogon_token);
> +        RevertToSelf();
> +        return false;
> +    }
> +    CloseHandle(winlogon_token);
> +
> +    if (!SetThreadToken(NULL, duplicated_token))
> +    {
> +        CloseHandle(duplicated_token);
> +        RevertToSelf();
> +        return false;
> +    }
> +    CloseHandle(duplicated_token);
> +
> +    return true;
> +}
> +
>  #endif /* ifdef _WIN32 */
> diff --git a/src/openvpn/win32.h b/src/openvpn/win32.h
> index 4814bbc..e511b56 100644
> --- a/src/openvpn/win32.h
> +++ b/src/openvpn/win32.h
> @@ -212,10 +212,12 @@ struct overlapped_io {
>      struct buffer buf;
>  };
>
> -void overlapped_io_init(struct overlapped_io *o,
> -                        const struct frame *frame,
> -                        BOOL event_state,
> -                        bool tuntap_buffer);
> +void
> +overlapped_io_init(struct overlapped_io *o,
> +                   const struct frame *frame,
> +                   bool reads, /* if true: reads buffer, if false: writes
> buffer */
> +                   bool tuntap_buffer,  /* if true: tuntap buffer, if
> false: socket buffer */
> +                   bool wintun);
>
>  void overlapped_io_close(struct overlapped_io *o);
>
> @@ -323,5 +325,14 @@ bool send_msg_iservice(HANDLE pipe, const void *data,
> size_t size,
>  int
>  openvpn_execve(const struct argv *a, const struct env_set *es, const
> unsigned int flags);
>
> +/**
> + * Impersonates current thread as SYSTEM, required
> + * to open Wintun device.
> + *
> + * @returns     True if it succeeds, false if it fails.
> + */
> +bool
> +impersonate_as_system();
> +
>  #endif /* ifndef OPENVPN_WIN32_H */
>  #endif /* ifdef _WIN32 */
> --
> 2.7.4
>
>

-- 
-Lev
_______________________________________________
Openvpn-devel mailing list
Openvpn-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to