OpenVPN
interactive.c
Go to the documentation of this file.
1/*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single TCP/UDP port, with support for SSL/TLS-based
4 * session authentication and key exchange,
5 * packet encryption, packet authentication, and
6 * packet compression.
7 *
8 * Copyright (C) 2012-2024 Heiko Hund <heiko.hund@sophos.com>
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, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 */
23
24
25#include "service.h"
26
27#include <ws2tcpip.h>
28#include <iphlpapi.h>
29#include <userenv.h>
30#include <accctrl.h>
31#include <aclapi.h>
32#include <stdio.h>
33#include <sddl.h>
34#include <shellapi.h>
35#include <mstcpip.h>
36#include <inttypes.h>
37
38#include <versionhelpers.h>
39
40#include "openvpn-msg.h"
41#include "validate.h"
42#include "wfp_block.h"
43#include "ring_buffer.h"
44
45#define IO_TIMEOUT 2000 /*ms*/
46
47#define ERROR_OPENVPN_STARTUP 0x20000000
48#define ERROR_STARTUP_DATA 0x20000001
49#define ERROR_MESSAGE_DATA 0x20000002
50#define ERROR_MESSAGE_TYPE 0x20000003
51
52static SERVICE_STATUS_HANDLE service;
53static SERVICE_STATUS status = { .dwServiceType = SERVICE_WIN32_SHARE_PROCESS };
54static HANDLE exit_event = NULL;
56static HANDLE rdns_semaphore = NULL;
57#define RDNS_TIMEOUT 600 /* seconds to wait for the semaphore */
58
59#define TUN_IOCTL_REGISTER_RINGS CTL_CODE(51820U, 0x970U, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
60
63 TEXT(PACKAGE_NAME) TEXT("ServiceInteractive"),
64 TEXT(PACKAGE_NAME) TEXT(" Interactive Service"),
66 SERVICE_AUTO_START
67};
68
69
70typedef struct {
71 WCHAR *directory;
72 WCHAR *options;
73 WCHAR *std_input;
75
76
77/* Datatype for linked lists */
78typedef struct _list_item {
80 LPVOID data;
82
83
84/* Datatypes for undo information */
97
98typedef struct {
99 HANDLE engine;
100 int index;
104
105typedef struct {
109
122
123static DWORD
124AddListItem(list_item_t **pfirst, LPVOID data)
125{
126 list_item_t *new_item = malloc(sizeof(list_item_t));
127 if (new_item == NULL)
128 {
129 return ERROR_OUTOFMEMORY;
130 }
131
132 new_item->next = *pfirst;
133 new_item->data = data;
134
135 *pfirst = new_item;
136 return NO_ERROR;
137}
138
139typedef BOOL (*match_fn_t) (LPVOID item, LPVOID ctx);
140
141static LPVOID
142RemoveListItem(list_item_t **pfirst, match_fn_t match, LPVOID ctx)
143{
144 LPVOID data = NULL;
145 list_item_t **pnext;
146
147 for (pnext = pfirst; *pnext; pnext = &(*pnext)->next)
148 {
149 list_item_t *item = *pnext;
150 if (!match(item->data, ctx))
151 {
152 continue;
153 }
154
155 /* Found item, remove from the list and free memory */
156 *pnext = item->next;
157 data = item->data;
158 free(item);
159 break;
160 }
161 return data;
162}
163
164
165static HANDLE
166CloseHandleEx(LPHANDLE handle)
167{
168 if (handle && *handle && *handle != INVALID_HANDLE_VALUE)
169 {
170 CloseHandle(*handle);
171 *handle = INVALID_HANDLE_VALUE;
172 }
173 return INVALID_HANDLE_VALUE;
174}
175
176static void
178{
179 if (ring && *ring)
180 {
181 UnmapViewOfFile(*ring);
182 *ring = NULL;
183 }
184}
185
186static void
188{
189 OvpnUnmapViewOfFile(&ring_buffer_maps->send_ring);
190 OvpnUnmapViewOfFile(&ring_buffer_maps->receive_ring);
191}
192
193static HANDLE
194InitOverlapped(LPOVERLAPPED overlapped)
195{
196 ZeroMemory(overlapped, sizeof(OVERLAPPED));
197 overlapped->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
198 return overlapped->hEvent;
199}
200
201
202static BOOL
203ResetOverlapped(LPOVERLAPPED overlapped)
204{
205 HANDLE io_event = overlapped->hEvent;
206 if (!ResetEvent(io_event))
207 {
208 return FALSE;
209 }
210 ZeroMemory(overlapped, sizeof(OVERLAPPED));
211 overlapped->hEvent = io_event;
212 return TRUE;
213}
214
215
216typedef enum {
219 write
221
222static DWORD
223AsyncPipeOp(async_op_t op, HANDLE pipe, LPVOID buffer, DWORD size, DWORD count, LPHANDLE events)
224{
225 DWORD i;
226 BOOL success;
227 HANDLE io_event;
228 DWORD res, bytes = 0;
229 OVERLAPPED overlapped;
230 LPHANDLE handles = NULL;
231
232 io_event = InitOverlapped(&overlapped);
233 if (!io_event)
234 {
235 goto out;
236 }
237
238 handles = malloc((count + 1) * sizeof(HANDLE));
239 if (!handles)
240 {
241 goto out;
242 }
243
244 if (op == write)
245 {
246 success = WriteFile(pipe, buffer, size, NULL, &overlapped);
247 }
248 else
249 {
250 success = ReadFile(pipe, buffer, size, NULL, &overlapped);
251 }
252 if (!success && GetLastError() != ERROR_IO_PENDING && GetLastError() != ERROR_MORE_DATA)
253 {
254 goto out;
255 }
256
257 handles[0] = io_event;
258 for (i = 0; i < count; i++)
259 {
260 handles[i + 1] = events[i];
261 }
262
263 res = WaitForMultipleObjects(count + 1, handles, FALSE,
264 op == peek ? INFINITE : IO_TIMEOUT);
265 if (res != WAIT_OBJECT_0)
266 {
267 CancelIo(pipe);
268 goto out;
269 }
270
271 if (op == peek)
272 {
273 PeekNamedPipe(pipe, NULL, 0, NULL, &bytes, NULL);
274 }
275 else
276 {
277 GetOverlappedResult(pipe, &overlapped, &bytes, TRUE);
278 }
279
280out:
281 CloseHandleEx(&io_event);
282 free(handles);
283 return bytes;
284}
285
286static DWORD
287PeekNamedPipeAsync(HANDLE pipe, DWORD count, LPHANDLE events)
288{
289 return AsyncPipeOp(peek, pipe, NULL, 0, count, events);
290}
291
292static DWORD
293ReadPipeAsync(HANDLE pipe, LPVOID buffer, DWORD size, DWORD count, LPHANDLE events)
294{
295 return AsyncPipeOp(read, pipe, buffer, size, count, events);
296}
297
298static DWORD
299WritePipeAsync(HANDLE pipe, LPVOID data, DWORD size, DWORD count, LPHANDLE events)
300{
301 return AsyncPipeOp(write, pipe, data, size, count, events);
302}
303
304static VOID
305ReturnProcessId(HANDLE pipe, DWORD pid, DWORD count, LPHANDLE events)
306{
307 const WCHAR msg[] = L"Process ID";
308 WCHAR buf[22 + _countof(msg)]; /* 10 chars each for error and PID and 2 for line breaks */
309
310 /*
311 * Same format as error messages (3 line string) with error = 0 in
312 * 0x%08x format, PID on line 2 and a description "Process ID" on line 3
313 */
314 swprintf(buf, _countof(buf), L"0x%08x\n0x%08x\n%ls", 0, pid, msg);
315
316 WritePipeAsync(pipe, buf, (DWORD)(wcslen(buf) * 2), count, events);
317}
318
319static VOID
320ReturnError(HANDLE pipe, DWORD error, LPCWSTR func, DWORD count, LPHANDLE events)
321{
322 DWORD result_len;
323 LPWSTR result = L"0xffffffff\nFormatMessage failed\nCould not return result";
324 DWORD_PTR args[] = {
325 (DWORD_PTR) error,
326 (DWORD_PTR) func,
327 (DWORD_PTR) ""
328 };
329
330 if (error != ERROR_OPENVPN_STARTUP)
331 {
332 FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM
333 |FORMAT_MESSAGE_ALLOCATE_BUFFER
334 |FORMAT_MESSAGE_IGNORE_INSERTS,
335 0, error, 0, (LPWSTR) &args[2], 0, NULL);
336 }
337
338 result_len = FormatMessageW(FORMAT_MESSAGE_FROM_STRING
339 |FORMAT_MESSAGE_ALLOCATE_BUFFER
340 |FORMAT_MESSAGE_ARGUMENT_ARRAY,
341 L"0x%1!08x!\n%2!s!\n%3!s!", 0, 0,
342 (LPWSTR) &result, 0, (va_list *) args);
343
344 WritePipeAsync(pipe, result, (DWORD)(wcslen(result) * 2), count, events);
346
347 if (error != ERROR_OPENVPN_STARTUP)
348 {
349 LocalFree((LPVOID) args[2]);
350 }
351 if (result_len)
352 {
353 LocalFree(result);
354 }
355}
356
357
358static VOID
359ReturnLastError(HANDLE pipe, LPCWSTR func)
360{
361 ReturnError(pipe, GetLastError(), func, 1, &exit_event);
362}
363
364/*
365 * Validate options against a white list. Also check the config_file is
366 * inside the config_dir. The white list is defined in validate.c
367 * Returns true on success, false on error with reason set in errmsg.
368 */
369static BOOL
370ValidateOptions(HANDLE pipe, const WCHAR *workdir, const WCHAR *options, WCHAR *errmsg, DWORD capacity)
371{
372 WCHAR **argv;
373 int argc;
374 BOOL ret = FALSE;
375 int i;
376 const WCHAR *msg1 = L"You have specified a config file location (%ls relative to %ls)"
377 L" that requires admin approval. This error may be avoided"
378 L" by adding your account to the \"%ls\" group";
379
380 const WCHAR *msg2 = L"You have specified an option (%ls) that may be used"
381 L" only with admin approval. This error may be avoided"
382 L" by adding your account to the \"%ls\" group";
383
384 argv = CommandLineToArgvW(options, &argc);
385
386 if (!argv)
387 {
388 swprintf(errmsg, capacity,
389 L"Cannot validate options: CommandLineToArgvW failed with error = 0x%08x",
390 GetLastError());
391 goto out;
392 }
393
394 /* Note: argv[0] is the first option */
395 if (argc < 1) /* no options */
396 {
397 ret = TRUE;
398 goto out;
399 }
400
401 /*
402 * If only one argument, it is the config file
403 */
404 if (argc == 1)
405 {
406 WCHAR *argv_tmp[2] = { L"--config", argv[0] };
407
408 if (!CheckOption(workdir, 2, argv_tmp, &settings))
409 {
410 swprintf(errmsg, capacity, msg1, argv[0], workdir,
412 }
413 goto out;
414 }
415
416 for (i = 0; i < argc; ++i)
417 {
418 if (!IsOption(argv[i]))
419 {
420 continue;
421 }
422
423 if (!CheckOption(workdir, argc-i, &argv[i], &settings))
424 {
425 if (wcscmp(L"--config", argv[i]) == 0 && argc-i > 1)
426 {
427 swprintf(errmsg, capacity, msg1, argv[i+1], workdir,
429 }
430 else
431 {
432 swprintf(errmsg, capacity, msg2, argv[i],
434 }
435 goto out;
436 }
437 }
438
439 /* all options passed */
440 ret = TRUE;
441
442out:
443 if (argv)
444 {
445 LocalFree(argv);
446 }
447 return ret;
448}
449
450static BOOL
451GetStartupData(HANDLE pipe, STARTUP_DATA *sud)
452{
453 size_t size, len;
454 WCHAR *data = NULL;
455 DWORD bytes, read;
456
457 bytes = PeekNamedPipeAsync(pipe, 1, &exit_event);
458 if (bytes == 0)
459 {
460 MsgToEventLog(M_SYSERR, TEXT("PeekNamedPipeAsync failed"));
461 ReturnLastError(pipe, L"PeekNamedPipeAsync");
462 goto err;
463 }
464
465 size = bytes / sizeof(*data);
466 if (size == 0)
467 {
468 MsgToEventLog(M_SYSERR, TEXT("malformed startup data: 1 byte received"));
469 ReturnError(pipe, ERROR_STARTUP_DATA, L"GetStartupData", 1, &exit_event);
470 goto err;
471 }
472
473 data = malloc(bytes);
474 if (data == NULL)
475 {
476 MsgToEventLog(M_SYSERR, TEXT("malloc failed"));
477 ReturnLastError(pipe, L"malloc");
478 goto err;
479 }
480
481 read = ReadPipeAsync(pipe, data, bytes, 1, &exit_event);
482 if (bytes != read)
483 {
484 MsgToEventLog(M_SYSERR, TEXT("ReadPipeAsync failed"));
485 ReturnLastError(pipe, L"ReadPipeAsync");
486 goto err;
487 }
488
489 if (data[size - 1] != 0)
490 {
491 MsgToEventLog(M_ERR, TEXT("Startup data is not NULL terminated"));
492 ReturnError(pipe, ERROR_STARTUP_DATA, L"GetStartupData", 1, &exit_event);
493 goto err;
494 }
495
496 sud->directory = data;
497 len = wcslen(sud->directory) + 1;
498 size -= len;
499 if (size <= 0)
500 {
501 MsgToEventLog(M_ERR, TEXT("Startup data ends at working directory"));
502 ReturnError(pipe, ERROR_STARTUP_DATA, L"GetStartupData", 1, &exit_event);
503 goto err;
504 }
505
506 sud->options = sud->directory + len;
507 len = wcslen(sud->options) + 1;
508 size -= len;
509 if (size <= 0)
510 {
511 MsgToEventLog(M_ERR, TEXT("Startup data ends at command line options"));
512 ReturnError(pipe, ERROR_STARTUP_DATA, L"GetStartupData", 1, &exit_event);
513 goto err;
514 }
515
516 sud->std_input = sud->options + len;
517 return TRUE;
518
519err:
520 sud->directory = NULL; /* caller must not free() */
521 free(data);
522 return FALSE;
523}
524
525
526static VOID
528{
529 free(sud->directory);
530}
531
532
533static SOCKADDR_INET
534sockaddr_inet(short family, inet_address_t *addr)
535{
536 SOCKADDR_INET sa_inet;
537 ZeroMemory(&sa_inet, sizeof(sa_inet));
538 sa_inet.si_family = family;
539 if (family == AF_INET)
540 {
541 sa_inet.Ipv4.sin_addr = addr->ipv4;
542 }
543 else if (family == AF_INET6)
544 {
545 sa_inet.Ipv6.sin6_addr = addr->ipv6;
546 }
547 return sa_inet;
548}
549
550static DWORD
551InterfaceLuid(const char *iface_name, PNET_LUID luid)
552{
553 NETIO_STATUS status;
554 LPWSTR wide_name = utf8to16(iface_name);
555
556 if (wide_name)
557 {
558 status = ConvertInterfaceAliasToLuid(wide_name, luid);
559 free(wide_name);
560 }
561 else
562 {
563 status = ERROR_OUTOFMEMORY;
564 }
565 return status;
566}
567
568static DWORD
569ConvertInterfaceNameToIndex(const wchar_t *ifname, NET_IFINDEX *index)
570{
571 NET_LUID luid;
572 DWORD err;
573
574 err = ConvertInterfaceAliasToLuid(ifname, &luid);
575 if (err == ERROR_SUCCESS)
576 {
577 err = ConvertInterfaceLuidToIndex(&luid, index);
578 }
579 if (err != ERROR_SUCCESS)
580 {
581 MsgToEventLog(M_ERR, L"Failed to find interface index for <%ls>", ifname);
582 }
583 return err;
584}
585
586static BOOL
587CmpAddress(LPVOID item, LPVOID address)
588{
589 return memcmp(item, address, sizeof(MIB_UNICASTIPADDRESS_ROW)) == 0 ? TRUE : FALSE;
590}
591
592static DWORD
593DeleteAddress(PMIB_UNICASTIPADDRESS_ROW addr_row)
594{
595 return DeleteUnicastIpAddressEntry(addr_row);
596}
597
598static DWORD
600{
601 DWORD err;
602 PMIB_UNICASTIPADDRESS_ROW addr_row;
603 BOOL add = msg->header.type == msg_add_address;
604
605 addr_row = malloc(sizeof(*addr_row));
606 if (addr_row == NULL)
607 {
608 return ERROR_OUTOFMEMORY;
609 }
610
611 InitializeUnicastIpAddressEntry(addr_row);
612 addr_row->Address = sockaddr_inet(msg->family, &msg->address);
613 addr_row->OnLinkPrefixLength = (UINT8) msg->prefix_len;
614
615 if (msg->iface.index != -1)
616 {
617 addr_row->InterfaceIndex = msg->iface.index;
618 }
619 else
620 {
621 NET_LUID luid;
622 err = InterfaceLuid(msg->iface.name, &luid);
623 if (err)
624 {
625 goto out;
626 }
627 addr_row->InterfaceLuid = luid;
628 }
629
630 if (add)
631 {
632 err = CreateUnicastIpAddressEntry(addr_row);
633 if (err)
634 {
635 goto out;
636 }
637
638 err = AddListItem(&(*lists)[address], addr_row);
639 if (err)
640 {
641 DeleteAddress(addr_row);
642 }
643 }
644 else
645 {
646 err = DeleteAddress(addr_row);
647 if (err)
648 {
649 goto out;
650 }
651
652 free(RemoveListItem(&(*lists)[address], CmpAddress, addr_row));
653 }
654
655out:
656 if (!add || err)
657 {
658 free(addr_row);
659 }
660
661 return err;
662}
663
664static BOOL
665CmpRoute(LPVOID item, LPVOID route)
666{
667 return memcmp(item, route, sizeof(MIB_IPFORWARD_ROW2)) == 0 ? TRUE : FALSE;
668}
669
670static DWORD
671DeleteRoute(PMIB_IPFORWARD_ROW2 fwd_row)
672{
673 return DeleteIpForwardEntry2(fwd_row);
674}
675
676static DWORD
678{
679 DWORD err;
680 PMIB_IPFORWARD_ROW2 fwd_row;
681 BOOL add = msg->header.type == msg_add_route;
682
683 fwd_row = malloc(sizeof(*fwd_row));
684 if (fwd_row == NULL)
685 {
686 return ERROR_OUTOFMEMORY;
687 }
688
689 ZeroMemory(fwd_row, sizeof(*fwd_row));
690 fwd_row->ValidLifetime = 0xffffffff;
691 fwd_row->PreferredLifetime = 0xffffffff;
692 fwd_row->Protocol = MIB_IPPROTO_NETMGMT;
693 fwd_row->Metric = msg->metric;
694 fwd_row->DestinationPrefix.Prefix = sockaddr_inet(msg->family, &msg->prefix);
695 fwd_row->DestinationPrefix.PrefixLength = (UINT8) msg->prefix_len;
696 fwd_row->NextHop = sockaddr_inet(msg->family, &msg->gateway);
697
698 if (msg->iface.index != -1)
699 {
700 fwd_row->InterfaceIndex = msg->iface.index;
701 }
702 else if (strlen(msg->iface.name))
703 {
704 NET_LUID luid;
705 err = InterfaceLuid(msg->iface.name, &luid);
706 if (err)
707 {
708 goto out;
709 }
710 fwd_row->InterfaceLuid = luid;
711 }
712
713 if (add)
714 {
715 err = CreateIpForwardEntry2(fwd_row);
716 if (err)
717 {
718 goto out;
719 }
720
721 err = AddListItem(&(*lists)[route], fwd_row);
722 if (err)
723 {
724 DeleteRoute(fwd_row);
725 }
726 }
727 else
728 {
729 err = DeleteRoute(fwd_row);
730 if (err)
731 {
732 goto out;
733 }
734
735 free(RemoveListItem(&(*lists)[route], CmpRoute, fwd_row));
736 }
737
738out:
739 if (!add || err)
740 {
741 free(fwd_row);
742 }
743
744 return err;
745}
746
747
748static DWORD
750{
751 if (msg->family == AF_INET)
752 {
753 return FlushIpNetTable(msg->iface.index);
754 }
755
756 return FlushIpNetTable2(msg->family, msg->iface.index);
757}
758
759static void
760BlockDNSErrHandler(DWORD err, const char *msg)
761{
762 TCHAR buf[256];
763 LPCTSTR err_str;
764
765 if (!err)
766 {
767 return;
768 }
769
770 err_str = TEXT("Unknown Win32 Error");
771
772 if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM
773 | FORMAT_MESSAGE_ARGUMENT_ARRAY,
774 NULL, err, 0, buf, sizeof(buf), NULL))
775 {
776 err_str = buf;
777 }
778
779 MsgToEventLog(M_ERR, L"%hs (status = %lu): %ls", msg, err, err_str);
780}
781
782/* Use an always-true match_fn to get the head of the list */
783static BOOL
784CmpAny(LPVOID item, LPVOID any)
785{
786 return TRUE;
787}
788
789static DWORD
791{
792 DWORD err = 0;
793 wfp_block_data_t *block_data = RemoveListItem(&(*lists)[wfp_block], CmpAny, NULL);
794
795 if (block_data)
796 {
797 err = delete_wfp_block_filters(block_data->engine);
798 if (block_data->metric_v4 >= 0)
799 {
800 set_interface_metric(msg->iface.index, AF_INET,
801 block_data->metric_v4);
802 }
803 if (block_data->metric_v6 >= 0)
804 {
805 set_interface_metric(msg->iface.index, AF_INET6,
806 block_data->metric_v6);
807 }
808 free(block_data);
809 }
810 else
811 {
812 MsgToEventLog(M_ERR, TEXT("No previous block filters to delete"));
813 }
814
815 return err;
816}
817
818static DWORD
820{
821 DWORD err = 0;
822 wfp_block_data_t *block_data = NULL;
823 HANDLE engine = NULL;
824 LPCWSTR exe_path;
825 BOOL dns_only;
826
827 exe_path = settings.exe_path;
828 dns_only = (msg->flags == wfp_block_dns);
829
830 err = add_wfp_block_filters(&engine, msg->iface.index, exe_path, BlockDNSErrHandler, dns_only);
831 if (!err)
832 {
833 block_data = malloc(sizeof(wfp_block_data_t));
834 if (!block_data)
835 {
836 err = ERROR_OUTOFMEMORY;
837 goto out;
838 }
839 block_data->engine = engine;
840 block_data->index = msg->iface.index;
841 int is_auto = 0;
842 block_data->metric_v4 = get_interface_metric(msg->iface.index,
843 AF_INET, &is_auto);
844 if (is_auto)
845 {
846 block_data->metric_v4 = 0;
847 }
848 block_data->metric_v6 = get_interface_metric(msg->iface.index,
849 AF_INET6, &is_auto);
850 if (is_auto)
851 {
852 block_data->metric_v6 = 0;
853 }
854
855 err = AddListItem(&(*lists)[wfp_block], block_data);
856 if (!err)
857 {
858 err = set_interface_metric(msg->iface.index, AF_INET,
860 if (!err)
861 {
862 /* for IPv6, we intentionally ignore errors, because
863 * otherwise block-dns activation will fail if a user or
864 * admin has disabled IPv6 on the tun/tap/dco interface
865 * (if OpenVPN wants IPv6 ifconfig, we'll fail there)
866 */
867 set_interface_metric(msg->iface.index, AF_INET6,
869 }
870 if (err)
871 {
872 /* delete the filters, remove undo item and free interface data */
873 DeleteWfpBlock(msg, lists);
874 engine = NULL;
875 }
876 }
877 }
878
879out:
880 if (err && engine)
881 {
883 free(block_data);
884 }
885
886 return err;
887}
888
889static DWORD
891{
892 if (msg->header.type == msg_add_wfp_block)
893 {
894 return AddWfpBlock(msg, lists);
895 }
896 else
897 {
898 return DeleteWfpBlock(msg, lists);
899 }
900}
901
902/*
903 * Execute a command and return its exit code. If timeout > 0, terminate
904 * the process if still running after timeout milliseconds. In that case
905 * the return value is the windows error code WAIT_TIMEOUT = 0x102
906 */
907static DWORD
908ExecCommand(const WCHAR *argv0, const WCHAR *cmdline, DWORD timeout)
909{
910 DWORD exit_code;
911 STARTUPINFOW si;
912 PROCESS_INFORMATION pi;
913 DWORD proc_flags = CREATE_NO_WINDOW|CREATE_UNICODE_ENVIRONMENT;
914 WCHAR *cmdline_dup = NULL;
915
916 ZeroMemory(&si, sizeof(si));
917 ZeroMemory(&pi, sizeof(pi));
918
919 si.cb = sizeof(si);
920
921 /* CreateProcess needs a modifiable cmdline: make a copy */
922 cmdline_dup = _wcsdup(cmdline);
923 if (cmdline_dup && CreateProcessW(argv0, cmdline_dup, NULL, NULL, FALSE,
924 proc_flags, NULL, NULL, &si, &pi) )
925 {
926 WaitForSingleObject(pi.hProcess, timeout ? timeout : INFINITE);
927 if (!GetExitCodeProcess(pi.hProcess, &exit_code))
928 {
929 MsgToEventLog(M_SYSERR, TEXT("ExecCommand: Error getting exit_code:"));
930 exit_code = GetLastError();
931 }
932 else if (exit_code == STILL_ACTIVE)
933 {
934 exit_code = WAIT_TIMEOUT; /* Windows error code 0x102 */
935
936 /* kill without impunity */
937 TerminateProcess(pi.hProcess, exit_code);
938 MsgToEventLog(M_ERR, TEXT("ExecCommand: \"%ls %ls\" killed after timeout"),
939 argv0, cmdline);
940 }
941 else if (exit_code)
942 {
943 MsgToEventLog(M_ERR, TEXT("ExecCommand: \"%ls %ls\" exited with status = %lu"),
944 argv0, cmdline, exit_code);
945 }
946 else
947 {
948 MsgToEventLog(M_INFO, TEXT("ExecCommand: \"%ls %ls\" completed"), argv0, cmdline);
949 }
950
951 CloseHandle(pi.hProcess);
952 CloseHandle(pi.hThread);
953 }
954 else
955 {
956 exit_code = GetLastError();
957 MsgToEventLog(M_SYSERR, TEXT("ExecCommand: could not run \"%ls %ls\" :"),
958 argv0, cmdline);
959 }
960
961 free(cmdline_dup);
962 return exit_code;
963}
964
965/*
966 * Entry point for register-dns thread.
967 */
968static DWORD WINAPI
969RegisterDNS(LPVOID unused)
970{
971 DWORD err;
972 size_t i;
973 DWORD timeout = RDNS_TIMEOUT * 1000; /* in milliseconds */
974
975 /* path of ipconfig command */
976 WCHAR ipcfg[MAX_PATH];
977
978 struct
979 {
980 WCHAR *argv0;
981 WCHAR *cmdline;
982 DWORD timeout;
983 } cmds [] = {
984 { ipcfg, L"ipconfig /flushdns", timeout },
985 { ipcfg, L"ipconfig /registerdns", timeout },
986 };
987
988 HANDLE wait_handles[2] = {rdns_semaphore, exit_event};
989
990 swprintf(ipcfg, MAX_PATH, L"%ls\\%ls", get_win_sys_path(), L"ipconfig.exe");
991
992 if (WaitForMultipleObjects(2, wait_handles, FALSE, timeout) == WAIT_OBJECT_0)
993 {
994 /* Semaphore locked */
995 for (i = 0; i < _countof(cmds); ++i)
996 {
997 ExecCommand(cmds[i].argv0, cmds[i].cmdline, cmds[i].timeout);
998 }
999 err = 0;
1000 if (!ReleaseSemaphore(rdns_semaphore, 1, NULL) )
1001 {
1002 err = MsgToEventLog(M_SYSERR, TEXT("RegisterDNS: Failed to release regsiter-dns semaphore:"));
1003 }
1004 }
1005 else
1006 {
1007 MsgToEventLog(M_ERR, TEXT("RegisterDNS: Failed to lock register-dns semaphore"));
1008 err = ERROR_SEM_TIMEOUT; /* Windows error code 0x79 */
1009 }
1010 return err;
1011}
1012
1013static DWORD
1015{
1016 DWORD err;
1017 HANDLE thread = NULL;
1018
1019 /* Delegate this job to a sub-thread */
1020 thread = CreateThread(NULL, 0, RegisterDNS, NULL, 0, NULL);
1021
1022 /*
1023 * We don't add these thread handles to the undo list -- the thread and
1024 * processes it spawns are all supposed to terminate or timeout by themselves.
1025 */
1026 if (thread)
1027 {
1028 err = 0;
1029 CloseHandle(thread);
1030 }
1031 else
1032 {
1033 err = GetLastError();
1034 }
1035
1036 return err;
1037}
1038
1048static DWORD
1049netsh_dns_cmd(const wchar_t *action, const wchar_t *proto, const wchar_t *if_name, const wchar_t *addr)
1050{
1051 DWORD err = 0;
1052 int timeout = 30000; /* in msec */
1053 wchar_t argv0[MAX_PATH];
1054 wchar_t *cmdline = NULL;
1055
1056 if (!addr)
1057 {
1058 if (wcscmp(action, L"delete") == 0)
1059 {
1060 addr = L"all";
1061 }
1062 else /* nothing to do -- return success*/
1063 {
1064 goto out;
1065 }
1066 }
1067
1068 /* Path of netsh */
1069 swprintf(argv0, _countof(argv0), L"%ls\\%ls", get_win_sys_path(), L"netsh.exe");
1070
1071 /* cmd template:
1072 * netsh interface $proto $action dns $if_name $addr [validate=no]
1073 */
1074 const wchar_t *fmt = L"netsh interface %ls %ls dns \"%ls\" %ls";
1075
1076 /* max cmdline length in wchars -- include room for worst case and some */
1077 size_t ncmdline = wcslen(fmt) + wcslen(if_name) + wcslen(addr) + 32 + 1;
1078 cmdline = malloc(ncmdline*sizeof(wchar_t));
1079 if (!cmdline)
1080 {
1081 err = ERROR_OUTOFMEMORY;
1082 goto out;
1083 }
1084
1085 swprintf(cmdline, ncmdline, fmt, proto, action, if_name, addr);
1086
1087 if (IsWindows7OrGreater())
1088 {
1089 wcscat_s(cmdline, ncmdline, L" validate=no");
1090 }
1091 err = ExecCommand(argv0, cmdline, timeout);
1092
1093out:
1094 free(cmdline);
1095 return err;
1096}
1097
1107static DWORD
1108netsh_wins_cmd(const wchar_t *action, const wchar_t *if_name, const wchar_t *addr)
1109{
1110 DWORD err = 0;
1111 int timeout = 30000; /* in msec */
1112 wchar_t argv0[MAX_PATH];
1113 wchar_t *cmdline = NULL;
1114 const wchar_t *addr_static = (wcscmp(action, L"set") == 0) ? L"static" : L"";
1115
1116 if (!addr)
1117 {
1118 if (wcscmp(action, L"delete") == 0)
1119 {
1120 addr = L"all";
1121 }
1122 else /* nothing to do -- return success*/
1123 {
1124 goto out;
1125 }
1126 }
1127
1128 /* Path of netsh */
1129 swprintf(argv0, _countof(argv0), L"%ls\\%ls", get_win_sys_path(), L"netsh.exe");
1130
1131 /* cmd template:
1132 * netsh interface ip $action wins $if_name $static $addr
1133 */
1134 const wchar_t *fmt = L"netsh interface ip %ls wins \"%ls\" %ls %ls";
1135
1136 /* max cmdline length in wchars -- include room for worst case and some */
1137 size_t ncmdline = wcslen(fmt) + wcslen(if_name) + wcslen(action) + wcslen(addr)
1138 +wcslen(addr_static) + 32 + 1;
1139 cmdline = malloc(ncmdline * sizeof(wchar_t));
1140 if (!cmdline)
1141 {
1142 err = ERROR_OUTOFMEMORY;
1143 goto out;
1144 }
1145
1146 swprintf(cmdline, ncmdline, fmt, action, if_name, addr_static, addr);
1147
1148 err = ExecCommand(argv0, cmdline, timeout);
1149
1150out:
1151 free(cmdline);
1152 return err;
1153}
1154
1163static DWORD
1164wmic_nicconfig_cmd(const wchar_t *action, const NET_IFINDEX if_index,
1165 const wchar_t *data)
1166{
1167 DWORD err = 0;
1168 wchar_t argv0[MAX_PATH];
1169 wchar_t *cmdline = NULL;
1170 int timeout = 10000; /* in msec */
1171
1172 swprintf(argv0, _countof(argv0), L"%ls\\%ls", get_win_sys_path(), L"wbem\\wmic.exe");
1173
1174 const wchar_t *fmt;
1175 /* comma separated list must be enclosed in parenthesis */
1176 if (data && wcschr(data, L','))
1177 {
1178 fmt = L"wmic nicconfig where (InterfaceIndex=%ld) call %ls (%ls)";
1179 }
1180 else
1181 {
1182 fmt = L"wmic nicconfig where (InterfaceIndex=%ld) call %ls \"%ls\"";
1183 }
1184
1185 size_t ncmdline = wcslen(fmt) + 20 + wcslen(action) /* max 20 for ifindex */
1186 + (data ? wcslen(data) + 1 : 1);
1187 cmdline = malloc(ncmdline*sizeof(wchar_t));
1188 if (!cmdline)
1189 {
1190 return ERROR_OUTOFMEMORY;
1191 }
1192
1193 swprintf(cmdline, ncmdline, fmt, if_index, action,
1194 data ? data : L"");
1195 err = ExecCommand(argv0, cmdline, timeout);
1196
1197 free(cmdline);
1198 return err;
1199}
1200
1201/* Delete all IPv4 or IPv6 dns servers for an interface */
1202static DWORD
1203DeleteDNS(short family, wchar_t *if_name)
1204{
1205 wchar_t *proto = (family == AF_INET6) ? L"ipv6" : L"ip";
1206 return netsh_dns_cmd(L"delete", proto, if_name, NULL);
1207}
1208
1209/* Add an IPv4 or IPv6 dns server to an interface */
1210static DWORD
1211AddDNS(short family, wchar_t *if_name, wchar_t *addr)
1212{
1213 wchar_t *proto = (family == AF_INET6) ? L"ipv6" : L"ip";
1214 return netsh_dns_cmd(L"add", proto, if_name, addr);
1215}
1216
1217static BOOL
1218CmpWString(LPVOID item, LPVOID str)
1219{
1220 return (wcscmp(item, str) == 0) ? TRUE : FALSE;
1221}
1222
1231static DWORD
1232SetDNSDomain(const wchar_t *if_name, const char *domain, undo_lists_t *lists)
1233{
1234 NET_IFINDEX if_index;
1235
1236 DWORD err = ConvertInterfaceNameToIndex(if_name, &if_index);
1237 if (err != ERROR_SUCCESS)
1238 {
1239 return err;
1240 }
1241
1242 wchar_t *wdomain = utf8to16(domain); /* utf8 to wide-char */
1243 if (!wdomain)
1244 {
1245 return ERROR_OUTOFMEMORY;
1246 }
1247
1248 /* free undo list if previously set */
1249 if (lists)
1250 {
1251 free(RemoveListItem(&(*lists)[undo_domain], CmpWString, (void *)if_name));
1252 }
1253
1254 err = wmic_nicconfig_cmd(L"SetDNSDomain", if_index, wdomain);
1255
1256 /* Add to undo list if domain is non-empty */
1257 if (err == 0 && wdomain[0] && lists)
1258 {
1259 wchar_t *tmp_name = _wcsdup(if_name);
1260 if (!tmp_name || AddListItem(&(*lists)[undo_domain], tmp_name))
1261 {
1262 free(tmp_name);
1263 err = ERROR_OUTOFMEMORY;
1264 }
1265 }
1266
1267 free(wdomain);
1268 return err;
1269}
1270
1271static DWORD
1273{
1274 DWORD err = 0;
1275 wchar_t addr[46]; /* large enough to hold string representation of an ipv4 / ipv6 address */
1276 undo_type_t undo_type = (msg->family == AF_INET6) ? undo_dns4 : undo_dns6;
1277 int addr_len = msg->addr_len;
1278
1279 /* sanity check */
1280 if (addr_len > _countof(msg->addr))
1281 {
1282 addr_len = _countof(msg->addr);
1283 }
1284
1285 if (!msg->iface.name[0]) /* interface name is required */
1286 {
1287 return ERROR_MESSAGE_DATA;
1288 }
1289
1290 /* use a non-const reference with limited scope to enforce null-termination of strings from client */
1291 {
1293 msgptr->iface.name[_countof(msg->iface.name)-1] = '\0';
1294 msgptr->domains[_countof(msg->domains)-1] = '\0';
1295 }
1296
1297 wchar_t *wide_name = utf8to16(msg->iface.name); /* utf8 to wide-char */
1298 if (!wide_name)
1299 {
1300 return ERROR_OUTOFMEMORY;
1301 }
1302
1303 /* We delete all current addresses before adding any
1304 * OR if the message type is del_dns_cfg
1305 */
1306 if (addr_len > 0 || msg->header.type == msg_del_dns_cfg)
1307 {
1308 err = DeleteDNS(msg->family, wide_name);
1309 if (err)
1310 {
1311 goto out;
1312 }
1313 free(RemoveListItem(&(*lists)[undo_type], CmpWString, wide_name));
1314 }
1315
1316 if (msg->header.type == msg_del_dns_cfg)
1317 {
1318 if (msg->domains[0])
1319 {
1320 /* setting an empty domain removes any previous value */
1321 err = SetDNSDomain(wide_name, "", lists);
1322 }
1323 goto out; /* job done */
1324 }
1325
1326 for (int i = 0; i < addr_len; ++i)
1327 {
1328 if (msg->family == AF_INET6)
1329 {
1330 RtlIpv6AddressToStringW(&msg->addr[i].ipv6, addr);
1331 }
1332 else
1333 {
1334 RtlIpv4AddressToStringW(&msg->addr[i].ipv4, addr);
1335 }
1336 err = AddDNS(msg->family, wide_name, addr);
1337 if (i == 0 && err)
1338 {
1339 goto out;
1340 }
1341 /* We do not check for duplicate addresses, so any error in adding
1342 * additional addresses is ignored.
1343 */
1344 }
1345
1346 err = 0;
1347
1348 if (msg->addr_len > 0)
1349 {
1350 wchar_t *tmp_name = _wcsdup(wide_name);
1351 if (!tmp_name || AddListItem(&(*lists)[undo_type], tmp_name))
1352 {
1353 free(tmp_name);
1354 DeleteDNS(msg->family, wide_name);
1355 err = ERROR_OUTOFMEMORY;
1356 goto out;
1357 }
1358 }
1359
1360 if (msg->domains[0])
1361 {
1362 err = SetDNSDomain(wide_name, msg->domains, lists);
1363 }
1364
1365out:
1366 free(wide_name);
1367 return err;
1368}
1369
1370static DWORD
1372{
1373 DWORD err = 0;
1374 wchar_t addr[16]; /* large enough to hold string representation of an ipv4 */
1375 int addr_len = msg->addr_len;
1376
1377 /* sanity check */
1378 if (addr_len > _countof(msg->addr))
1379 {
1380 addr_len = _countof(msg->addr);
1381 }
1382
1383 if (!msg->iface.name[0]) /* interface name is required */
1384 {
1385 return ERROR_MESSAGE_DATA;
1386 }
1387
1388 /* use a non-const reference with limited scope to enforce null-termination of strings from client */
1389 {
1391 msgptr->iface.name[_countof(msg->iface.name) - 1] = '\0';
1392 }
1393
1394 wchar_t *wide_name = utf8to16(msg->iface.name); /* utf8 to wide-char */
1395 if (!wide_name)
1396 {
1397 return ERROR_OUTOFMEMORY;
1398 }
1399
1400 /* We delete all current addresses before adding any
1401 * OR if the message type is del_wins_cfg
1402 */
1403 if (addr_len > 0 || msg->header.type == msg_del_wins_cfg)
1404 {
1405 err = netsh_wins_cmd(L"delete", wide_name, NULL);
1406 if (err)
1407 {
1408 goto out;
1409 }
1410 free(RemoveListItem(&(*lists)[undo_wins], CmpWString, wide_name));
1411 }
1412
1413 if (msg->header.type == msg_del_wins_cfg)
1414 {
1415 goto out; /* job done */
1416 }
1417
1418 for (int i = 0; i < addr_len; ++i)
1419 {
1420 RtlIpv4AddressToStringW(&msg->addr[i].ipv4, addr);
1421 err = netsh_wins_cmd(i == 0 ? L"set" : L"add", wide_name, addr);
1422 if (i == 0 && err)
1423 {
1424 goto out;
1425 }
1426 /* We do not check for duplicate addresses, so any error in adding
1427 * additional addresses is ignored.
1428 */
1429 }
1430
1431 err = 0;
1432
1433 if (addr_len > 0)
1434 {
1435 wchar_t *tmp_name = _wcsdup(wide_name);
1436 if (!tmp_name || AddListItem(&(*lists)[undo_wins], tmp_name))
1437 {
1438 free(tmp_name);
1439 netsh_wins_cmd(L"delete", wide_name, NULL);
1440 err = ERROR_OUTOFMEMORY;
1441 goto out;
1442 }
1443 }
1444
1445out:
1446 free(wide_name);
1447 return err;
1448}
1449
1450static DWORD
1452{
1453 DWORD err = 0;
1454 DWORD timeout = 5000; /* in milli seconds */
1455 wchar_t argv0[MAX_PATH];
1456
1457 /* Path of netsh */
1458 swprintf(argv0, _countof(argv0), L"%ls\\%ls", get_win_sys_path(), L"netsh.exe");
1459
1460 /* cmd template:
1461 * netsh interface ipv4 set address name=$if_index source=dhcp
1462 */
1463 const wchar_t *fmt = L"netsh interface ipv4 set address name=\"%d\" source=dhcp";
1464
1465 /* max cmdline length in wchars -- include room for if index:
1466 * 10 chars for 32 bit int in decimal and +1 for NUL
1467 */
1468 size_t ncmdline = wcslen(fmt) + 10 + 1;
1469 wchar_t *cmdline = malloc(ncmdline*sizeof(wchar_t));
1470 if (!cmdline)
1471 {
1472 err = ERROR_OUTOFMEMORY;
1473 return err;
1474 }
1475
1476 swprintf(cmdline, ncmdline, fmt, dhcp->iface.index);
1477
1478 err = ExecCommand(argv0, cmdline, timeout);
1479
1480 /* Note: This could fail if dhcp is already enabled, so the caller
1481 * may not want to treat errors as FATAL.
1482 */
1483
1484 free(cmdline);
1485 return err;
1486}
1487
1488static DWORD
1489OvpnDuplicateHandle(HANDLE ovpn_proc, HANDLE orig_handle, HANDLE *new_handle)
1490{
1491 DWORD err = ERROR_SUCCESS;
1492
1493 if (!DuplicateHandle(ovpn_proc, orig_handle, GetCurrentProcess(), new_handle, 0, FALSE, DUPLICATE_SAME_ACCESS))
1494 {
1495 err = GetLastError();
1496 MsgToEventLog(M_SYSERR, TEXT("Could not duplicate handle"));
1497 return err;
1498 }
1499
1500 return err;
1501}
1502
1503static DWORD
1504DuplicateAndMapRing(HANDLE ovpn_proc, HANDLE orig_handle, struct tun_ring **ring)
1505{
1506 DWORD err = ERROR_SUCCESS;
1507
1508 HANDLE dup_handle = NULL;
1509
1510 err = OvpnDuplicateHandle(ovpn_proc, orig_handle, &dup_handle);
1511 if (err != ERROR_SUCCESS)
1512 {
1513 return err;
1514 }
1515 *ring = (struct tun_ring *)MapViewOfFile(dup_handle, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(struct tun_ring));
1516 CloseHandleEx(&dup_handle);
1517 if (*ring == NULL)
1518 {
1519 err = GetLastError();
1520 MsgToEventLog(M_SYSERR, TEXT("Could not map shared memory"));
1521 return err;
1522 }
1523
1524 return err;
1525}
1526
1527static DWORD
1529 undo_lists_t *lists)
1530{
1531 DWORD err = 0;
1532
1533 ring_buffer_maps_t *ring_buffer_maps = RemoveListItem(&(*lists)[undo_ring_buffer], CmpAny, NULL);
1534
1535 if (ring_buffer_maps)
1536 {
1537 UnmapRingBuffer(ring_buffer_maps);
1538 }
1539 else if ((ring_buffer_maps = calloc(1, sizeof(*ring_buffer_maps))) == NULL)
1540 {
1541 return ERROR_OUTOFMEMORY;
1542 }
1543
1544 HANDLE device = NULL;
1545 HANDLE send_tail_moved = NULL;
1546 HANDLE receive_tail_moved = NULL;
1547
1548 err = OvpnDuplicateHandle(ovpn_proc, rrb->device, &device);
1549 if (err != ERROR_SUCCESS)
1550 {
1551 goto out;
1552 }
1553
1554 err = DuplicateAndMapRing(ovpn_proc, rrb->send_ring_handle, &ring_buffer_maps->send_ring);
1555 if (err != ERROR_SUCCESS)
1556 {
1557 goto out;
1558 }
1559
1560 err = DuplicateAndMapRing(ovpn_proc, rrb->receive_ring_handle, &ring_buffer_maps->receive_ring);
1561 if (err != ERROR_SUCCESS)
1562 {
1563 goto out;
1564 }
1565
1566 err = OvpnDuplicateHandle(ovpn_proc, rrb->send_tail_moved, &send_tail_moved);
1567 if (err != ERROR_SUCCESS)
1568 {
1569 goto out;
1570 }
1571
1572 err = OvpnDuplicateHandle(ovpn_proc, rrb->receive_tail_moved, &receive_tail_moved);
1573 if (err != ERROR_SUCCESS)
1574 {
1575 goto out;
1576 }
1577
1578 if (!register_ring_buffers(device, ring_buffer_maps->send_ring,
1579 ring_buffer_maps->receive_ring,
1580 send_tail_moved, receive_tail_moved))
1581 {
1582 err = GetLastError();
1583 MsgToEventLog(M_SYSERR, TEXT("Could not register ring buffers"));
1584 goto out;
1585 }
1586
1587 err = AddListItem(&(*lists)[undo_ring_buffer], ring_buffer_maps);
1588
1589out:
1590 if (err != ERROR_SUCCESS && ring_buffer_maps)
1591 {
1592 UnmapRingBuffer(ring_buffer_maps);
1593 free(ring_buffer_maps);
1594 }
1595 CloseHandleEx(&device);
1596 CloseHandleEx(&send_tail_moved);
1597 CloseHandleEx(&receive_tail_moved);
1598 return err;
1599}
1600
1601static DWORD
1603{
1604 DWORD err = 0;
1605 MIB_IPINTERFACE_ROW ipiface;
1606 InitializeIpInterfaceEntry(&ipiface);
1607 ipiface.Family = mtu->family;
1608 ipiface.InterfaceIndex = mtu->iface.index;
1609 err = GetIpInterfaceEntry(&ipiface);
1610 if (err != NO_ERROR)
1611 {
1612 return err;
1613 }
1614 if (mtu->family == AF_INET)
1615 {
1616 ipiface.SitePrefixLength = 0;
1617 }
1618 ipiface.NlMtu = mtu->mtu;
1619
1620 err = SetIpInterfaceEntry(&ipiface);
1621 return err;
1622}
1623
1624static VOID
1625HandleMessage(HANDLE pipe, HANDLE ovpn_proc,
1626 DWORD bytes, DWORD count, LPHANDLE events, undo_lists_t *lists)
1627{
1629 ack_message_t ack = {
1630 .header = {
1632 .size = sizeof(ack),
1633 .message_id = -1
1634 },
1635 .error_number = ERROR_MESSAGE_DATA
1636 };
1637
1638 DWORD read = ReadPipeAsync(pipe, &msg, bytes, count, events);
1639 if (read != bytes || read < sizeof(msg.header) || read != msg.header.size)
1640 {
1641 goto out;
1642 }
1643
1644 ack.header.message_id = msg.header.message_id;
1645
1646 switch (msg.header.type)
1647 {
1648 case msg_add_address:
1649 case msg_del_address:
1650 if (msg.header.size == sizeof(msg.address))
1651 {
1652 ack.error_number = HandleAddressMessage(&msg.address, lists);
1653 }
1654 break;
1655
1656 case msg_add_route:
1657 case msg_del_route:
1658 if (msg.header.size == sizeof(msg.route))
1659 {
1660 ack.error_number = HandleRouteMessage(&msg.route, lists);
1661 }
1662 break;
1663
1665 if (msg.header.size == sizeof(msg.flush_neighbors))
1666 {
1667 ack.error_number = HandleFlushNeighborsMessage(&msg.flush_neighbors);
1668 }
1669 break;
1670
1671 case msg_add_wfp_block:
1672 case msg_del_wfp_block:
1673 if (msg.header.size == sizeof(msg.wfp_block))
1674 {
1675 ack.error_number = HandleWfpBlockMessage(&msg.wfp_block, lists);
1676 }
1677 break;
1678
1679 case msg_register_dns:
1681 break;
1682
1683 case msg_add_dns_cfg:
1684 case msg_del_dns_cfg:
1685 ack.error_number = HandleDNSConfigMessage(&msg.dns, lists);
1686 break;
1687
1688 case msg_add_wins_cfg:
1689 case msg_del_wins_cfg:
1690 ack.error_number = HandleWINSConfigMessage(&msg.wins, lists);
1691 break;
1692
1693 case msg_enable_dhcp:
1694 if (msg.header.size == sizeof(msg.dhcp))
1695 {
1697 }
1698 break;
1699
1701 if (msg.header.size == sizeof(msg.rrb))
1702 {
1703 ack.error_number = HandleRegisterRingBuffers(&msg.rrb, ovpn_proc, lists);
1704 }
1705 break;
1706
1707 case msg_set_mtu:
1708 if (msg.header.size == sizeof(msg.mtu))
1709 {
1710 ack.error_number = HandleMTUMessage(&msg.mtu);
1711 }
1712 break;
1713
1714 default:
1716 MsgToEventLog(MSG_FLAGS_ERROR, TEXT("Unknown message type %d"), msg.header.type);
1717 break;
1718 }
1719
1720out:
1721 WritePipeAsync(pipe, &ack, sizeof(ack), count, events);
1722}
1723
1724
1725static VOID
1727{
1728 undo_type_t type;
1729 wfp_block_data_t *interface_data;
1730 for (type = 0; type < _undo_type_max; type++)
1731 {
1732 list_item_t **pnext = &(*lists)[type];
1733 while (*pnext)
1734 {
1735 list_item_t *item = *pnext;
1736 switch (type)
1737 {
1738 case address:
1739 DeleteAddress(item->data);
1740 break;
1741
1742 case route:
1743 DeleteRoute(item->data);
1744 break;
1745
1746 case undo_dns4:
1747 DeleteDNS(AF_INET, item->data);
1748 break;
1749
1750 case undo_dns6:
1751 DeleteDNS(AF_INET6, item->data);
1752 break;
1753
1754 case undo_wins:
1755 netsh_wins_cmd(L"delete", item->data, NULL);
1756 break;
1757
1758 case undo_domain:
1759 SetDNSDomain(item->data, "", NULL);
1760 break;
1761
1762 case wfp_block:
1763 interface_data = (wfp_block_data_t *)(item->data);
1764 delete_wfp_block_filters(interface_data->engine);
1765 if (interface_data->metric_v4 >= 0)
1766 {
1767 set_interface_metric(interface_data->index, AF_INET,
1768 interface_data->metric_v4);
1769 }
1770 if (interface_data->metric_v6 >= 0)
1771 {
1772 set_interface_metric(interface_data->index, AF_INET6,
1773 interface_data->metric_v6);
1774 }
1775 break;
1776
1777 case undo_ring_buffer:
1778 UnmapRingBuffer(item->data);
1779 break;
1780
1781 case _undo_type_max:
1782 /* unreachable */
1783 break;
1784 }
1785
1786 /* Remove from the list and free memory */
1787 *pnext = item->next;
1788 free(item->data);
1789 free(item);
1790 }
1791 }
1792}
1793
1794static DWORD WINAPI
1795RunOpenvpn(LPVOID p)
1796{
1797 HANDLE pipe = p;
1798 HANDLE ovpn_pipe = NULL, svc_pipe = NULL;
1799 PTOKEN_USER svc_user = NULL, ovpn_user = NULL;
1800 HANDLE svc_token = NULL, imp_token = NULL, pri_token = NULL;
1801 HANDLE stdin_read = NULL, stdin_write = NULL;
1802 HANDLE stdout_write = NULL;
1803 DWORD pipe_mode, len, exit_code = 0;
1804 STARTUP_DATA sud = { 0, 0, 0 };
1805 STARTUPINFOW startup_info;
1806 PROCESS_INFORMATION proc_info;
1807 LPVOID user_env = NULL;
1808 TCHAR ovpn_pipe_name[256]; /* The entire pipe name string can be up to 256 characters long according to MSDN. */
1809 LPCWSTR exe_path;
1810 WCHAR *cmdline = NULL;
1811 size_t cmdline_size;
1812 undo_lists_t undo_lists;
1813 WCHAR errmsg[512] = L"";
1814
1815 SECURITY_ATTRIBUTES inheritable = {
1816 .nLength = sizeof(inheritable),
1817 .lpSecurityDescriptor = NULL,
1818 .bInheritHandle = TRUE
1819 };
1820
1821 PACL ovpn_dacl;
1822 EXPLICIT_ACCESS ea[2];
1823 SECURITY_DESCRIPTOR ovpn_sd;
1824 SECURITY_ATTRIBUTES ovpn_sa = {
1825 .nLength = sizeof(ovpn_sa),
1826 .lpSecurityDescriptor = &ovpn_sd,
1827 .bInheritHandle = FALSE
1828 };
1829
1830 ZeroMemory(&ea, sizeof(ea));
1831 ZeroMemory(&startup_info, sizeof(startup_info));
1832 ZeroMemory(&undo_lists, sizeof(undo_lists));
1833 ZeroMemory(&proc_info, sizeof(proc_info));
1834
1835 if (!GetStartupData(pipe, &sud))
1836 {
1837 goto out;
1838 }
1839
1840 if (!InitializeSecurityDescriptor(&ovpn_sd, SECURITY_DESCRIPTOR_REVISION))
1841 {
1842 ReturnLastError(pipe, L"InitializeSecurityDescriptor");
1843 goto out;
1844 }
1845
1846 /* Get SID of user the service is running under */
1847 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &svc_token))
1848 {
1849 ReturnLastError(pipe, L"OpenProcessToken");
1850 goto out;
1851 }
1852 len = 0;
1853 while (!GetTokenInformation(svc_token, TokenUser, svc_user, len, &len))
1854 {
1855 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
1856 {
1857 ReturnLastError(pipe, L"GetTokenInformation (service token)");
1858 goto out;
1859 }
1860 free(svc_user);
1861 svc_user = malloc(len);
1862 if (svc_user == NULL)
1863 {
1864 ReturnLastError(pipe, L"malloc (service token user)");
1865 goto out;
1866 }
1867 }
1868 if (!IsValidSid(svc_user->User.Sid))
1869 {
1870 ReturnLastError(pipe, L"IsValidSid (service token user)");
1871 goto out;
1872 }
1873
1874 if (!ImpersonateNamedPipeClient(pipe))
1875 {
1876 ReturnLastError(pipe, L"ImpersonateNamedPipeClient");
1877 goto out;
1878 }
1879 if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &imp_token))
1880 {
1881 ReturnLastError(pipe, L"OpenThreadToken");
1882 goto out;
1883 }
1884 len = 0;
1885 while (!GetTokenInformation(imp_token, TokenUser, ovpn_user, len, &len))
1886 {
1887 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
1888 {
1889 ReturnLastError(pipe, L"GetTokenInformation (impersonation token)");
1890 goto out;
1891 }
1892 free(ovpn_user);
1893 ovpn_user = malloc(len);
1894 if (ovpn_user == NULL)
1895 {
1896 ReturnLastError(pipe, L"malloc (impersonation token user)");
1897 goto out;
1898 }
1899 }
1900 if (!IsValidSid(ovpn_user->User.Sid))
1901 {
1902 ReturnLastError(pipe, L"IsValidSid (impersonation token user)");
1903 goto out;
1904 }
1905
1906 /*
1907 * Only authorized users are allowed to use any command line options or
1908 * have the config file in locations other than the global config directory.
1909 *
1910 * Check options are white-listed and config is in the global directory
1911 * OR user is authorized to run any config.
1912 */
1913 if (!ValidateOptions(pipe, sud.directory, sud.options, errmsg, _countof(errmsg))
1914 && !IsAuthorizedUser(ovpn_user->User.Sid, imp_token, settings.ovpn_admin_group))
1915 {
1916 ReturnError(pipe, ERROR_STARTUP_DATA, errmsg, 1, &exit_event);
1917 goto out;
1918 }
1919
1920 /* OpenVPN process DACL entry for access by service and user */
1921 ea[0].grfAccessPermissions = SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL;
1922 ea[0].grfAccessMode = SET_ACCESS;
1923 ea[0].grfInheritance = NO_INHERITANCE;
1924 ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
1925 ea[0].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
1926 ea[0].Trustee.ptstrName = (LPTSTR) svc_user->User.Sid;
1927 ea[1].grfAccessPermissions = READ_CONTROL | SYNCHRONIZE | PROCESS_VM_READ
1928 |SYNCHRONIZE | PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION;
1929 ea[1].grfAccessMode = SET_ACCESS;
1930 ea[1].grfInheritance = NO_INHERITANCE;
1931 ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
1932 ea[1].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
1933 ea[1].Trustee.ptstrName = (LPTSTR) ovpn_user->User.Sid;
1934
1935 /* Set owner and DACL of OpenVPN security descriptor */
1936 if (!SetSecurityDescriptorOwner(&ovpn_sd, svc_user->User.Sid, FALSE))
1937 {
1938 ReturnLastError(pipe, L"SetSecurityDescriptorOwner");
1939 goto out;
1940 }
1941 if (SetEntriesInAcl(2, ea, NULL, &ovpn_dacl) != ERROR_SUCCESS)
1942 {
1943 ReturnLastError(pipe, L"SetEntriesInAcl");
1944 goto out;
1945 }
1946 if (!SetSecurityDescriptorDacl(&ovpn_sd, TRUE, ovpn_dacl, FALSE))
1947 {
1948 ReturnLastError(pipe, L"SetSecurityDescriptorDacl");
1949 goto out;
1950 }
1951
1952 /* Create primary token from impersonation token */
1953 if (!DuplicateTokenEx(imp_token, TOKEN_ALL_ACCESS, NULL, 0, TokenPrimary, &pri_token))
1954 {
1955 ReturnLastError(pipe, L"DuplicateTokenEx");
1956 goto out;
1957 }
1958
1959 /* use /dev/null for stdout of openvpn (client should use --log for output) */
1960 stdout_write = CreateFile(_T("NUL"), GENERIC_WRITE, FILE_SHARE_WRITE,
1961 &inheritable, OPEN_EXISTING, 0, NULL);
1962 if (stdout_write == INVALID_HANDLE_VALUE)
1963 {
1964 ReturnLastError(pipe, L"CreateFile for stdout");
1965 goto out;
1966 }
1967
1968 if (!CreatePipe(&stdin_read, &stdin_write, &inheritable, 0)
1969 || !SetHandleInformation(stdin_write, HANDLE_FLAG_INHERIT, 0))
1970 {
1971 ReturnLastError(pipe, L"CreatePipe");
1972 goto out;
1973 }
1974
1975 swprintf(ovpn_pipe_name, _countof(ovpn_pipe_name),
1976 TEXT("\\\\.\\pipe\\") TEXT(PACKAGE) TEXT("%ls\\service_%lu"), service_instance, GetCurrentThreadId());
1977 ovpn_pipe = CreateNamedPipe(ovpn_pipe_name,
1978 PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED,
1979 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 1, 128, 128, 0, NULL);
1980 if (ovpn_pipe == INVALID_HANDLE_VALUE)
1981 {
1982 ReturnLastError(pipe, L"CreateNamedPipe");
1983 goto out;
1984 }
1985
1986 svc_pipe = CreateFile(ovpn_pipe_name, GENERIC_READ | GENERIC_WRITE, 0,
1987 &inheritable, OPEN_EXISTING, 0, NULL);
1988 if (svc_pipe == INVALID_HANDLE_VALUE)
1989 {
1990 ReturnLastError(pipe, L"CreateFile");
1991 goto out;
1992 }
1993
1994 pipe_mode = PIPE_READMODE_MESSAGE;
1995 if (!SetNamedPipeHandleState(svc_pipe, &pipe_mode, NULL, NULL))
1996 {
1997 ReturnLastError(pipe, L"SetNamedPipeHandleState");
1998 goto out;
1999 }
2000
2001 cmdline_size = wcslen(sud.options) + 128;
2002 cmdline = malloc(cmdline_size * sizeof(*cmdline));
2003 if (cmdline == NULL)
2004 {
2005 ReturnLastError(pipe, L"malloc");
2006 goto out;
2007 }
2008 /* there seem to be no common printf specifier that works on all
2009 * mingw/msvc platforms without trickery, so convert to void* and use
2010 * PRIuPTR to print that as best compromise */
2011 swprintf(cmdline, cmdline_size, L"openvpn %ls --msg-channel %" PRIuPTR,
2012 sud.options, (uintptr_t)svc_pipe);
2013
2014 if (!CreateEnvironmentBlock(&user_env, imp_token, FALSE))
2015 {
2016 ReturnLastError(pipe, L"CreateEnvironmentBlock");
2017 goto out;
2018 }
2019
2020 startup_info.cb = sizeof(startup_info);
2021 startup_info.dwFlags = STARTF_USESTDHANDLES;
2022 startup_info.hStdInput = stdin_read;
2023 startup_info.hStdOutput = stdout_write;
2024 startup_info.hStdError = stdout_write;
2025
2026 exe_path = settings.exe_path;
2027
2028 /* TODO: make sure HKCU is correct or call LoadUserProfile() */
2029 if (!CreateProcessAsUserW(pri_token, exe_path, cmdline, &ovpn_sa, NULL, TRUE,
2030 settings.priority | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
2031 user_env, sud.directory, &startup_info, &proc_info))
2032 {
2033 ReturnLastError(pipe, L"CreateProcessAsUser");
2034 goto out;
2035 }
2036
2037 if (!RevertToSelf())
2038 {
2039 TerminateProcess(proc_info.hProcess, 1);
2040 ReturnLastError(pipe, L"RevertToSelf");
2041 goto out;
2042 }
2043
2044 ReturnProcessId(pipe, proc_info.dwProcessId, 1, &exit_event);
2045
2046 CloseHandleEx(&stdout_write);
2047 CloseHandleEx(&stdin_read);
2048 CloseHandleEx(&svc_pipe);
2049
2050 DWORD input_size = WideCharToMultiByte(CP_UTF8, 0, sud.std_input, -1, NULL, 0, NULL, NULL);
2051 LPSTR input = NULL;
2052 if (input_size && (input = malloc(input_size)))
2053 {
2054 DWORD written;
2055 WideCharToMultiByte(CP_UTF8, 0, sud.std_input, -1, input, input_size, NULL, NULL);
2056 WriteFile(stdin_write, input, (DWORD)strlen(input), &written, NULL);
2057 free(input);
2058 }
2059
2060 while (TRUE)
2061 {
2062 DWORD bytes = PeekNamedPipeAsync(ovpn_pipe, 1, &exit_event);
2063 if (bytes == 0)
2064 {
2065 break;
2066 }
2067
2068 if (bytes > sizeof(pipe_message_t))
2069 {
2070 /* process at the other side of the pipe is misbehaving, shut it down */
2071 MsgToEventLog(MSG_FLAGS_ERROR, TEXT("OpenVPN process sent too large payload length to the pipe (%lu bytes), it will be terminated"), bytes);
2072 break;
2073 }
2074
2075 HandleMessage(ovpn_pipe, proc_info.hProcess, bytes, 1, &exit_event, &undo_lists);
2076 }
2077
2078 WaitForSingleObject(proc_info.hProcess, IO_TIMEOUT);
2079 GetExitCodeProcess(proc_info.hProcess, &exit_code);
2080 if (exit_code == STILL_ACTIVE)
2081 {
2082 TerminateProcess(proc_info.hProcess, 1);
2083 }
2084 else if (exit_code != 0)
2085 {
2086 WCHAR buf[256];
2087 swprintf(buf, _countof(buf),
2088 L"OpenVPN exited with error: exit code = %lu", exit_code);
2090 }
2091 Undo(&undo_lists);
2092
2093out:
2094 FlushFileBuffers(pipe);
2095 DisconnectNamedPipe(pipe);
2096
2097 free(ovpn_user);
2098 free(svc_user);
2099 free(cmdline);
2100 DestroyEnvironmentBlock(user_env);
2101 FreeStartupData(&sud);
2102 CloseHandleEx(&proc_info.hProcess);
2103 CloseHandleEx(&proc_info.hThread);
2104 CloseHandleEx(&stdin_read);
2105 CloseHandleEx(&stdin_write);
2106 CloseHandleEx(&stdout_write);
2107 CloseHandleEx(&svc_token);
2108 CloseHandleEx(&imp_token);
2109 CloseHandleEx(&pri_token);
2110 CloseHandleEx(&ovpn_pipe);
2111 CloseHandleEx(&svc_pipe);
2112 CloseHandleEx(&pipe);
2113
2114 return 0;
2115}
2116
2117
2118static DWORD WINAPI
2119ServiceCtrlInteractive(DWORD ctrl_code, DWORD event, LPVOID data, LPVOID ctx)
2120{
2121 SERVICE_STATUS *status = ctx;
2122 switch (ctrl_code)
2123 {
2124 case SERVICE_CONTROL_STOP:
2125 status->dwCurrentState = SERVICE_STOP_PENDING;
2127 if (exit_event)
2128 {
2129 SetEvent(exit_event);
2130 }
2131 return NO_ERROR;
2132
2133 case SERVICE_CONTROL_INTERROGATE:
2134 return NO_ERROR;
2135
2136 default:
2137 return ERROR_CALL_NOT_IMPLEMENTED;
2138 }
2139}
2140
2141
2142static HANDLE
2144{
2145 /*
2146 * allow all access for local system
2147 * deny FILE_CREATE_PIPE_INSTANCE for everyone
2148 * allow read/write for authenticated users
2149 * deny all access to anonymous
2150 */
2151 const TCHAR *sddlString = TEXT("D:(A;OICI;GA;;;S-1-5-18)(D;OICI;0x4;;;S-1-1-0)(A;OICI;GRGW;;;S-1-5-11)(D;;GA;;;S-1-5-7)");
2152
2153 PSECURITY_DESCRIPTOR sd = NULL;
2154 if (!ConvertStringSecurityDescriptorToSecurityDescriptor(sddlString, SDDL_REVISION_1, &sd, NULL))
2155 {
2156 MsgToEventLog(M_SYSERR, TEXT("ConvertStringSecurityDescriptorToSecurityDescriptor failed."));
2157 return INVALID_HANDLE_VALUE;
2158 }
2159
2160 /* Set up SECURITY_ATTRIBUTES */
2161 SECURITY_ATTRIBUTES sa = {0};
2162 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
2163 sa.lpSecurityDescriptor = sd;
2164 sa.bInheritHandle = FALSE;
2165
2166 DWORD flags = PIPE_ACCESS_DUPLEX | WRITE_DAC | FILE_FLAG_OVERLAPPED;
2167
2168 static BOOL first = TRUE;
2169 if (first)
2170 {
2171 flags |= FILE_FLAG_FIRST_PIPE_INSTANCE;
2172 first = FALSE;
2173 }
2174
2175 TCHAR pipe_name[256]; /* The entire pipe name string can be up to 256 characters long according to MSDN. */
2176 swprintf(pipe_name, _countof(pipe_name), TEXT("\\\\.\\pipe\\") TEXT(PACKAGE) TEXT("%ls\\service"), service_instance);
2177 HANDLE pipe = CreateNamedPipe(pipe_name, flags,
2178 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
2179 PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0, &sa);
2180
2181 LocalFree(sd);
2182
2183 if (pipe == INVALID_HANDLE_VALUE)
2184 {
2185 MsgToEventLog(M_SYSERR, TEXT("Could not create named pipe"));
2186 return INVALID_HANDLE_VALUE;
2187 }
2188
2189 return pipe;
2190}
2191
2192
2193static DWORD
2194UpdateWaitHandles(LPHANDLE *handles_ptr, LPDWORD count,
2195 HANDLE io_event, HANDLE exit_event, list_item_t *threads)
2196{
2197 static DWORD size = 10;
2198 static LPHANDLE handles = NULL;
2199 DWORD pos = 0;
2200
2201 if (handles == NULL)
2202 {
2203 handles = malloc(size * sizeof(HANDLE));
2204 *handles_ptr = handles;
2205 if (handles == NULL)
2206 {
2207 return ERROR_OUTOFMEMORY;
2208 }
2209 }
2210
2211 handles[pos++] = io_event;
2212
2213 if (!threads)
2214 {
2215 handles[pos++] = exit_event;
2216 }
2217
2218 while (threads)
2219 {
2220 if (pos == size)
2221 {
2222 LPHANDLE tmp;
2223 size += 10;
2224 tmp = realloc(handles, size * sizeof(HANDLE));
2225 if (tmp == NULL)
2226 {
2227 size -= 10;
2228 *count = pos;
2229 return ERROR_OUTOFMEMORY;
2230 }
2231 handles = tmp;
2232 *handles_ptr = handles;
2233 }
2234 handles[pos++] = threads->data;
2235 threads = threads->next;
2236 }
2237
2238 *count = pos;
2239 return NO_ERROR;
2240}
2241
2242
2243static VOID
2245{
2246 free(h);
2247}
2248
2249static BOOL
2250CmpHandle(LPVOID item, LPVOID hnd)
2251{
2252 return item == hnd;
2253}
2254
2255
2256VOID WINAPI
2257ServiceStartInteractiveOwn(DWORD dwArgc, LPTSTR *lpszArgv)
2258{
2259 status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
2260 ServiceStartInteractive(dwArgc, lpszArgv);
2261}
2262
2263
2264VOID WINAPI
2265ServiceStartInteractive(DWORD dwArgc, LPTSTR *lpszArgv)
2266{
2267 HANDLE pipe, io_event = NULL;
2268 OVERLAPPED overlapped;
2269 DWORD error = NO_ERROR;
2270 list_item_t *threads = NULL;
2271 PHANDLE handles = NULL;
2272 DWORD handle_count;
2273
2274 service = RegisterServiceCtrlHandlerEx(interactive_service.name, ServiceCtrlInteractive, &status);
2275 if (!service)
2276 {
2277 return;
2278 }
2279
2280 status.dwCurrentState = SERVICE_START_PENDING;
2281 status.dwServiceSpecificExitCode = NO_ERROR;
2282 status.dwWin32ExitCode = NO_ERROR;
2283 status.dwWaitHint = 3000;
2285
2286 /* Read info from registry in key HKLM\SOFTWARE\OpenVPN */
2287 error = GetOpenvpnSettings(&settings);
2288 if (error != ERROR_SUCCESS)
2289 {
2290 goto out;
2291 }
2292
2293 io_event = InitOverlapped(&overlapped);
2294 exit_event = CreateEvent(NULL, TRUE, FALSE, NULL);
2295 if (!exit_event || !io_event)
2296 {
2297 error = MsgToEventLog(M_SYSERR, TEXT("Could not create event"));
2298 goto out;
2299 }
2300
2301 rdns_semaphore = CreateSemaphoreW(NULL, 1, 1, NULL);
2302 if (!rdns_semaphore)
2303 {
2304 error = MsgToEventLog(M_SYSERR, TEXT("Could not create semaphore for register-dns"));
2305 goto out;
2306 }
2307
2308 error = UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
2309 if (error != NO_ERROR)
2310 {
2311 goto out;
2312 }
2313
2314 pipe = CreateClientPipeInstance();
2315 if (pipe == INVALID_HANDLE_VALUE)
2316 {
2317 goto out;
2318 }
2319
2320 status.dwCurrentState = SERVICE_RUNNING;
2321 status.dwWaitHint = 0;
2323
2324 while (TRUE)
2325 {
2326 if (ConnectNamedPipe(pipe, &overlapped) == FALSE
2327 && GetLastError() != ERROR_PIPE_CONNECTED
2328 && GetLastError() != ERROR_IO_PENDING)
2329 {
2330 MsgToEventLog(M_SYSERR, TEXT("Could not connect pipe"));
2331 break;
2332 }
2333
2334 error = WaitForMultipleObjects(handle_count, handles, FALSE, INFINITE);
2335 if (error == WAIT_OBJECT_0)
2336 {
2337 /* Client connected, spawn a worker thread for it */
2338 HANDLE next_pipe = CreateClientPipeInstance();
2339 HANDLE thread = CreateThread(NULL, 0, RunOpenvpn, pipe, CREATE_SUSPENDED, NULL);
2340 if (thread)
2341 {
2342 error = AddListItem(&threads, thread);
2343 if (!error)
2344 {
2345 error = UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
2346 }
2347 if (error)
2348 {
2349 ReturnError(pipe, error, L"Insufficient resources to service new clients", 1, &exit_event);
2350 /* Update wait handles again after removing the last worker thread */
2351 RemoveListItem(&threads, CmpHandle, thread);
2352 UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
2353 TerminateThread(thread, 1);
2354 CloseHandleEx(&thread);
2355 CloseHandleEx(&pipe);
2356 }
2357 else
2358 {
2359 ResumeThread(thread);
2360 }
2361 }
2362 else
2363 {
2364 CloseHandleEx(&pipe);
2365 }
2366
2367 ResetOverlapped(&overlapped);
2368 pipe = next_pipe;
2369 }
2370 else
2371 {
2372 CancelIo(pipe);
2373 if (error == WAIT_FAILED)
2374 {
2375 MsgToEventLog(M_SYSERR, TEXT("WaitForMultipleObjects failed"));
2376 SetEvent(exit_event);
2377 /* Give some time for worker threads to exit and then terminate */
2378 Sleep(1000);
2379 break;
2380 }
2381 if (!threads)
2382 {
2383 /* exit event signaled */
2384 CloseHandleEx(&pipe);
2385 ResetEvent(exit_event);
2386 error = NO_ERROR;
2387 break;
2388 }
2389
2390 /* Worker thread ended */
2391 HANDLE thread = RemoveListItem(&threads, CmpHandle, handles[error]);
2392 UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
2393 CloseHandleEx(&thread);
2394 }
2395 }
2396
2397out:
2398 FreeWaitHandles(handles);
2399 CloseHandleEx(&io_event);
2402
2403 status.dwCurrentState = SERVICE_STOPPED;
2404 status.dwWin32ExitCode = error;
2406}
DWORD MsgToEventLog(DWORD flags, LPCTSTR format,...)
Definition common.c:215
DWORD GetOpenvpnSettings(settings_t *s)
Definition common.c:56
LPCTSTR service_instance
Definition common.c:27
#define M_INFO
Definition errlevel.h:55
static void OvpnUnmapViewOfFile(struct tun_ring **ring)
static VOID ReturnLastError(HANDLE pipe, LPCWSTR func)
static DWORD ReadPipeAsync(HANDLE pipe, LPVOID buffer, DWORD size, DWORD count, LPHANDLE events)
static VOID ReturnProcessId(HANDLE pipe, DWORD pid, DWORD count, LPHANDLE events)
static DWORD HandleWINSConfigMessage(const wins_cfg_message_t *msg, undo_lists_t *lists)
static BOOL CmpAddress(LPVOID item, LPVOID address)
static DWORD PeekNamedPipeAsync(HANDLE pipe, DWORD count, LPHANDLE events)
static BOOL ResetOverlapped(LPOVERLAPPED overlapped)
static DWORD ExecCommand(const WCHAR *argv0, const WCHAR *cmdline, DWORD timeout)
static DWORD HandleEnableDHCPMessage(const enable_dhcp_message_t *dhcp)
static DWORD AddWfpBlock(const wfp_block_message_t *msg, undo_lists_t *lists)
static BOOL CmpWString(LPVOID item, LPVOID str)
static HANDLE CreateClientPipeInstance(VOID)
static DWORD SetDNSDomain(const wchar_t *if_name, const char *domain, undo_lists_t *lists)
Set interface specific DNS domain suffix.
static BOOL GetStartupData(HANDLE pipe, STARTUP_DATA *sud)
static DWORD DeleteWfpBlock(const wfp_block_message_t *msg, undo_lists_t *lists)
static VOID Undo(undo_lists_t *lists)
#define ERROR_STARTUP_DATA
Definition interactive.c:48
static DWORD WINAPI RunOpenvpn(LPVOID p)
static settings_t settings
Definition interactive.c:55
static DWORD DeleteRoute(PMIB_IPFORWARD_ROW2 fwd_row)
static DWORD ConvertInterfaceNameToIndex(const wchar_t *ifname, NET_IFINDEX *index)
static SERVICE_STATUS status
Definition interactive.c:53
static DWORD DuplicateAndMapRing(HANDLE ovpn_proc, HANDLE orig_handle, struct tun_ring **ring)
#define ERROR_MESSAGE_TYPE
Definition interactive.c:50
static VOID HandleMessage(HANDLE pipe, HANDLE ovpn_proc, DWORD bytes, DWORD count, LPHANDLE events, undo_lists_t *lists)
static SOCKADDR_INET sockaddr_inet(short family, inet_address_t *addr)
static LPVOID RemoveListItem(list_item_t **pfirst, match_fn_t match, LPVOID ctx)
static BOOL CmpHandle(LPVOID item, LPVOID hnd)
static DWORD HandleAddressMessage(address_message_t *msg, undo_lists_t *lists)
static DWORD DeleteDNS(short family, wchar_t *if_name)
static VOID ReturnError(HANDLE pipe, DWORD error, LPCWSTR func, DWORD count, LPHANDLE events)
static DWORD AddListItem(list_item_t **pfirst, LPVOID data)
static void BlockDNSErrHandler(DWORD err, const char *msg)
#define RDNS_TIMEOUT
Definition interactive.c:57
undo_type_t
Definition interactive.c:85
@ wfp_block
Definition interactive.c:88
@ _undo_type_max
Definition interactive.c:94
@ undo_dns6
Definition interactive.c:90
@ undo_dns4
Definition interactive.c:89
@ undo_ring_buffer
Definition interactive.c:92
@ undo_wins
Definition interactive.c:93
@ route
Definition interactive.c:87
@ address
Definition interactive.c:86
@ undo_domain
Definition interactive.c:91
static DWORD OvpnDuplicateHandle(HANDLE ovpn_proc, HANDLE orig_handle, HANDLE *new_handle)
static DWORD HandleRouteMessage(route_message_t *msg, undo_lists_t *lists)
static DWORD WINAPI RegisterDNS(LPVOID unused)
static HANDLE InitOverlapped(LPOVERLAPPED overlapped)
BOOL(* match_fn_t)(LPVOID item, LPVOID ctx)
static HANDLE CloseHandleEx(LPHANDLE handle)
static DWORD WINAPI ServiceCtrlInteractive(DWORD ctrl_code, DWORD event, LPVOID data, LPVOID ctx)
struct _list_item list_item_t
static DWORD AddDNS(short family, wchar_t *if_name, wchar_t *addr)
static DWORD DeleteAddress(PMIB_UNICASTIPADDRESS_ROW addr_row)
#define ERROR_OPENVPN_STARTUP
Definition interactive.c:47
static DWORD HandleRegisterRingBuffers(const register_ring_buffers_message_t *rrb, HANDLE ovpn_proc, undo_lists_t *lists)
static VOID FreeWaitHandles(LPHANDLE h)
openvpn_service_t interactive_service
Definition interactive.c:61
static DWORD AsyncPipeOp(async_op_t op, HANDLE pipe, LPVOID buffer, DWORD size, DWORD count, LPHANDLE events)
#define IO_TIMEOUT
Definition interactive.c:45
VOID WINAPI ServiceStartInteractiveOwn(DWORD dwArgc, LPTSTR *lpszArgv)
VOID WINAPI ServiceStartInteractive(DWORD dwArgc, LPTSTR *lpszArgv)
static DWORD HandleFlushNeighborsMessage(flush_neighbors_message_t *msg)
static DWORD HandleMTUMessage(const set_mtu_message_t *mtu)
list_item_t * undo_lists_t[_undo_type_max]
Definition interactive.c:96
static DWORD wmic_nicconfig_cmd(const wchar_t *action, const NET_IFINDEX if_index, const wchar_t *data)
Run command: wmic nicconfig (InterfaceIndex=$if_index) call $action ($data)
static DWORD HandleRegisterDNSMessage(void)
#define ERROR_MESSAGE_DATA
Definition interactive.c:49
static HANDLE exit_event
Definition interactive.c:54
static VOID FreeStartupData(STARTUP_DATA *sud)
static DWORD HandleWfpBlockMessage(const wfp_block_message_t *msg, undo_lists_t *lists)
static HANDLE rdns_semaphore
Definition interactive.c:56
static DWORD netsh_dns_cmd(const wchar_t *action, const wchar_t *proto, const wchar_t *if_name, const wchar_t *addr)
Run the command: netsh interface $proto $action dns $if_name $addr [validate=no].
static DWORD InterfaceLuid(const char *iface_name, PNET_LUID luid)
static BOOL ValidateOptions(HANDLE pipe, const WCHAR *workdir, const WCHAR *options, WCHAR *errmsg, DWORD capacity)
static DWORD UpdateWaitHandles(LPHANDLE *handles_ptr, LPDWORD count, HANDLE io_event, HANDLE exit_event, list_item_t *threads)
static BOOL CmpRoute(LPVOID item, LPVOID route)
static DWORD HandleDNSConfigMessage(const dns_cfg_message_t *msg, undo_lists_t *lists)
static void UnmapRingBuffer(ring_buffer_maps_t *ring_buffer_maps)
static BOOL CmpAny(LPVOID item, LPVOID any)
async_op_t
@ peek
@ write
@ read
static DWORD netsh_wins_cmd(const wchar_t *action, const wchar_t *if_name, const wchar_t *addr)
Run the command: netsh interface ip $action wins $if_name [static] $addr.
static SERVICE_STATUS_HANDLE service
Definition interactive.c:52
static DWORD WritePipeAsync(HANDLE pipe, LPVOID data, DWORD size, DWORD count, LPHANDLE events)
@ wfp_block_dns
Definition openvpn-msg.h:69
@ msg_del_address
Definition openvpn-msg.h:33
@ msg_add_wins_cfg
Definition openvpn-msg.h:47
@ msg_add_address
Definition openvpn-msg.h:32
@ msg_del_wfp_block
Definition openvpn-msg.h:42
@ msg_enable_dhcp
Definition openvpn-msg.h:44
@ msg_register_ring_buffers
Definition openvpn-msg.h:45
@ msg_add_wfp_block
Definition openvpn-msg.h:41
@ msg_add_route
Definition openvpn-msg.h:34
@ msg_del_wins_cfg
Definition openvpn-msg.h:48
@ msg_acknowledgement
Definition openvpn-msg.h:31
@ msg_add_dns_cfg
Definition openvpn-msg.h:36
@ msg_register_dns
Definition openvpn-msg.h:43
@ msg_del_route
Definition openvpn-msg.h:35
@ msg_set_mtu
Definition openvpn-msg.h:46
@ msg_flush_neighbors
Definition openvpn-msg.h:40
@ msg_del_dns_cfg
Definition openvpn-msg.h:37
#define M_ERR
Definition error.h:105
#define msg(flags,...)
Definition error.h:144
static bool register_ring_buffers(HANDLE device, struct tun_ring *send_ring, struct tun_ring *receive_ring, HANDLE send_tail_moved, HANDLE receive_tail_moved)
Registers ring buffers used to exchange data between userspace openvpn process and wintun kernel driv...
Definition ring_buffer.h:98
BOOL ReportStatusToSCMgr(SERVICE_STATUS_HANDLE service, SERVICE_STATUS *status)
Definition service.c:22
#define SERVICE_DEPENDENCIES
Definition service.h:43
#define M_SYSERR
Definition service.h:51
#define MSG_FLAGS_ERROR
Definition service.h:48
@ interactive
Definition service.h:55
static wchar_t * utf8to16(const char *utf8)
Convert a zero terminated UTF-8 string to UTF-16.
Definition service.h:123
static int pos(char c)
Definition base64.c:105
LPVOID data
Definition interactive.c:80
struct _list_item * next
Definition interactive.c:79
WCHAR * directory
Definition interactive.c:71
WCHAR * options
Definition interactive.c:72
WCHAR * std_input
Definition interactive.c:73
message_header_t header
Definition argv.h:35
Wrapper structure for dynamically allocated memory.
Definition buffer.h:61
Definition dhcp.h:53
char domains[512]
Definition openvpn-msg.h:93
interface_t iface
Definition openvpn-msg.h:92
char name[256]
Definition openvpn-msg.h:64
message_type_t type
Definition openvpn-msg.h:52
struct tun_ring * receive_ring
struct tun_ring * send_ring
interface_t iface
TCHAR exe_path[MAX_PATH]
Definition service.h:69
TCHAR ovpn_admin_group[MAX_NAME]
Definition service.h:73
DWORD priority
Definition service.h:74
Wintun ring buffer See https://github.com/WireGuard/wintun#ring-layout.
Definition ring_buffer.h:51
UCHAR data[WINTUN_RING_CAPACITY+WINTUN_RING_TRAILING_BYTES]
Definition ring_buffer.h:55
interface_t iface
struct in6_addr ipv6
Definition openvpn-msg.h:59
struct in_addr ipv4
Definition openvpn-msg.h:58
dns_cfg_message_t dns
address_message_t address
flush_neighbors_message_t flush_neighbors
wfp_block_message_t wfp_block
message_header_t header
wins_cfg_message_t wins
enable_dhcp_message_t dhcp
register_ring_buffers_message_t rrb
route_message_t route
set_mtu_message_t mtu
BOOL CheckOption(const WCHAR *workdir, int argc, WCHAR *argv[], const settings_t *s)
Definition validate.c:317
BOOL IsAuthorizedUser(PSID sid, const HANDLE token, const WCHAR *ovpn_admin_group)
Definition validate.c:148
static BOOL IsOption(const WCHAR *o)
Definition validate.h:42
int get_interface_metric(const NET_IFINDEX index, const ADDRESS_FAMILY family, int *is_auto)
Return interface metric value for the specified interface index.
Definition wfp_block.c:404
DWORD set_interface_metric(const NET_IFINDEX index, const ADDRESS_FAMILY family, const ULONG metric)
Sets interface metric value for specified interface index.
Definition wfp_block.c:443
DWORD delete_wfp_block_filters(HANDLE engine_handle)
Definition wfp_block.c:379
DWORD add_wfp_block_filters(HANDLE *engine_handle, int index, const WCHAR *exe_path, wfp_block_msg_handler_t msg_handler, BOOL dns_only)
Definition wfp_block.c:185
#define WFP_BLOCK_IFACE_METRIC
Definition wfp_block.h:34
char * get_win_sys_path(void)
Definition win32.c:1113