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-2026 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, see <https://www.gnu.org/licenses/>.
21 */
22
23
24#include "service.h"
25
26#include <ws2tcpip.h>
27#include <iphlpapi.h>
28#include <userenv.h>
29#include <accctrl.h>
30#include <aclapi.h>
31#include <stdio.h>
32#include <sddl.h>
33#include <shellapi.h>
34#include <mstcpip.h>
35#include <inttypes.h>
36#include <malloc.h>
37
38#include <versionhelpers.h>
39
40#include "openvpn-msg.h"
41#include "validate.h"
42#include "wfp_block.h"
43
44#define IO_TIMEOUT 2000 /*ms*/
45
46#define ERROR_OPENVPN_STARTUP 0x20000000
47#define ERROR_STARTUP_DATA 0x20000001
48#define ERROR_MESSAGE_DATA 0x20000002
49#define ERROR_MESSAGE_TYPE 0x20000003
50
51static SERVICE_STATUS_HANDLE service;
52static SERVICE_STATUS status = { .dwServiceType = SERVICE_WIN32_SHARE_PROCESS };
53static HANDLE exit_event = NULL;
55static HANDLE rdns_semaphore = NULL;
56#define RDNS_TIMEOUT 600 /* seconds to wait for the semaphore */
57
58#define TUN_IOCTL_REGISTER_RINGS \
59 CTL_CODE(51820U, 0x970U, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
60
61openvpn_service_t interactive_service = { interactive, _L(PACKAGE_NAME) L"ServiceInteractive",
62 _L(PACKAGE_NAME) L" Interactive Service",
63 SERVICE_DEPENDENCIES, SERVICE_AUTO_START };
64
65
66typedef struct
67{
68 WCHAR *directory;
69 WCHAR *options;
70 WCHAR *std_input;
72
73
74/* Datatype for linked lists */
75typedef struct _list_item
76{
78 LPVOID data;
80
81
82/* Datatypes for undo information */
96
97typedef struct
98{
99 HANDLE engine;
100 DWORD index;
104
105typedef struct
106{
107 char itf_name[256];
108 PWSTR domains;
110
125
126typedef struct
127{
128 CHAR addresses[NRPT_ADDR_NUM * NRPT_ADDR_SIZE];
129 WCHAR domains[512]; /* MULTI_SZ string */
130 DWORD domains_size; /* bytes in domains */
132
133
134static DWORD
135AddListItem(list_item_t **pfirst, LPVOID data)
136{
137 list_item_t *new_item = malloc(sizeof(list_item_t));
138 if (new_item == NULL)
139 {
140 return ERROR_OUTOFMEMORY;
141 }
142
143 new_item->next = *pfirst;
144 new_item->data = data;
145
146 *pfirst = new_item;
147 return NO_ERROR;
148}
149
150typedef BOOL (*match_fn_t)(LPVOID item, LPVOID ctx);
151
152static LPVOID
153RemoveListItem(list_item_t **pfirst, match_fn_t match, LPVOID ctx)
154{
155 LPVOID data = NULL;
156 list_item_t **pnext;
157
158 for (pnext = pfirst; *pnext; pnext = &(*pnext)->next)
159 {
160 list_item_t *item = *pnext;
161 if (!match(item->data, ctx))
162 {
163 continue;
164 }
165
166 /* Found item, remove from the list and free memory */
167 *pnext = item->next;
168 data = item->data;
169 free(item);
170 break;
171 }
172 return data;
173}
174
175
176static HANDLE
177CloseHandleEx(LPHANDLE handle)
178{
179 if (handle && *handle && *handle != INVALID_HANDLE_VALUE)
180 {
181 CloseHandle(*handle);
182 *handle = INVALID_HANDLE_VALUE;
183 }
184 return INVALID_HANDLE_VALUE;
185}
186
187static HANDLE
188InitOverlapped(LPOVERLAPPED overlapped)
189{
190 ZeroMemory(overlapped, sizeof(OVERLAPPED));
191 overlapped->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
192 return overlapped->hEvent;
193}
194
195static BOOL
196ResetOverlapped(LPOVERLAPPED overlapped)
197{
198 HANDLE io_event = overlapped->hEvent;
199 if (!ResetEvent(io_event))
200 {
201 return FALSE;
202 }
203 ZeroMemory(overlapped, sizeof(OVERLAPPED));
204 overlapped->hEvent = io_event;
205 return TRUE;
206}
207
208
216
217static DWORD
218AsyncPipeOp(async_op_t op, HANDLE pipe, LPVOID buffer, DWORD size, DWORD count, LPHANDLE events)
219{
220 DWORD i;
221 BOOL success;
222 HANDLE io_event;
223 DWORD res, bytes = 0;
224 OVERLAPPED overlapped;
225 LPHANDLE handles = NULL;
226
227 io_event = InitOverlapped(&overlapped);
228 if (!io_event)
229 {
230 goto out;
231 }
232
233 handles = malloc((count + 1) * sizeof(HANDLE));
234 if (!handles)
235 {
236 goto out;
237 }
238
239 if (op == write)
240 {
241 success = WriteFile(pipe, buffer, size, NULL, &overlapped);
242 }
243 else
244 {
245 success = ReadFile(pipe, buffer, size, NULL, &overlapped);
246 }
247 if (!success && GetLastError() != ERROR_IO_PENDING && GetLastError() != ERROR_MORE_DATA)
248 {
249 goto out;
250 }
251
252 handles[0] = io_event;
253 for (i = 0; i < count; i++)
254 {
255 handles[i + 1] = events[i];
256 }
257
258 res = WaitForMultipleObjects(count + 1, handles, FALSE, op == peek ? INFINITE : IO_TIMEOUT);
259 if (res != WAIT_OBJECT_0)
260 {
261 CancelIo(pipe);
262 goto out;
263 }
264
265 if (op == peek || op == peek_timed)
266 {
267 PeekNamedPipe(pipe, NULL, 0, NULL, &bytes, NULL);
268 }
269 else
270 {
271 GetOverlappedResult(pipe, &overlapped, &bytes, TRUE);
272 }
273
274out:
275 CloseHandleEx(&io_event);
276 free(handles);
277 return bytes;
278}
279
280static DWORD
281PeekNamedPipeAsync(HANDLE pipe, DWORD count, LPHANDLE events)
282{
283 return AsyncPipeOp(peek, pipe, NULL, 0, count, events);
284}
285
286static DWORD
287PeekNamedPipeAsyncTimed(HANDLE pipe, DWORD count, LPHANDLE events)
288{
289 return AsyncPipeOp(peek_timed, 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[] = { (DWORD_PTR)error, (DWORD_PTR)func, (DWORD_PTR) "" };
325
326 if (error != ERROR_OPENVPN_STARTUP)
327 {
328 FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER
329 | FORMAT_MESSAGE_IGNORE_INSERTS,
330 0, error, 0, (LPWSTR)&args[2], 0, NULL);
331 }
332
333 result_len = FormatMessageW(
334 FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY,
335 L"0x%1!08x!\n%2!s!\n%3!s!", 0, 0, (LPWSTR)&result, 0, (va_list *)args);
336
337 WritePipeAsync(pipe, result, (DWORD)(wcslen(result) * 2), count, events);
339
340 if (error != ERROR_OPENVPN_STARTUP)
341 {
342 LocalFree((LPVOID)args[2]);
343 }
344 if (result_len)
345 {
346 LocalFree(result);
347 }
348}
349
350
351static VOID
352ReturnLastError(HANDLE pipe, LPCWSTR func)
353{
354 ReturnError(pipe, GetLastError(), func, 1, &exit_event);
355}
356
357/*
358 * Validate options against a white list. Also check the config_file is
359 * inside the config_dir. The white list is defined in validate.c
360 * Returns true on success, false on error with reason set in errmsg.
361 */
362static BOOL
363ValidateOptions(HANDLE pipe, const WCHAR *workdir, const WCHAR *options, WCHAR *errmsg,
364 DWORD capacity)
365{
366 WCHAR **argv;
367 int argc;
368 BOOL ret = FALSE;
369 int i;
370 const WCHAR *msg1 = L"You have specified a config file location (%ls relative to %ls)"
371 L" that requires admin approval. This error may be avoided"
372 L" by adding your account to the \"%ls\" group";
373
374 const WCHAR *msg2 = L"You have specified an option (%ls) that may be used"
375 L" only with admin approval. This error may be avoided"
376 L" by adding your account to the \"%ls\" group";
377
378 argv = CommandLineToArgvW(options, &argc);
379
380 if (!argv)
381 {
382 swprintf(errmsg, capacity,
383 L"Cannot validate options: CommandLineToArgvW failed with error = 0x%08x",
384 GetLastError());
385 goto out;
386 }
387
388 /* Note: argv[0] is the first option */
389 if (argc < 1) /* no options */
390 {
391 ret = TRUE;
392 goto out;
393 }
394
395 /*
396 * If only one argument, it is the config file
397 */
398 if (argc == 1)
399 {
400 WCHAR *argv_tmp[2] = { L"--config", argv[0] };
401
402 if (!CheckOption(workdir, 2, argv_tmp, &settings))
403 {
404 swprintf(errmsg, capacity, msg1, argv[0], workdir, settings.ovpn_admin_group);
405 }
406 goto out;
407 }
408
409 for (i = 0; i < argc; ++i)
410 {
411 if (!IsOption(argv[i]))
412 {
413 continue;
414 }
415
416 if (!CheckOption(workdir, argc - i, &argv[i], &settings))
417 {
418 if (wcscmp(L"--config", argv[i]) == 0 && argc - i > 1)
419 {
420 swprintf(errmsg, capacity, msg1, argv[i + 1], workdir, settings.ovpn_admin_group);
421 }
422 else
423 {
424 swprintf(errmsg, capacity, msg2, argv[i], settings.ovpn_admin_group);
425 }
426 goto out;
427 }
428 }
429
430 /* all options passed */
431 ret = TRUE;
432
433out:
434 if (argv)
435 {
436 LocalFree(argv);
437 }
438 return ret;
439}
440
441static BOOL
442GetStartupData(HANDLE pipe, STARTUP_DATA *sud)
443{
444 size_t size, len;
445 WCHAR *data = NULL;
446 DWORD bytes, read;
447
448 bytes = PeekNamedPipeAsyncTimed(pipe, 1, &exit_event);
449 if (bytes == 0)
450 {
451 MsgToEventLog(M_ERR, L"Timeout waiting for startup data");
452 ReturnError(pipe, ERROR_STARTUP_DATA, L"GetStartupData (timeout)", 1, &exit_event);
453 goto err;
454 }
455
456 size = bytes / sizeof(*data);
457 if ((size == 0) || (size > 4096)) /* our startup data is 1024 wchars at the moment */
458 {
459 MsgToEventLog(M_SYSERR, L"malformed startup data: %lu bytes received", size);
460 ReturnError(pipe, ERROR_STARTUP_DATA, L"GetStartupData", 1, &exit_event);
461 goto err;
462 }
463
464 data = malloc(bytes);
465 if (data == NULL)
466 {
467 MsgToEventLog(M_SYSERR, L"malloc failed");
468 ReturnLastError(pipe, L"malloc");
469 goto err;
470 }
471
472 read = ReadPipeAsync(pipe, data, bytes, 1, &exit_event);
473 if (bytes != read)
474 {
475 MsgToEventLog(M_SYSERR, L"ReadPipeAsync failed");
476 ReturnLastError(pipe, L"ReadPipeAsync");
477 goto err;
478 }
479
480 if (data[size - 1] != 0)
481 {
482 MsgToEventLog(M_ERR, L"Startup data is not NULL terminated");
483 ReturnError(pipe, ERROR_STARTUP_DATA, L"GetStartupData", 1, &exit_event);
484 goto err;
485 }
486
487 sud->directory = data;
488 len = wcslen(sud->directory) + 1;
489 size -= len;
490 if (size == 0)
491 {
492 MsgToEventLog(M_ERR, L"Startup data ends at working directory");
493 ReturnError(pipe, ERROR_STARTUP_DATA, L"GetStartupData", 1, &exit_event);
494 goto err;
495 }
496
497 sud->options = sud->directory + len;
498 len = wcslen(sud->options) + 1;
499 size -= len;
500 if (size == 0)
501 {
502 MsgToEventLog(M_ERR, L"Startup data ends at command line options");
503 ReturnError(pipe, ERROR_STARTUP_DATA, L"GetStartupData", 1, &exit_event);
504 goto err;
505 }
506
507 sud->std_input = sud->options + len;
508 return TRUE;
509
510err:
511 sud->directory = NULL; /* caller must not free() */
512 free(data);
513 return FALSE;
514}
515
516
517static VOID
519{
520 free(sud->directory);
521}
522
523
524static SOCKADDR_INET
525sockaddr_inet(short family, inet_address_t *addr)
526{
527 SOCKADDR_INET sa_inet;
528 ZeroMemory(&sa_inet, sizeof(sa_inet));
529 sa_inet.si_family = family;
530 if (family == AF_INET)
531 {
532 sa_inet.Ipv4.sin_addr = addr->ipv4;
533 }
534 else if (family == AF_INET6)
535 {
536 sa_inet.Ipv6.sin6_addr = addr->ipv6;
537 }
538 return sa_inet;
539}
540
541static DWORD
542InterfaceLuid(const char *iface_name, PNET_LUID luid)
543{
544 NETIO_STATUS status;
545 LPWSTR wide_name = utf8to16(iface_name);
546
547 if (wide_name)
548 {
549 status = ConvertInterfaceAliasToLuid(wide_name, luid);
550 free(wide_name);
551 }
552 else
553 {
554 status = ERROR_OUTOFMEMORY;
555 }
556 return status;
557}
558
559static BOOL
560CmpAddress(LPVOID item, LPVOID address)
561{
562 return memcmp(item, address, sizeof(MIB_UNICASTIPADDRESS_ROW)) == 0 ? TRUE : FALSE;
563}
564
565static DWORD
566DeleteAddress(PMIB_UNICASTIPADDRESS_ROW addr_row)
567{
568 return DeleteUnicastIpAddressEntry(addr_row);
569}
570
571static DWORD
573{
574 DWORD err;
575 PMIB_UNICASTIPADDRESS_ROW addr_row;
576 BOOL add = msg->header.type == msg_add_address;
577
578 addr_row = malloc(sizeof(*addr_row));
579 if (addr_row == NULL)
580 {
581 return ERROR_OUTOFMEMORY;
582 }
583
584 InitializeUnicastIpAddressEntry(addr_row);
585 addr_row->Address = sockaddr_inet(msg->family, &msg->address);
586 addr_row->OnLinkPrefixLength = (UINT8)msg->prefix_len;
587
588 if (msg->iface.index != TUN_ADAPTER_INDEX_INVALID)
589 {
590 addr_row->InterfaceIndex = msg->iface.index;
591 }
592 else
593 {
594 NET_LUID luid;
595 err = InterfaceLuid(msg->iface.name, &luid);
596 if (err)
597 {
598 goto out;
599 }
600 addr_row->InterfaceLuid = luid;
601 }
602
603 if (add)
604 {
605 err = CreateUnicastIpAddressEntry(addr_row);
606 if (err)
607 {
608 goto out;
609 }
610
611 err = AddListItem(&(*lists)[address], addr_row);
612 if (err)
613 {
614 DeleteAddress(addr_row);
615 }
616 }
617 else
618 {
619 err = DeleteAddress(addr_row);
620 if (err)
621 {
622 goto out;
623 }
624
625 free(RemoveListItem(&(*lists)[address], CmpAddress, addr_row));
626 }
627
628out:
629 if (!add || err)
630 {
631 free(addr_row);
632 }
633
634 return err;
635}
636
637static BOOL
638CmpRoute(LPVOID item, LPVOID route)
639{
640 return memcmp(item, route, sizeof(MIB_IPFORWARD_ROW2)) == 0 ? TRUE : FALSE;
641}
642
643static DWORD
644DeleteRoute(PMIB_IPFORWARD_ROW2 fwd_row)
645{
646 return DeleteIpForwardEntry2(fwd_row);
647}
648
649static DWORD
651{
652 DWORD err;
653 PMIB_IPFORWARD_ROW2 fwd_row;
654 BOOL add = msg->header.type == msg_add_route;
655
656 fwd_row = malloc(sizeof(*fwd_row));
657 if (fwd_row == NULL)
658 {
659 return ERROR_OUTOFMEMORY;
660 }
661
662 ZeroMemory(fwd_row, sizeof(*fwd_row));
663 fwd_row->ValidLifetime = 0xffffffff;
664 fwd_row->PreferredLifetime = 0xffffffff;
665 fwd_row->Protocol = MIB_IPPROTO_NETMGMT;
666 fwd_row->Metric = msg->metric;
667 fwd_row->DestinationPrefix.Prefix = sockaddr_inet(msg->family, &msg->prefix);
668 fwd_row->DestinationPrefix.PrefixLength = (UINT8)msg->prefix_len;
669 fwd_row->NextHop = sockaddr_inet(msg->family, &msg->gateway);
670
671 if (msg->iface.index != TUN_ADAPTER_INDEX_INVALID)
672 {
673 fwd_row->InterfaceIndex = msg->iface.index;
674 }
675 else if (strlen(msg->iface.name))
676 {
677 NET_LUID luid;
678 err = InterfaceLuid(msg->iface.name, &luid);
679 if (err)
680 {
681 goto out;
682 }
683 fwd_row->InterfaceLuid = luid;
684 }
685
686 if (add)
687 {
688 err = CreateIpForwardEntry2(fwd_row);
689 if (err)
690 {
691 goto out;
692 }
693
694 err = AddListItem(&(*lists)[route], fwd_row);
695 if (err)
696 {
697 DeleteRoute(fwd_row);
698 }
699 }
700 else
701 {
702 err = DeleteRoute(fwd_row);
703 if (err)
704 {
705 goto out;
706 }
707
708 free(RemoveListItem(&(*lists)[route], CmpRoute, fwd_row));
709 }
710
711out:
712 if (!add || err)
713 {
714 free(fwd_row);
715 }
716
717 return err;
718}
719
720
721static DWORD
723{
724 if (msg->family == AF_INET)
725 {
726 return FlushIpNetTable(msg->iface.index);
727 }
728
729 return FlushIpNetTable2(msg->family, msg->iface.index);
730}
731
732static void
733BlockDNSErrHandler(DWORD err, const char *msg)
734{
735 WCHAR buf[256];
736 LPCWSTR err_str;
737
738 if (!err)
739 {
740 return;
741 }
742
743 err_str = L"Unknown Win32 Error";
744
745 if (FormatMessageW(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
746 NULL, err, 0, buf, _countof(buf), NULL))
747 {
748 err_str = buf;
749 }
750
751 MsgToEventLog(M_ERR, L"%hs (status = %lu): %ls", msg, err, err_str);
752}
753
754/* Use an always-true match_fn to get the head of the list */
755static BOOL
756CmpAny(LPVOID item, LPVOID any)
757{
758 return TRUE;
759}
760
761static DWORD
763{
764 DWORD err = 0;
765 wfp_block_data_t *block_data = RemoveListItem(&(*lists)[wfp_block], CmpAny, NULL);
766
767 if (block_data)
768 {
769 err = delete_wfp_block_filters(block_data->engine);
770 if (block_data->metric_v4 >= 0)
771 {
772 set_interface_metric(block_data->index, AF_INET, block_data->metric_v4);
773 }
774 if (block_data->metric_v6 >= 0)
775 {
776 set_interface_metric(block_data->index, AF_INET6, block_data->metric_v6);
777 }
778 free(block_data);
779 }
780 else
781 {
782 MsgToEventLog(M_ERR, L"No previous block filters to delete");
783 }
784
785 return err;
786}
787
788static DWORD
790{
791 DWORD err = 0;
792 wfp_block_data_t *block_data = NULL;
793 HANDLE engine = NULL;
794 LPCWSTR exe_path;
795 BOOL dns_only;
796
797 exe_path = settings.exe_path;
798 dns_only = (msg->flags == wfp_block_dns);
799
800 err = add_wfp_block_filters(&engine, msg->iface.index, exe_path, BlockDNSErrHandler, dns_only);
801 if (!err)
802 {
803 block_data = malloc(sizeof(wfp_block_data_t));
804 if (!block_data)
805 {
806 err = ERROR_OUTOFMEMORY;
807 goto out;
808 }
809 block_data->engine = engine;
810 block_data->index = msg->iface.index;
811 int is_auto = 0;
812 block_data->metric_v4 = get_interface_metric(msg->iface.index, AF_INET, &is_auto);
813 if (is_auto)
814 {
815 block_data->metric_v4 = 0;
816 }
817 block_data->metric_v6 = get_interface_metric(msg->iface.index, AF_INET6, &is_auto);
818 if (is_auto)
819 {
820 block_data->metric_v6 = 0;
821 }
822
823 err = AddListItem(&(*lists)[wfp_block], block_data);
824 if (!err)
825 {
826 err = set_interface_metric(msg->iface.index, AF_INET, WFP_BLOCK_IFACE_METRIC);
827 if (!err)
828 {
829 /* for IPv6, we intentionally ignore errors, because
830 * otherwise block-dns activation will fail if a user or
831 * admin has disabled IPv6 on the tun/tap/dco interface
832 * (if OpenVPN wants IPv6 ifconfig, we'll fail there)
833 */
834 set_interface_metric(msg->iface.index, AF_INET6, WFP_BLOCK_IFACE_METRIC);
835 }
836 if (err)
837 {
838 /* delete the filters, remove undo item and free interface data */
839 DeleteWfpBlock(lists);
840 engine = NULL;
841 }
842 }
843 }
844
845out:
846 if (err && engine)
847 {
849 free(block_data);
850 }
851
852 return err;
853}
854
855static DWORD
857{
858 if (msg->header.type == msg_add_wfp_block)
859 {
860 return AddWfpBlock(msg, lists);
861 }
862 else
863 {
864 return DeleteWfpBlock(lists);
865 }
866}
867
868/*
869 * Execute a command and return its exit code. If timeout > 0, terminate
870 * the process if still running after timeout milliseconds. In that case
871 * the return value is the windows error code WAIT_TIMEOUT = 0x102
872 */
873static DWORD
874ExecCommand(const WCHAR *argv0, const WCHAR *cmdline, DWORD timeout)
875{
876 DWORD exit_code;
877 STARTUPINFOW si;
878 PROCESS_INFORMATION pi;
879 DWORD proc_flags = CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT;
880 WCHAR *cmdline_dup = NULL;
881
882 ZeroMemory(&si, sizeof(si));
883 ZeroMemory(&pi, sizeof(pi));
884
885 si.cb = sizeof(si);
886
887 /* CreateProcess needs a modifiable cmdline: make a copy */
888 cmdline_dup = _wcsdup(cmdline);
889 if (cmdline_dup
890 && CreateProcessW(argv0, cmdline_dup, NULL, NULL, FALSE, proc_flags, NULL, NULL, &si, &pi))
891 {
892 WaitForSingleObject(pi.hProcess, timeout ? timeout : INFINITE);
893 if (!GetExitCodeProcess(pi.hProcess, &exit_code))
894 {
895 MsgToEventLog(M_SYSERR, L"ExecCommand: Error getting exit_code:");
896 exit_code = GetLastError();
897 }
898 else if (exit_code == STILL_ACTIVE)
899 {
900 exit_code = WAIT_TIMEOUT; /* Windows error code 0x102 */
901
902 /* kill without impunity */
903 TerminateProcess(pi.hProcess, exit_code);
904 MsgToEventLog(M_ERR, L"ExecCommand: \"%ls %ls\" killed after timeout", argv0, cmdline);
905 }
906 else if (exit_code)
907 {
908 MsgToEventLog(M_ERR, L"ExecCommand: \"%ls %ls\" exited with status = %lu", argv0,
909 cmdline, exit_code);
910 }
911 else
912 {
913 MsgToEventLog(M_INFO, L"ExecCommand: \"%ls %ls\" completed", argv0, cmdline);
914 }
915
916 CloseHandle(pi.hProcess);
917 CloseHandle(pi.hThread);
918 }
919 else
920 {
921 exit_code = GetLastError();
922 MsgToEventLog(M_SYSERR, L"ExecCommand: could not run \"%ls %ls\" :", argv0, cmdline);
923 }
924
925 free(cmdline_dup);
926 return exit_code;
927}
928
929/*
930 * Entry point for register-dns thread.
931 */
932static DWORD WINAPI
933RegisterDNS(LPVOID unused)
934{
935 DWORD err;
936 size_t i;
937 DWORD timeout = RDNS_TIMEOUT * 1000; /* in milliseconds */
938
939 /* path of ipconfig command */
940 WCHAR ipcfg[MAX_PATH];
941
942 struct
943 {
944 WCHAR *argv0;
945 WCHAR *cmdline;
946 DWORD timeout;
947 } cmds[] = {
948 { ipcfg, L"ipconfig /flushdns", timeout },
949 { ipcfg, L"ipconfig /registerdns", timeout },
950 };
951
952 HANDLE wait_handles[2] = { rdns_semaphore, exit_event };
953
954 swprintf(ipcfg, MAX_PATH, L"%ls\\%ls", get_win_sys_path(), L"ipconfig.exe");
955
956 if (WaitForMultipleObjects(2, wait_handles, FALSE, timeout) == WAIT_OBJECT_0)
957 {
958 /* Semaphore locked */
959 for (i = 0; i < _countof(cmds); ++i)
960 {
961 ExecCommand(cmds[i].argv0, cmds[i].cmdline, cmds[i].timeout);
962 }
963 err = 0;
964 if (!ReleaseSemaphore(rdns_semaphore, 1, NULL))
965 {
966 err =
967 MsgToEventLog(M_SYSERR, L"RegisterDNS: Failed to release regsiter-dns semaphore:");
968 }
969 }
970 else
971 {
972 MsgToEventLog(M_ERR, L"RegisterDNS: Failed to lock register-dns semaphore");
973 err = ERROR_SEM_TIMEOUT; /* Windows error code 0x79 */
974 }
975 return err;
976}
977
978static DWORD
980{
981 DWORD err;
982 HANDLE thread = NULL;
983
984 /* Delegate this job to a sub-thread */
985 thread = CreateThread(NULL, 0, RegisterDNS, NULL, 0, NULL);
986
987 /*
988 * We don't add these thread handles to the undo list -- the thread and
989 * processes it spawns are all supposed to terminate or timeout by themselves.
990 */
991 if (thread)
992 {
993 err = 0;
994 CloseHandle(thread);
995 }
996 else
997 {
998 err = GetLastError();
999 }
1000
1001 return err;
1002}
1003
1013static DWORD
1014netsh_wins_cmd(const wchar_t *action, DWORD if_index, const wchar_t *addr)
1015{
1016 DWORD err = 0;
1017 int timeout = 30000; /* in msec */
1018 wchar_t argv0[MAX_PATH];
1019 wchar_t *cmdline = NULL;
1020 const wchar_t *addr_static = (wcscmp(action, L"set") == 0) ? L"static" : L"";
1021
1022 if (!addr)
1023 {
1024 if (wcscmp(action, L"delete") == 0)
1025 {
1026 addr = L"all";
1027 }
1028 else /* nothing to do -- return success*/
1029 {
1030 goto out;
1031 }
1032 }
1033
1034 /* Path of netsh */
1035 swprintf(argv0, _countof(argv0), L"%ls\\%ls", get_win_sys_path(), L"netsh.exe");
1036
1037 /* cmd template:
1038 * netsh interface ip $action wins $if_name $static $addr
1039 */
1040 const wchar_t *fmt = L"netsh interface ip %ls wins %lu %ls %ls";
1041
1042 /* max cmdline length in wchars -- include room for worst case and some */
1043 size_t ncmdline = wcslen(fmt) + 11 /*if_index*/ + wcslen(action) + wcslen(addr)
1044 + wcslen(addr_static) + 32 + 1;
1045 cmdline = malloc(ncmdline * sizeof(wchar_t));
1046 if (!cmdline)
1047 {
1048 err = ERROR_OUTOFMEMORY;
1049 goto out;
1050 }
1051
1052 swprintf(cmdline, ncmdline, fmt, action, if_index, addr_static, addr);
1053
1054 err = ExecCommand(argv0, cmdline, timeout);
1055
1056out:
1057 free(cmdline);
1058 return err;
1059}
1060
1067static BOOL
1069{
1070 typedef NTSTATUS(__stdcall * publish_fn_t)(DWORD StateNameLo, DWORD StateNameHi, DWORD TypeId,
1071 DWORD Buffer, DWORD Length, DWORD ExplicitScope);
1072 publish_fn_t RtlPublishWnfStateData;
1073 const DWORD WNF_GPOL_SYSTEM_CHANGES_HI = 0x0D891E2A;
1074 const DWORD WNF_GPOL_SYSTEM_CHANGES_LO = 0xA3BC0875;
1075 BOOL ret = FALSE;
1076
1077 HMODULE ntdll = LoadLibraryA("ntdll.dll");
1078 if (ntdll == NULL)
1079 {
1080 return FALSE;
1081 }
1082
1083 RtlPublishWnfStateData = (publish_fn_t)GetProcAddress(ntdll, "RtlPublishWnfStateData");
1084 if (RtlPublishWnfStateData == NULL)
1085 {
1086 goto cleanup;
1087 }
1088
1089 if (RtlPublishWnfStateData(WNF_GPOL_SYSTEM_CHANGES_LO, WNF_GPOL_SYSTEM_CHANGES_HI, 0, 0, 0, 0)
1090 != ERROR_SUCCESS)
1091 {
1092 goto cleanup;
1093 }
1094
1095 ret = TRUE;
1096cleanup:
1097 FreeLibrary(ntdll);
1098 return ret;
1099}
1100
1107static BOOL
1109{
1110 typedef NTSTATUS (*publish_fn_t)(INT64 StateName, INT64 TypeId, INT64 Buffer,
1111 unsigned int Length, INT64 ExplicitScope);
1112 publish_fn_t RtlPublishWnfStateData;
1113 const INT64 WNF_GPOL_SYSTEM_CHANGES = 0x0D891E2AA3BC0875;
1114 BOOL ret = FALSE;
1115
1116 HMODULE ntdll = LoadLibraryA("ntdll.dll");
1117 if (ntdll == NULL)
1118 {
1119 return FALSE;
1120 }
1121
1122 RtlPublishWnfStateData = (publish_fn_t)GetProcAddress(ntdll, "RtlPublishWnfStateData");
1123 if (RtlPublishWnfStateData == NULL)
1124 {
1125 goto cleanup;
1126 }
1127
1128 if (RtlPublishWnfStateData(WNF_GPOL_SYSTEM_CHANGES, 0, 0, 0, 0) != ERROR_SUCCESS)
1129 {
1130 goto cleanup;
1131 }
1132
1133 ret = TRUE;
1134cleanup:
1135 FreeLibrary(ntdll);
1136 return ret;
1137}
1138
1144static BOOL
1146{
1147 SYSTEM_INFO si;
1148 GetSystemInfo(&si);
1149 const BOOL win_32bit = si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL;
1150 return win_32bit ? ApplyGpolSettings32() : ApplyGpolSettings64();
1151}
1152
1160static BOOL
1161ApplyDnsSettings(BOOL apply_gpol)
1162{
1163 BOOL res = FALSE;
1164 SC_HANDLE scm = NULL;
1165 SC_HANDLE dnssvc = NULL;
1166
1167 if (apply_gpol && ApplyGpolSettings() == FALSE)
1168 {
1169 MsgToEventLog(M_ERR, L"%S: sending GPOL notification failed", __func__);
1170 }
1171
1172 scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
1173 if (scm == NULL)
1174 {
1175 MsgToEventLog(M_ERR, L"%S: OpenSCManager call failed (%lu)", __func__, GetLastError());
1176 goto out;
1177 }
1178
1179 dnssvc = OpenServiceA(scm, "Dnscache", SERVICE_PAUSE_CONTINUE);
1180 if (dnssvc == NULL)
1181 {
1182 MsgToEventLog(M_ERR, L"%S: OpenService call failed (%lu)", __func__, GetLastError());
1183 goto out;
1184 }
1185
1186 SERVICE_STATUS status;
1187 if (ControlService(dnssvc, SERVICE_CONTROL_PARAMCHANGE, &status) == 0)
1188 {
1189 MsgToEventLog(M_ERR, L"%S: ControlService call failed (%lu)", __func__, GetLastError());
1190 goto out;
1191 }
1192
1193 res = TRUE;
1194
1195out:
1196 if (dnssvc)
1197 {
1198 CloseServiceHandle(dnssvc);
1199 }
1200 if (scm)
1201 {
1202 CloseServiceHandle(scm);
1203 }
1204 return res;
1205}
1206
1216static DWORD
1217InterfaceIdString(PCSTR itf_name, PWSTR str, size_t len)
1218{
1219 DWORD err;
1220 GUID guid;
1221 NET_LUID luid;
1222 PWSTR iid_str = NULL;
1223
1224 err = InterfaceLuid(itf_name, &luid);
1225 if (err)
1226 {
1227 MsgToEventLog(M_ERR, L"%S: failed to convert itf alias '%s'", __func__, itf_name);
1228 goto out;
1229 }
1230 err = ConvertInterfaceLuidToGuid(&luid, &guid);
1231 if (err)
1232 {
1233 MsgToEventLog(M_ERR, L"%S: Failed to convert itf '%s' LUID", __func__, itf_name);
1234 goto out;
1235 }
1236
1237 if (StringFromIID(&guid, &iid_str) != S_OK)
1238 {
1239 MsgToEventLog(M_ERR, L"%S: Failed to convert itf '%s' IID", __func__, itf_name);
1240 err = ERROR_OUTOFMEMORY;
1241 goto out;
1242 }
1243 if (wcslen(iid_str) + 1 > len)
1244 {
1245 err = ERROR_INVALID_PARAMETER;
1246 goto out;
1247 }
1248
1249 wcsncpy(str, iid_str, len);
1250
1251out:
1252 if (iid_str)
1253 {
1254 CoTaskMemFree(iid_str);
1255 }
1256 return err;
1257}
1258
1272static BOOL
1274{
1275 char data[64];
1276 DWORD size = sizeof(data);
1277 LSTATUS err = RegGetValueA(key, NULL, "SearchList", RRF_RT_REG_SZ, NULL, (PBYTE)data, &size);
1278 if (!err || err == ERROR_MORE_DATA)
1279 {
1280 data[sizeof(data) - 1] = '\0';
1281 for (size_t i = 0; i < strlen(data); ++i)
1282 {
1283 if (isalnum(data[i]) || data[i] == '-' || data[i] == '.')
1284 {
1285 return TRUE;
1286 }
1287 }
1288 }
1289 return FALSE;
1290}
1291
1309static BOOL
1310GetDnsSearchListKey(PCSTR itf_name, PBOOL gpol, PHKEY key)
1311{
1312 LSTATUS err;
1313
1314 *gpol = FALSE;
1315
1316 /* Try the group policy search list */
1317 err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient",
1318 0, KEY_ALL_ACCESS, key);
1319 if (!err)
1320 {
1321 if (HasValidSearchList(*key))
1322 {
1323 *gpol = TRUE;
1324 return TRUE;
1325 }
1326 RegCloseKey(*key);
1327 }
1328
1329 /* Try the system-wide search list */
1330 err =
1331 RegOpenKeyExA(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\TCPIP\\Parameters",
1332 0, KEY_ALL_ACCESS, key);
1333 if (!err)
1334 {
1335 if (HasValidSearchList(*key))
1336 {
1337 return TRUE;
1338 }
1339 RegCloseKey(*key);
1340 }
1341
1342 if (itf_name)
1343 {
1344 /* Always return the VPN interface key (if it exists) */
1345 WCHAR iid[64];
1346 DWORD iid_err = InterfaceIdString(itf_name, iid, _countof(iid));
1347 if (!iid_err)
1348 {
1349 HKEY itfs;
1350 err =
1351 RegOpenKeyExA(HKEY_LOCAL_MACHINE,
1352 "System\\CurrentControlSet\\Services\\TCPIP\\Parameters\\Interfaces",
1353 0, KEY_ALL_ACCESS, &itfs);
1354 if (!err)
1355 {
1356 err = RegOpenKeyExW(itfs, iid, 0, KEY_ALL_ACCESS, key);
1357 RegCloseKey(itfs);
1358 if (!err)
1359 {
1360 return FALSE; /* No need to preserve the VPN itf search list */
1361 }
1362 }
1363 }
1364 }
1365
1366 *key = INVALID_HANDLE_VALUE;
1367 return FALSE;
1368}
1369
1377static BOOL
1379{
1380 LSTATUS err;
1381
1382 err = RegGetValueA(key, NULL, "InitialSearchList", RRF_RT_REG_SZ, NULL, NULL, NULL);
1383 if (err)
1384 {
1385 if (err == ERROR_FILE_NOT_FOUND)
1386 {
1387 return FALSE;
1388 }
1389 MsgToEventLog(M_ERR, L"%S: failed to get InitialSearchList (%lu)", __func__, err);
1390 }
1391
1392 return TRUE;
1393}
1394
1399static DWORD
1400RegWStringSize(PCWSTR string)
1401{
1402 size_t length = (wcslen(string) + 1) * sizeof(wchar_t);
1403 if (length > UINT_MAX)
1404 {
1405 length = UINT_MAX;
1406 }
1407 return (DWORD)length;
1408}
1409
1420static BOOL
1422{
1423 if (!list || wcslen(list) == 0)
1424 {
1425 MsgToEventLog(M_ERR, L"%S: empty search list", __func__);
1426 return FALSE;
1427 }
1428
1430 {
1431 /* Initial list had already been stored */
1432 return TRUE;
1433 }
1434
1435 DWORD size = RegWStringSize(list);
1436 LSTATUS err = RegSetValueExW(key, L"InitialSearchList", 0, REG_SZ, (PBYTE)list, size);
1437 if (err)
1438 {
1439 MsgToEventLog(M_ERR, L"%S: failed to set InitialSearchList value (%lu)", __func__, err);
1440 return FALSE;
1441 }
1442
1443 return TRUE;
1444}
1445
1460static BOOL
1461AppendSearchList(PWSTR list, size_t list_cap, PCWSTR add)
1462{
1463 size_t list_len = wcslen(list);
1464 size_t add_len = wcslen(add);
1465 if (add_len == 0)
1466 {
1467 return TRUE;
1468 }
1469
1470 size_t sep_len = (list_len > 0) ? 1 : 0;
1471 if (list_len + sep_len + add_len + 1 > list_cap)
1472 {
1473 return FALSE;
1474 }
1475
1476 if (sep_len)
1477 {
1478 list[list_len++] = L',';
1479 }
1480 wmemcpy(list + list_len, add, add_len + 1);
1481 return TRUE;
1482}
1483
1507static size_t
1508RemoveSearchListTokens(PWSTR list, PCWSTR remove)
1509{
1510 size_t removed = 0;
1511 PCWSTR domain = remove;
1512 while (*domain)
1513 {
1514 PCWSTR comma = wcschr(domain, L',');
1515 size_t domain_len = comma ? (size_t)(comma - domain) : wcslen(domain);
1516 if (domain_len > 0)
1517 {
1518 /* Find the last token in @p list that exactly equals @p domain. */
1519 PWSTR match = NULL;
1520 PWSTR match_end = NULL;
1521 for (PWSTR p = list; *p;)
1522 {
1523 PWSTR tok_end = wcschr(p, L',');
1524 size_t tok_len = tok_end ? (size_t)(tok_end - p) : wcslen(p);
1525 if (tok_len == domain_len && wcsncmp(p, domain, domain_len) == 0)
1526 {
1527 match = p;
1528 match_end = tok_end;
1529 }
1530 if (!tok_end)
1531 {
1532 break;
1533 }
1534 p = tok_end + 1;
1535 }
1536 if (match)
1537 {
1538 /* Splice the token out, eating its leading comma if it has
1539 * one, otherwise its trailing comma. */
1540 PWSTR cut_start, cut_end;
1541 if (match == list)
1542 {
1543 cut_start = match;
1544 cut_end = match_end ? match_end + 1 : match + domain_len;
1545 }
1546 else
1547 {
1548 cut_start = match - 1;
1549 cut_end = match + domain_len;
1550 }
1551 wmemmove(cut_start, cut_end, wcslen(cut_end) + 1);
1552 removed++;
1553 }
1554 }
1555 if (!comma)
1556 {
1557 break;
1558 }
1559 domain = comma + 1;
1560 }
1561 return removed;
1562}
1563
1580static BOOL
1581AddDnsSearchDomains(HKEY key, BOOL have_list, PCWSTR domains)
1582{
1583 WCHAR list[2048] = { 0 };
1584
1585 if (have_list)
1586 {
1587 DWORD size = sizeof(list);
1588 LSTATUS err =
1589 RegGetValueW(key, NULL, L"SearchList", RRF_RT_REG_SZ, NULL, list, &size);
1590 if (err)
1591 {
1592 MsgToEventLog(M_SYSERR, L"%S: could not get SearchList from registry (%lu)", __func__,
1593 err);
1594 return FALSE;
1595 }
1596
1597 if (!StoreInitialDnsSearchList(key, list))
1598 {
1599 return FALSE;
1600 }
1601 }
1602
1603 if (!AppendSearchList(list, _countof(list), domains))
1604 {
1605 MsgToEventLog(M_SYSERR, L"%S: not enough space in list for search domains", __func__);
1606 return FALSE;
1607 }
1608
1609 DWORD size = RegWStringSize(list);
1610 LSTATUS err = RegSetValueExW(key, L"SearchList", 0, REG_SZ, (PBYTE)list, size);
1611 if (err)
1612 {
1613 MsgToEventLog(M_SYSERR, L"%S: could not set SearchList to registry (%lu)", __func__, err);
1614 return FALSE;
1615 }
1616
1617 return TRUE;
1618}
1619
1631static BOOL
1633{
1634 LSTATUS err;
1635 BOOL ret = FALSE;
1636 WCHAR list[2048];
1637 DWORD size = sizeof(list);
1638
1639 err = RegGetValueW(key, NULL, L"InitialSearchList", RRF_RT_REG_SZ, NULL, list, &size);
1640 if (err)
1641 {
1642 if (err != ERROR_FILE_NOT_FOUND)
1643 {
1644 MsgToEventLog(M_SYSERR, L"%S: could not get InitialSearchList from registry (%lu)",
1645 __func__, err);
1646 }
1647 goto out;
1648 }
1649
1650 size = RegWStringSize(list);
1651 err = RegSetValueExW(key, L"SearchList", 0, REG_SZ, (PBYTE)list, size);
1652 if (err)
1653 {
1654 MsgToEventLog(M_SYSERR, L"%S: could not set SearchList in registry (%lu)", __func__, err);
1655 goto out;
1656 }
1657
1658 RegDeleteValueA(key, "InitialSearchList");
1659 ret = TRUE;
1660
1661out:
1662 return ret;
1663}
1664
1679static void
1680RemoveDnsSearchDomains(HKEY key, PCWSTR domains)
1681{
1682 WCHAR list[2048];
1683 DWORD size = sizeof(list);
1684 LSTATUS err = RegGetValueW(key, NULL, L"SearchList", RRF_RT_REG_SZ, NULL, list, &size);
1685 if (err)
1686 {
1687 MsgToEventLog(M_SYSERR, L"%S: could not get SearchList from registry (%lu)", __func__, err);
1688 return;
1689 }
1690
1691 if (RemoveSearchListTokens(list, domains) == 0)
1692 {
1693 MsgToEventLog(M_ERR, L"%S: could not find domains in search list", __func__);
1694 return;
1695 }
1696
1697 if (list[0] != L'\0')
1698 {
1699 /* If the shortened list equals the snapshot we took at first
1700 * touch, the user's pre-VPN state is fully restored -- wipe both
1701 * SearchList and InitialSearchList. */
1702 WCHAR initial[2048];
1703 size = sizeof(initial);
1704 err = RegGetValueW(key, NULL, L"InitialSearchList", RRF_RT_REG_SZ, NULL, initial, &size);
1705 if (!err && wcscmp(list, initial) == 0)
1706 {
1708 return;
1709 }
1710 if (err && err != ERROR_FILE_NOT_FOUND)
1711 {
1712 MsgToEventLog(M_SYSERR, L"%S: could not get InitialSearchList from registry (%lu)",
1713 __func__, err);
1714 return;
1715 }
1716 }
1717
1718 size = RegWStringSize(list);
1719 err = RegSetValueExW(key, L"SearchList", 0, REG_SZ, (PBYTE)list, size);
1720 if (err)
1721 {
1722 MsgToEventLog(M_SYSERR, L"%S: could not set SearchList in registry (%lu)", __func__, err);
1723 }
1724}
1725
1731static void
1733{
1734 BOOL gpol;
1735 HKEY dns_searchlist_key;
1736 GetDnsSearchListKey(undo_data->itf_name, &gpol, &dns_searchlist_key);
1737 if (dns_searchlist_key != INVALID_HANDLE_VALUE)
1738 {
1739 RemoveDnsSearchDomains(dns_searchlist_key, undo_data->domains);
1740 RegCloseKey(dns_searchlist_key);
1741 ApplyDnsSettings(gpol);
1742
1743 free(undo_data->domains);
1744 undo_data->domains = NULL;
1745 }
1746}
1747
1769static DWORD
1770SetDnsSearchDomains(PCSTR itf_name, PCSTR domains, PBOOL gpol, undo_lists_t *lists)
1771{
1772 DWORD err = ERROR_OUTOFMEMORY;
1773
1774 HKEY list_key;
1775 BOOL have_list = GetDnsSearchListKey(itf_name, gpol, &list_key);
1776 if (list_key == INVALID_HANDLE_VALUE)
1777 {
1778 MsgToEventLog(M_SYSERR, L"%S: could not get search list registry key", __func__);
1779 return ERROR_FILE_NOT_FOUND;
1780 }
1781
1782 /* Remove previously installed search domains */
1783 dns_domains_undo_data_t *undo_data = RemoveListItem(&(*lists)[undo_domains], CmpAny, NULL);
1784 if (undo_data)
1785 {
1786 RemoveDnsSearchDomains(list_key, undo_data->domains);
1787 free(undo_data->domains);
1788 free(undo_data);
1789 undo_data = NULL;
1790 }
1791
1792 /* If there are search domains, add them */
1793 if (domains && *domains)
1794 {
1795 wchar_t *wide_domains = utf8to16(domains); /* utf8 to wide-char */
1796 if (!wide_domains)
1797 {
1798 goto out;
1799 }
1800
1801 undo_data = malloc(sizeof(*undo_data));
1802 if (!undo_data)
1803 {
1804 free(wide_domains);
1805 wide_domains = NULL;
1806 goto out;
1807 }
1808 strncpy(undo_data->itf_name, itf_name, sizeof(undo_data->itf_name));
1809 undo_data->domains = wide_domains;
1810
1811 if (AddDnsSearchDomains(list_key, have_list, wide_domains) == FALSE
1812 || AddListItem(&(*lists)[undo_domains], undo_data) != NO_ERROR)
1813 {
1814 RemoveDnsSearchDomains(list_key, wide_domains);
1815 free(wide_domains);
1816 free(undo_data);
1817 undo_data = NULL;
1818 goto out;
1819 }
1820 }
1821
1822 err = NO_ERROR;
1823
1824out:
1825 RegCloseKey(list_key);
1826 return err;
1827}
1828
1836static BOOL
1837GetInterfacesKey(short family, PHKEY key)
1838{
1839 PCSTR itfs_key = family == AF_INET6
1840 ? "SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters\\Interfaces"
1841 : "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces";
1842
1843 LSTATUS err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, itfs_key, 0, KEY_ALL_ACCESS, key);
1844 if (err)
1845 {
1846 *key = INVALID_HANDLE_VALUE;
1847 MsgToEventLog(M_SYSERR, L"%S: could not open interfaces registry key for family %d (%lu)",
1848 __func__, family, err);
1849 }
1850
1851 return err ? FALSE : TRUE;
1852}
1853
1863static DWORD
1864SetNameServersValue(PCWSTR itf_id, short family, PCSTR value)
1865{
1866 DWORD err;
1867
1868 HKEY itfs;
1869 if (!GetInterfacesKey(family, &itfs))
1870 {
1871 return ERROR_FILE_NOT_FOUND;
1872 }
1873
1874 HKEY itf = INVALID_HANDLE_VALUE;
1875 err = RegOpenKeyExW(itfs, itf_id, 0, KEY_ALL_ACCESS, &itf);
1876 if (err)
1877 {
1878 MsgToEventLog(M_SYSERR, L"%S: could not open interface key for %s family %d (%lu)",
1879 __func__, itf_id, family, err);
1880 goto out;
1881 }
1882
1883 err = RegSetValueExA(itf, "NameServer", 0, REG_SZ, (PBYTE)value, (DWORD)strlen(value) + 1);
1884 if (err)
1885 {
1886 MsgToEventLog(M_SYSERR, L"%S: could not set name servers '%S' for %s family %d (%lu)",
1887 __func__, value, itf_id, family, err);
1888 }
1889
1890out:
1891 if (itf != INVALID_HANDLE_VALUE)
1892 {
1893 RegCloseKey(itf);
1894 }
1895 if (itfs != INVALID_HANDLE_VALUE)
1896 {
1897 RegCloseKey(itfs);
1898 }
1899 return err;
1900}
1901
1911static DWORD
1912SetNameServers(PCWSTR itf_id, short family, PCSTR addrs)
1913{
1914 return SetNameServersValue(itf_id, family, addrs);
1915}
1916
1925static DWORD
1926ResetNameServers(PCWSTR itf_id, short family)
1927{
1928 return SetNameServersValue(itf_id, family, "");
1929}
1930
1931static DWORD
1933{
1934 DWORD err = 0;
1935 undo_type_t undo_type = (msg->family == AF_INET6) ? undo_dns6 : undo_dns4;
1936 unsigned int addr_len = msg->addr_len;
1937
1938 /* sanity check */
1939 const unsigned int max_addrs = _countof(msg->addr);
1940 if (addr_len > max_addrs)
1941 {
1942 addr_len = max_addrs;
1943 }
1944
1945 if (!msg->iface.name[0]) /* interface name is required */
1946 {
1947 return ERROR_MESSAGE_DATA;
1948 }
1949
1950 /* use a non-const reference with limited scope to enforce null-termination of strings from
1951 * client */
1952 {
1954 msgptr->iface.name[_countof(msg->iface.name) - 1] = '\0';
1955 msgptr->domains[_countof(msg->domains) - 1] = '\0';
1956 }
1957
1958 WCHAR iid[64];
1959 err = InterfaceIdString(msg->iface.name, iid, _countof(iid));
1960 if (err)
1961 {
1962 return err;
1963 }
1964
1965 /* We delete all current addresses before adding any
1966 * OR if the message type is del_dns_cfg
1967 */
1968 if (addr_len > 0 || msg->header.type == msg_del_dns_cfg)
1969 {
1970 err = ResetNameServers(iid, msg->family);
1971 if (err)
1972 {
1973 return err;
1974 }
1975 free(RemoveListItem(&(*lists)[undo_type], CmpAny, iid));
1976 }
1977
1978 if (msg->header.type == msg_del_dns_cfg)
1979 {
1980 BOOL gpol = FALSE;
1981 if (msg->domains[0])
1982 {
1983 /* setting an empty domain list removes any previous value */
1984 err = SetDnsSearchDomains(msg->iface.name, NULL, &gpol, lists);
1985 }
1986 ApplyDnsSettings(gpol);
1987 return err; /* job done */
1988 }
1989
1990 if (addr_len > 0)
1991 {
1992 /* prepare the comma separated address list */
1993 /* cannot use max_addrs here as that is not considered compile
1994 * time constant by all compilers and constexpr is C23 */
1995 CHAR addrs[_countof(msg->addr) * 64]; /* 64 is enough for one IPv4/6 address */
1996 size_t offset = 0;
1997 for (unsigned int i = 0; i < addr_len; ++i)
1998 {
1999 if (i != 0)
2000 {
2001 addrs[offset++] = ',';
2002 }
2003 if (msg->family == AF_INET6)
2004 {
2005 RtlIpv6AddressToStringA(&msg->addr[i].ipv6, addrs + offset);
2006 }
2007 else
2008 {
2009 RtlIpv4AddressToStringA(&msg->addr[i].ipv4, addrs + offset);
2010 }
2011 offset = strlen(addrs);
2012 }
2013
2014 err = SetNameServers(iid, msg->family, addrs);
2015 if (err)
2016 {
2017 return err;
2018 }
2019
2020 wchar_t *tmp_iid = _wcsdup(iid);
2021 if (!tmp_iid || AddListItem(&(*lists)[undo_type], tmp_iid))
2022 {
2023 free(tmp_iid);
2024 ResetNameServers(iid, msg->family);
2025 return ERROR_OUTOFMEMORY;
2026 }
2027 }
2028
2029 BOOL gpol = FALSE;
2030 if (msg->domains[0])
2031 {
2032 err = SetDnsSearchDomains(msg->iface.name, msg->domains, &gpol, lists);
2033 }
2034 ApplyDnsSettings(gpol);
2035
2036 return err;
2037}
2038
2047static BOOL
2049{
2050 DWORD dhcp;
2051 DWORD size = sizeof(dhcp);
2052 LSTATUS err;
2053
2054 err = RegGetValueA(key, NULL, "EnableDHCP", RRF_RT_REG_DWORD, NULL, (PBYTE)&dhcp, &size);
2055 if (err != NO_ERROR)
2056 {
2057 MsgToEventLog(M_SYSERR, L"%S: Could not read DHCP status (%lu)", __func__, err);
2058 return FALSE;
2059 }
2060
2061 return dhcp ? TRUE : FALSE;
2062}
2063
2072static LSTATUS
2073SetNameServerAddresses(PWSTR itf_id, const nrpt_address_t *addresses)
2074{
2075 const short families[] = { AF_INET, AF_INET6 };
2076 for (size_t i = 0; i < _countof(families); i++)
2077 {
2078 short family = families[i];
2079
2080 /* Create a comma sparated list of addresses of this family */
2081 size_t offset = 0;
2082 char addr_list[NRPT_ADDR_SIZE * NRPT_ADDR_NUM];
2083 for (int j = 0; j < NRPT_ADDR_NUM && addresses[j][0]; j++)
2084 {
2085 if ((family == AF_INET6 && strchr(addresses[j], ':') == NULL)
2086 || (family == AF_INET && strchr(addresses[j], ':') != NULL))
2087 {
2088 /* Address family doesn't match, skip this one */
2089 continue;
2090 }
2091 if (offset)
2092 {
2093 addr_list[offset++] = ',';
2094 }
2095 strcpy(addr_list + offset, addresses[j]);
2096 offset += strlen(addresses[j]);
2097 }
2098
2099 if (offset == 0)
2100 {
2101 /* No address for this family to set */
2102 continue;
2103 }
2104
2105 /* Set name server addresses */
2106 LSTATUS err = SetNameServers(itf_id, family, addr_list);
2107 if (err)
2108 {
2109 return err;
2110 }
2111 }
2112 return NO_ERROR;
2113}
2114
2125static LSTATUS
2126GetItfDnsServersV4(HKEY itf_key, PSTR addrs, PDWORD size)
2127{
2128 addrs[*size - 1] = '\0';
2129
2130 LSTATUS err;
2131 DWORD s = *size;
2132 err = RegGetValueA(itf_key, NULL, "NameServer", RRF_RT_REG_SZ, NULL, (PBYTE)addrs, &s);
2133 if (err && err != ERROR_FILE_NOT_FOUND)
2134 {
2135 *size = 0;
2136 return err;
2137 }
2138
2139 /* Try DHCP addresses if we don't have some already */
2140 if (!strchr(addrs, '.') && IsDhcpEnabled(itf_key))
2141 {
2142 s = *size;
2143 RegGetValueA(itf_key, NULL, "DhcpNameServer", RRF_RT_REG_SZ, NULL, (PBYTE)addrs, &s);
2144 if (err)
2145 {
2146 *size = 0;
2147 return err;
2148 }
2149 }
2150
2151 if (strchr(addrs, '.'))
2152 {
2153 *size = s;
2154 return NO_ERROR;
2155 }
2156
2157 *size = 0;
2158 return ERROR_FILE_NOT_FOUND;
2159}
2160
2170static LSTATUS
2171GetItfDnsServersV6(HKEY itf_key, PSTR addrs, PDWORD size)
2172{
2173 addrs[*size - 1] = '\0';
2174
2175 LSTATUS err;
2176 DWORD s = *size;
2177 err = RegGetValueA(itf_key, NULL, "NameServer", RRF_RT_REG_SZ, NULL, (PBYTE)addrs, &s);
2178 if (err && err != ERROR_FILE_NOT_FOUND)
2179 {
2180 *size = 0;
2181 return err;
2182 }
2183
2184 /* Try DHCP addresses if we don't have some already */
2185 if (!strchr(addrs, ':') && IsDhcpEnabled(itf_key))
2186 {
2187 IN6_ADDR in_addrs[8];
2188 DWORD in_addrs_size = sizeof(in_addrs);
2189 err = RegGetValueA(itf_key, NULL, "Dhcpv6DNSServers", RRF_RT_REG_BINARY, NULL,
2190 (PBYTE)in_addrs, &in_addrs_size);
2191 if (err)
2192 {
2193 *size = 0;
2194 return err;
2195 }
2196
2197 s = *size;
2198 PSTR pos = addrs;
2199 size_t in_addrs_read = in_addrs_size / sizeof(IN6_ADDR);
2200 for (size_t i = 0; i < in_addrs_read; ++i)
2201 {
2202 if (i != 0)
2203 {
2204 /* Add separator */
2205 *pos++ = ',';
2206 s--;
2207 }
2208
2209 if (inet_ntop(AF_INET6, &in_addrs[i], pos, s) != NULL)
2210 {
2211 *size = 0;
2212 return ERROR_MORE_DATA;
2213 }
2214
2215 size_t addr_len = strlen(pos);
2216 pos += addr_len;
2217 s -= (DWORD)addr_len;
2218 }
2219 s = (DWORD)strlen(addrs) + 1;
2220 }
2221
2222 if (strchr(addrs, ':'))
2223 {
2224 *size = s;
2225 return NO_ERROR;
2226 }
2227
2228 *size = 0;
2229 return ERROR_FILE_NOT_FOUND;
2230}
2231
2241static BOOL
2242ListContainsDomain(PCWSTR list, PCWSTR domain, size_t len)
2243{
2244 PCWSTR entry = list;
2245 while (entry && *entry)
2246 {
2247 PCWSTR comma = wcschr(entry, L',');
2248 size_t entry_len = comma ? (size_t)(comma - entry) : wcslen(entry);
2249 if (entry_len == len && wcsncmp(entry, domain, len) == 0)
2250 {
2251 return TRUE;
2252 }
2253 if (!comma)
2254 {
2255 break;
2256 }
2257 entry = comma + 1;
2258 }
2259 return FALSE;
2260}
2261
2284static LSTATUS
2285ConvertItfDnsDomains(PCWSTR search_domains, PWSTR domains, PDWORD size, const DWORD capacity)
2286{
2287 const size_t glyph_size = sizeof(*domains);
2288 const size_t max_len = (size_t)capacity / glyph_size;
2289
2290 /* Space required for leading dot and two terminating zeros */
2291 const size_t dot_len = 1;
2292 const size_t term_len = 2;
2293
2294 LSTATUS ret = NO_ERROR;
2295 size_t tmp_len = 0;
2296 WCHAR *tmp = malloc(capacity);
2297 if (tmp == NULL)
2298 {
2299 ret = ERROR_OUTOFMEMORY;
2300 goto done;
2301 }
2302
2303 PWCHAR tmp_pos = tmp;
2304 PCWCHAR domain = domains;
2305
2306 while (domain && *domain)
2307 {
2308 PWCHAR comma = wcschr(domain, L',');
2309 size_t domain_len = comma ? (size_t)(comma - domain) : wcslen(domain);
2310
2311 if (ListContainsDomain(search_domains, domain, domain_len))
2312 {
2313 /* Skip this domain */
2314 domain = comma ? comma + 1 : domain + domain_len;
2315 continue;
2316 }
2317
2318 /* Check for enough space to convert this domain */
2319 if (tmp_len + dot_len + domain_len + term_len > max_len)
2320 {
2321 /* Domain doesn't fit, bad luck if it's the first one */
2322 *tmp_pos = L'\0';
2323 if (tmp_len > 0)
2324 {
2325 tmp_len += 1;
2326 }
2327 ret = ERROR_MORE_DATA;
2328 goto done;
2329 }
2330
2331 /* Write leading dot and domain into tmp buffer */
2332 *tmp_pos++ = L'.';
2333 wcsncpy(tmp_pos, domain, domain_len);
2334 tmp_pos += domain_len;
2335 *tmp_pos++ = L'\0';
2336 tmp_len += dot_len + domain_len + 1;
2337
2338 domain = comma ? comma + 1 : domain + domain_len;
2339 }
2340
2341 if (tmp_len == 0)
2342 {
2343 ret = ERROR_FILE_NOT_FOUND;
2344 goto done;
2345 }
2346
2347 /* REG_MULTI_SZ second zero terminator */
2348 *tmp_pos = L'\0';
2349 tmp_len += 1;
2350
2351done:
2352 if (tmp)
2353 {
2354 wmemcpy(domains, tmp, tmp_len);
2355 free(tmp);
2356 }
2357 *size = (DWORD)(tmp_len * glyph_size);
2358 return ret;
2359}
2360
2382static LSTATUS
2383GetItfDnsDomains(HKEY itf, PCWSTR search_domains, PWSTR domains, PDWORD size)
2384{
2385 if (domains == NULL || size == NULL || *size == 0)
2386 {
2387 return ERROR_INVALID_PARAMETER;
2388 }
2389
2390 LSTATUS err = ERROR_FILE_NOT_FOUND;
2391 const DWORD buf_size = *size;
2392 const DWORD glyph_size = sizeof(*domains);
2393 PWSTR values[] = { L"SearchList", L"Domain", L"DhcpDomainSearchList", L"DhcpDomain", NULL };
2394
2395 for (int i = 0; values[i]; i++)
2396 {
2397 *size = buf_size;
2398 err = RegGetValueW(itf, NULL, values[i], RRF_RT_REG_SZ, NULL, (PBYTE)domains, size);
2399 if (!err && *size > glyph_size && domains[(*size / glyph_size) - 1] == '\0' && wcschr(domains, '.'))
2400 {
2401 return ConvertItfDnsDomains(search_domains, domains, size, buf_size);
2402 }
2403 }
2404
2405 *size = 0;
2406 return err;
2407}
2408
2417static BOOL
2419{
2420 GUID iid;
2421 BOOL res = FALSE;
2422 MIB_IF_ROW2 itf_row;
2423
2424 /* Get GUID from string */
2425 if (IIDFromString(iid_str, &iid) != S_OK)
2426 {
2427 MsgToEventLog(M_SYSERR, L"%S: could not convert interface %s GUID string", __func__,
2428 iid_str);
2429 goto out;
2430 }
2431
2432 /* Get LUID from GUID */
2433 if (ConvertInterfaceGuidToLuid(&iid, &itf_row.InterfaceLuid) != NO_ERROR)
2434 {
2435 goto out;
2436 }
2437
2438 /* Look up interface status */
2439 if (GetIfEntry2(&itf_row) != NO_ERROR)
2440 {
2441 MsgToEventLog(M_SYSERR, L"%S: could not get interface %s status", __func__, iid_str);
2442 goto out;
2443 }
2444
2445 if (itf_row.MediaConnectState == MediaConnectStateConnected
2446 && itf_row.OperStatus == IfOperStatusUp)
2447 {
2448 res = TRUE;
2449 }
2450
2451out:
2452 return res;
2453}
2454
2464static void
2465GetNrptExcludeData(PCWSTR search_domains, nrpt_exclude_data_t *data, size_t data_size)
2466{
2467 HKEY v4_itfs = INVALID_HANDLE_VALUE;
2468 HKEY v6_itfs = INVALID_HANDLE_VALUE;
2469
2470 if (!GetInterfacesKey(AF_INET, &v4_itfs) || !GetInterfacesKey(AF_INET6, &v6_itfs))
2471 {
2472 goto out;
2473 }
2474
2475 size_t i = 0;
2476 DWORD enum_index = 0;
2477 while (i < data_size)
2478 {
2479 WCHAR itf_guid[MAX_PATH];
2480 DWORD itf_guid_len = _countof(itf_guid);
2481 LSTATUS err =
2482 RegEnumKeyExW(v4_itfs, enum_index++, itf_guid, &itf_guid_len, NULL, NULL, NULL, NULL);
2483 if (err)
2484 {
2485 if (err != ERROR_NO_MORE_ITEMS)
2486 {
2487 MsgToEventLog(M_SYSERR, L"%S: could not enumerate interfaces (%lu)", __func__, err);
2488 }
2489 goto out;
2490 }
2491
2492 /* Ignore interfaces that are not connected or disabled */
2493 if (!IsInterfaceConnected(itf_guid))
2494 {
2495 continue;
2496 }
2497
2498 HKEY v4_itf;
2499 if (RegOpenKeyExW(v4_itfs, itf_guid, 0, KEY_READ, &v4_itf) != NO_ERROR)
2500 {
2501 MsgToEventLog(M_SYSERR, L"%S: could not open interface %s v4 registry key", __func__,
2502 itf_guid);
2503 goto out;
2504 }
2505
2506 /* Get the DNS domain(s) for exclude routing */
2507 data[i].domains_size = sizeof(data[0].domains);
2508 memset(data[i].domains, 0, data[i].domains_size);
2509 err = GetItfDnsDomains(v4_itf, search_domains, data[i].domains, &data[i].domains_size);
2510 if (err)
2511 {
2512 if (err != ERROR_FILE_NOT_FOUND)
2513 {
2514 MsgToEventLog(M_SYSERR, L"%S: could not read interface %s domain suffix", __func__,
2515 itf_guid);
2516 }
2517 goto next_itf;
2518 }
2519
2520 /* Get the IPv4 DNS servers */
2521 DWORD v4_addrs_size = sizeof(data[0].addresses);
2522 err = GetItfDnsServersV4(v4_itf, data[i].addresses, &v4_addrs_size);
2523 if (err && err != ERROR_FILE_NOT_FOUND)
2524 {
2525 MsgToEventLog(M_SYSERR, L"%S: could not read interface %s v4 name servers (%ld)",
2526 __func__, itf_guid, err);
2527 goto next_itf;
2528 }
2529
2530 /* Get the IPv6 DNS servers, if there's space left */
2531 PSTR v6_addrs = data[i].addresses + v4_addrs_size;
2532 DWORD v6_addrs_size = sizeof(data[0].addresses) - v4_addrs_size;
2533 if (v6_addrs_size > NRPT_ADDR_SIZE)
2534 {
2535 HKEY v6_itf;
2536 if (RegOpenKeyExW(v6_itfs, itf_guid, 0, KEY_READ, &v6_itf) != NO_ERROR)
2537 {
2538 MsgToEventLog(M_SYSERR, L"%S: could not open interface %s v6 registry key",
2539 __func__, itf_guid);
2540 goto next_itf;
2541 }
2542 err = GetItfDnsServersV6(v6_itf, v6_addrs, &v6_addrs_size);
2543 RegCloseKey(v6_itf);
2544 if (err && err != ERROR_FILE_NOT_FOUND)
2545 {
2546 MsgToEventLog(M_SYSERR, L"%S: could not read interface %s v6 name servers (%ld)",
2547 __func__, itf_guid, err);
2548 goto next_itf;
2549 }
2550 }
2551
2552 if (v4_addrs_size || v6_addrs_size)
2553 {
2554 /* Replace delimiters with semicolons, as required by NRPT */
2555 for (size_t j = 0; j < sizeof(data[0].addresses) && data[i].addresses[j]; j++)
2556 {
2557 if (data[i].addresses[j] == ',' || data[i].addresses[j] == ' ')
2558 {
2559 data[i].addresses[j] = ';';
2560 }
2561 }
2562 ++i;
2563 }
2564
2565next_itf:
2566 RegCloseKey(v4_itf);
2567 }
2568
2569out:
2570 RegCloseKey(v6_itfs);
2571 RegCloseKey(v4_itfs);
2572}
2573
2586static DWORD
2587SetNrptRule(HKEY nrpt_key, PCWSTR subkey, PCSTR address, PCWSTR domains, DWORD dom_size,
2588 BOOL dnssec)
2589{
2590 /* Create rule subkey */
2591 DWORD err = NO_ERROR;
2592 HKEY rule_key;
2593 err = RegCreateKeyExW(nrpt_key, subkey, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &rule_key, NULL);
2594 if (err)
2595 {
2596 return err;
2597 }
2598
2599 /* Set name(s) for DNS routing */
2600 err = RegSetValueExW(rule_key, L"Name", 0, REG_MULTI_SZ, (PBYTE)domains, dom_size);
2601 if (err)
2602 {
2603 goto out;
2604 }
2605
2606 /* Set DNS Server address */
2607 err = RegSetValueExA(rule_key, "GenericDNSServers", 0, REG_SZ, (PBYTE)address,
2608 (DWORD)strlen(address) + 1);
2609 if (err)
2610 {
2611 goto out;
2612 }
2613
2614 DWORD reg_val;
2615 /* Set DNSSEC if required */
2616 if (dnssec)
2617 {
2618 reg_val = 1;
2619 err = RegSetValueExA(rule_key, "DNSSECValidationRequired", 0, REG_DWORD, (PBYTE)&reg_val,
2620 sizeof(reg_val));
2621 if (err)
2622 {
2623 goto out;
2624 }
2625
2626 reg_val = 0;
2627 err = RegSetValueExA(rule_key, "DNSSECQueryIPSECRequired", 0, REG_DWORD, (PBYTE)&reg_val,
2628 sizeof(reg_val));
2629 if (err)
2630 {
2631 goto out;
2632 }
2633
2634 reg_val = 0;
2635 err = RegSetValueExA(rule_key, "DNSSECQueryIPSECEncryption", 0, REG_DWORD, (PBYTE)&reg_val,
2636 sizeof(reg_val));
2637 if (err)
2638 {
2639 goto out;
2640 }
2641 }
2642
2643 /* Set NRPT config options */
2644 reg_val = dnssec ? 0x0000000A : 0x00000008;
2645 err = RegSetValueExA(rule_key, "ConfigOptions", 0, REG_DWORD, (const PBYTE)&reg_val,
2646 sizeof(reg_val));
2647 if (err)
2648 {
2649 goto out;
2650 }
2651
2652 /* Mandatory NRPT version */
2653 reg_val = 2;
2654 err = RegSetValueExA(rule_key, "Version", 0, REG_DWORD, (const PBYTE)&reg_val, sizeof(reg_val));
2655 if (err)
2656 {
2657 goto out;
2658 }
2659
2660out:
2661 if (err)
2662 {
2663 RegDeleteKeyW(nrpt_key, subkey);
2664 }
2665 RegCloseKey(rule_key);
2666 return err;
2667}
2668
2678static void
2679SetNrptExcludeRules(HKEY nrpt_key, DWORD ovpn_pid, PCWSTR search_domains)
2680{
2681 nrpt_exclude_data_t data[8]; /* data from up to 8 interfaces */
2682 memset(data, 0, sizeof(data));
2683 GetNrptExcludeData(search_domains, data, _countof(data));
2684
2685 unsigned n = 0;
2686 for (size_t i = 0; i < _countof(data); ++i)
2687 {
2688 nrpt_exclude_data_t *d = &data[i];
2689 if (d->domains_size == 0)
2690 {
2691 break;
2692 }
2693
2694 DWORD err;
2695 WCHAR subkey[48];
2696 swprintf(subkey, _countof(subkey), L"OpenVPNDNSRoutingX-%02x-%lu", ++n, ovpn_pid);
2697 err = SetNrptRule(nrpt_key, subkey, d->addresses, d->domains, d->domains_size, FALSE);
2698 if (err)
2699 {
2700 MsgToEventLog(M_ERR, L"%S: failed to set rule %s (%lu)", __func__, subkey, err);
2701 }
2702 }
2703}
2704
2717static DWORD
2718SetNrptRules(HKEY nrpt_key, const nrpt_address_t *addresses, const char *domains,
2719 const char *search_domains, BOOL dnssec, DWORD ovpn_pid)
2720{
2721 DWORD err = NO_ERROR;
2722 PWSTR wide_domains = L".\0"; /* DNS route everything by default */
2723 DWORD dom_size = 6;
2724
2725 /* Prepare DNS routing domains / split DNS */
2726 if (domains[0])
2727 {
2728 size_t domains_len = strlen(domains);
2729 dom_size = (DWORD)domains_len + 2; /* len + the trailing NULs */
2730
2731 wide_domains = utf8to16_size(domains, dom_size);
2732 dom_size *= sizeof(*wide_domains);
2733 if (!wide_domains)
2734 {
2735 return ERROR_OUTOFMEMORY;
2736 }
2737 /* Make a MULTI_SZ from a comma separated list */
2738 for (size_t i = 0; i < domains_len; ++i)
2739 {
2740 if (wide_domains[i] == ',')
2741 {
2742 wide_domains[i] = 0;
2743 }
2744 }
2745 }
2746 else
2747 {
2748 PWSTR wide_search_domains;
2749 wide_search_domains = utf8to16(search_domains);
2750 if (!wide_search_domains)
2751 {
2752 return ERROR_OUTOFMEMORY;
2753 }
2754 SetNrptExcludeRules(nrpt_key, ovpn_pid, wide_search_domains);
2755 free(wide_search_domains);
2756 }
2757
2758 if (addresses[0][0])
2759 {
2760 /* Create address string list */
2761 CHAR addr_list[NRPT_ADDR_NUM * NRPT_ADDR_SIZE];
2762 PSTR pos = addr_list;
2763 for (int i = 0; i < NRPT_ADDR_NUM && addresses[i][0]; ++i)
2764 {
2765 if (i != 0)
2766 {
2767 *pos++ = ';';
2768 }
2769 strcpy(pos, addresses[i]);
2770 pos += strlen(pos);
2771 }
2772
2773 WCHAR subkey[MAX_PATH];
2774 swprintf(subkey, _countof(subkey), L"OpenVPNDNSRouting-%lu", ovpn_pid);
2775 err = SetNrptRule(nrpt_key, subkey, addr_list, wide_domains, dom_size, dnssec);
2776 if (err)
2777 {
2778 MsgToEventLog(M_ERR, L"%S: failed to set rule %s (%lu)", __func__, subkey, err);
2779 }
2780 }
2781
2782 if (domains[0])
2783 {
2784 free(wide_domains);
2785 }
2786 return err;
2787}
2788
2797static LSTATUS
2798OpenNrptBaseKey(PHKEY key, PBOOL gpol)
2799{
2800 /*
2801 * Registry keys Name Service Policy Table (NRPT) rules can be stored at.
2802 * When the group policy key exists, NRPT rules must be placed there.
2803 * It is created when NRPT rules are pushed via group policy and it
2804 * remains in the registry even if the last GP-NRPT rule is deleted.
2805 */
2806 static PCSTR gpol_key = "SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient\\DnsPolicyConfig";
2807 static PCSTR sys_key =
2808 "SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters\\DnsPolicyConfig";
2809
2810 HKEY nrpt;
2811 *gpol = TRUE;
2812 LSTATUS err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, gpol_key, 0, KEY_ALL_ACCESS, &nrpt);
2813 if (err == ERROR_FILE_NOT_FOUND)
2814 {
2815 *gpol = FALSE;
2816 err = RegCreateKeyExA(HKEY_LOCAL_MACHINE, sys_key, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &nrpt,
2817 NULL);
2818 if (err)
2819 {
2820 nrpt = INVALID_HANDLE_VALUE;
2821 }
2822 }
2823 *key = nrpt;
2824 return err;
2825}
2826
2838static BOOL
2839DeleteNrptRules(DWORD pid, PBOOL gpol)
2840{
2841 HKEY key;
2842 LSTATUS err = OpenNrptBaseKey(&key, gpol);
2843 if (err)
2844 {
2845 MsgToEventLog(M_SYSERR, L"%S: could not open NRPT base key (%lu)", __func__, err);
2846 return FALSE;
2847 }
2848
2849 /* PID suffix string to compare against later */
2850 WCHAR pid_str[16];
2851 size_t pidlen = 0;
2852 if (pid)
2853 {
2854 swprintf(pid_str, _countof(pid_str), L"-%lu", pid);
2855 pidlen = wcslen(pid_str);
2856 }
2857
2858 int deleted = 0;
2859 DWORD enum_index = 0;
2860 while (TRUE)
2861 {
2862 WCHAR name[MAX_PATH];
2863 DWORD namelen = _countof(name);
2864 err = RegEnumKeyExW(key, enum_index++, name, &namelen, NULL, NULL, NULL, NULL);
2865 if (err)
2866 {
2867 if (err != ERROR_NO_MORE_ITEMS)
2868 {
2869 MsgToEventLog(M_SYSERR, L"%S: could not enumerate NRPT rules (%lu)", __func__, err);
2870 }
2871 break;
2872 }
2873
2874 /* Keep rule if name doesn't match */
2875 if (wcsncmp(name, L"OpenVPNDNSRouting", 17) != 0
2876 || (pid && wcsncmp(name + namelen - pidlen, pid_str, pidlen) != 0))
2877 {
2878 continue;
2879 }
2880
2881 if (RegDeleteKeyW(key, name) == NO_ERROR)
2882 {
2883 enum_index--;
2884 deleted++;
2885 }
2886 }
2887
2888 RegCloseKey(key);
2889 return deleted ? TRUE : FALSE;
2890}
2891
2897static void
2898UndoNrptRules(DWORD ovpn_pid)
2899{
2900 BOOL gpol;
2901 if (DeleteNrptRules(ovpn_pid, &gpol))
2902 {
2903 ApplyDnsSettings(gpol);
2904 }
2905}
2906
2918static DWORD
2920{
2921 /*
2922 * Use a non-const reference with limited scope to
2923 * enforce null-termination of strings from client
2924 */
2925 {
2927 msgptr->iface.name[_countof(msg->iface.name) - 1] = '\0';
2928 msgptr->search_domains[_countof(msg->search_domains) - 1] = '\0';
2929 msgptr->resolve_domains[_countof(msg->resolve_domains) - 1] = '\0';
2930 for (size_t i = 0; i < NRPT_ADDR_NUM; ++i)
2931 {
2932 msgptr->addresses[i][_countof(msg->addresses[0]) - 1] = '\0';
2933 }
2934 }
2935
2936 /* Make sure we have the VPN interface name */
2937 if (msg->iface.name[0] == 0)
2938 {
2939 return ERROR_MESSAGE_DATA;
2940 }
2941
2942 /* Some sanity checks on the add message data */
2943 if (msg->header.type == msg_add_nrpt_cfg)
2944 {
2945 /* At least one name server address is set */
2946 if (msg->addresses[0][0] == 0)
2947 {
2948 return ERROR_MESSAGE_DATA;
2949 }
2950 /* Resolve domains are double zero terminated (MULTI_SZ) */
2951 const char *rdom = msg->resolve_domains;
2952 size_t rdom_size = sizeof(msg->resolve_domains);
2953 size_t rdom_len = strlen(rdom);
2954 if (rdom_len && (rdom_len + 1 >= rdom_size || rdom[rdom_len + 2] != 0))
2955 {
2956 return ERROR_MESSAGE_DATA;
2957 }
2958 }
2959
2960 BOOL gpol_nrpt = FALSE;
2961 BOOL gpol_list = FALSE;
2962
2963 WCHAR iid[64];
2964 DWORD iid_err = InterfaceIdString(msg->iface.name, iid, _countof(iid));
2965 if (iid_err)
2966 {
2967 return iid_err;
2968 }
2969
2970 /* Delete previously set values for this instance first, if any */
2971 PDWORD undo_pid = RemoveListItem(&(*lists)[undo_nrpt], CmpAny, NULL);
2972 if (undo_pid)
2973 {
2974 if (*undo_pid != ovpn_pid)
2975 {
2977 L"%S: PID stored for undo doesn't match: %lu vs %lu. "
2978 "This is likely an error. Cleaning up anyway.",
2979 __func__, *undo_pid, ovpn_pid);
2980 }
2981 DeleteNrptRules(*undo_pid, &gpol_nrpt);
2982 free(undo_pid);
2983
2984 ResetNameServers(iid, AF_INET);
2985 ResetNameServers(iid, AF_INET6);
2986 }
2987 SetDnsSearchDomains(msg->iface.name, NULL, &gpol_list, lists);
2988
2989 if (msg->header.type == msg_del_nrpt_cfg)
2990 {
2991 ApplyDnsSettings(gpol_nrpt || gpol_list);
2992 return NO_ERROR; /* Done dealing with del message */
2993 }
2994
2995 HKEY key;
2996 LSTATUS err = OpenNrptBaseKey(&key, &gpol_nrpt);
2997 if (err)
2998 {
2999 goto out;
3000 }
3001
3002 /* Add undo information first in case there's no heap left */
3003 PDWORD pid = malloc(sizeof(ovpn_pid));
3004 if (!pid)
3005 {
3006 err = ERROR_OUTOFMEMORY;
3007 goto out;
3008 }
3009 *pid = ovpn_pid;
3010 if (AddListItem(&(*lists)[undo_nrpt], pid))
3011 {
3012 err = ERROR_OUTOFMEMORY;
3013 free(pid);
3014 goto out;
3015 }
3016
3017 /* Set NRPT rules */
3018 BOOL dnssec = (msg->flags & nrpt_dnssec) != 0;
3019 err = SetNrptRules(key, msg->addresses, msg->resolve_domains, msg->search_domains, dnssec,
3020 ovpn_pid);
3021 if (err)
3022 {
3023 goto out;
3024 }
3025
3026 /*
3027 * Set DNS on the adapter for search domains to be considered.
3028 * If split DNS is configured, do this only when search domains
3029 * are given, so that look-ups for other domains do not go over
3030 * the VPN all the time.
3031 */
3032 if (msg->search_domains[0] || !msg->resolve_domains[0])
3033 {
3034 err = SetNameServerAddresses(iid, msg->addresses);
3035 if (err)
3036 {
3037 goto out;
3038 }
3039 }
3040
3041 /* Set search domains, if any */
3042 if (msg->search_domains[0])
3043 {
3044 err = SetDnsSearchDomains(msg->iface.name, msg->search_domains, &gpol_list, lists);
3045 }
3046
3047 ApplyDnsSettings(gpol_nrpt || gpol_list);
3048
3049out:
3050 return err;
3051}
3052
3053static DWORD
3055{
3056 DWORD err = NO_ERROR;
3057 wchar_t addr[16]; /* large enough to hold string representation of an ipv4 */
3058 unsigned int addr_len = msg->addr_len;
3059
3060 /* sanity check */
3061 if (addr_len > _countof(msg->addr))
3062 {
3063 addr_len = _countof(msg->addr);
3064 }
3065
3066 if (!msg->iface.index) /* interface index is required */
3067 {
3068 return ERROR_MESSAGE_DATA;
3069 }
3070
3071 /* We delete all current addresses before adding any
3072 * OR if the message type is del_wins_cfg
3073 */
3074 if (addr_len > 0 || msg->header.type == msg_del_wins_cfg)
3075 {
3076 err = netsh_wins_cmd(L"delete", msg->iface.index, NULL);
3077 if (err)
3078 {
3079 goto out;
3080 }
3081 free(RemoveListItem(&(*lists)[undo_wins], CmpAny, NULL));
3082 }
3083
3084 if (addr_len == 0 || msg->header.type == msg_del_wins_cfg)
3085 {
3086 goto out; /* job done */
3087 }
3088
3089 for (unsigned int i = 0; i < addr_len; ++i)
3090 {
3091 RtlIpv4AddressToStringW(&msg->addr[i].ipv4, addr);
3092 err = netsh_wins_cmd(i == 0 ? L"set" : L"add", msg->iface.index, addr);
3093 if (i == 0 && err)
3094 {
3095 goto out;
3096 }
3097 /* We do not check for duplicate addresses, so any error in adding
3098 * additional addresses is ignored.
3099 */
3100 }
3101
3102 PDWORD if_index = malloc(sizeof(msg->iface.index));
3103 if (if_index)
3104 {
3105 *if_index = msg->iface.index;
3106 }
3107
3108 if (!if_index || AddListItem(&(*lists)[undo_wins], if_index))
3109 {
3110 free(if_index);
3111 netsh_wins_cmd(L"delete", msg->iface.index, NULL);
3112 err = ERROR_OUTOFMEMORY;
3113 goto out;
3114 }
3115
3116 err = 0;
3117
3118out:
3119 return err;
3120}
3121
3122static DWORD
3124{
3125 DWORD err = 0;
3126 DWORD timeout = 5000; /* in milli seconds */
3127 wchar_t argv0[MAX_PATH];
3128
3129 /* Path of netsh */
3130 swprintf(argv0, _countof(argv0), L"%ls\\%ls", get_win_sys_path(), L"netsh.exe");
3131
3132 /* cmd template:
3133 * netsh interface ipv4 set address name=$if_index source=dhcp
3134 */
3135 const wchar_t *fmt = L"netsh interface ipv4 set address name=\"%lu\" source=dhcp";
3136
3137 /* max cmdline length in wchars -- include room for if index:
3138 * 10 chars for 32 bit int in decimal and +1 for NUL
3139 */
3140 size_t ncmdline = wcslen(fmt) + 10 + 1;
3141 wchar_t *cmdline = malloc(ncmdline * sizeof(wchar_t));
3142 if (!cmdline)
3143 {
3144 err = ERROR_OUTOFMEMORY;
3145 return err;
3146 }
3147
3148 swprintf(cmdline, ncmdline, fmt, dhcp->iface.index);
3149
3150 err = ExecCommand(argv0, cmdline, timeout);
3151
3152 /* Note: This could fail if dhcp is already enabled, so the caller
3153 * may not want to treat errors as FATAL.
3154 */
3155
3156 free(cmdline);
3157 return err;
3158}
3159
3160static DWORD
3162{
3163 DWORD err = 0;
3164 MIB_IPINTERFACE_ROW ipiface;
3165 InitializeIpInterfaceEntry(&ipiface);
3166 ipiface.Family = mtu->family;
3167 ipiface.InterfaceIndex = mtu->iface.index;
3168 err = GetIpInterfaceEntry(&ipiface);
3169 if (err != NO_ERROR)
3170 {
3171 return err;
3172 }
3173 if (mtu->family == AF_INET)
3174 {
3175 ipiface.SitePrefixLength = 0;
3176 }
3177 ipiface.NlMtu = mtu->mtu;
3178
3179 err = SetIpInterfaceEntry(&ipiface);
3180 return err;
3181}
3182
3190static DWORD
3192{
3193 const WCHAR *hwid;
3194
3195 switch (msg->adapter_type)
3196 {
3197 case ADAPTER_TYPE_DCO:
3198 hwid = L"ovpn-dco";
3199 break;
3200
3201 case ADAPTER_TYPE_TAP:
3202 hwid = L"root\\tap0901";
3203 break;
3204
3205 default:
3206 return ERROR_INVALID_PARAMETER;
3207 }
3208
3209 WCHAR cmd[MAX_PATH];
3210 WCHAR args[MAX_PATH];
3211
3212 if (swprintf_s(cmd, _countof(cmd), L"%s\\tapctl.exe", settings.bin_dir) < 0)
3213 {
3214 return ERROR_BUFFER_OVERFLOW;
3215 }
3216
3217 if (swprintf_s(args, _countof(args), L"tapctl create --hwid %s", hwid) < 0)
3218 {
3219 return ERROR_BUFFER_OVERFLOW;
3220 }
3221
3222 return ExecCommand(cmd, args, 10000);
3223}
3224
3225static VOID
3226HandleMessage(HANDLE pipe, PPROCESS_INFORMATION proc_info, DWORD bytes, DWORD count,
3227 LPHANDLE events, undo_lists_t *lists)
3228{
3230 ack_message_t ack = {
3231 .header = { .type = msg_acknowledgement, .size = sizeof(ack), .message_id = -1 },
3232 .error_number = ERROR_MESSAGE_DATA
3233 };
3234
3235 DWORD read = ReadPipeAsync(pipe, &msg, bytes, count, events);
3236 if (read != bytes || read < sizeof(msg.header) || read != msg.header.size)
3237 {
3238 goto out;
3239 }
3240
3241 ack.header.message_id = msg.header.message_id;
3242
3243 switch (msg.header.type)
3244 {
3245 case msg_add_address:
3246 case msg_del_address:
3247 if (msg.header.size == sizeof(msg.address))
3248 {
3249 ack.error_number = HandleAddressMessage(&msg.address, lists);
3250 }
3251 break;
3252
3253 case msg_add_route:
3254 case msg_del_route:
3255 if (msg.header.size == sizeof(msg.route))
3256 {
3257 ack.error_number = HandleRouteMessage(&msg.route, lists);
3258 }
3259 break;
3260
3262 if (msg.header.size == sizeof(msg.flush_neighbors))
3263 {
3264 ack.error_number = HandleFlushNeighborsMessage(&msg.flush_neighbors);
3265 }
3266 break;
3267
3268 case msg_add_wfp_block:
3269 case msg_del_wfp_block:
3270 if (msg.header.size == sizeof(msg.wfp_block))
3271 {
3272 ack.error_number = HandleWfpBlockMessage(&msg.wfp_block, lists);
3273 }
3274 break;
3275
3276 case msg_register_dns:
3278 break;
3279
3280 case msg_add_dns_cfg:
3281 case msg_del_dns_cfg:
3282 ack.error_number = HandleDNSConfigMessage(&msg.dns, lists);
3283 break;
3284
3285 case msg_add_nrpt_cfg:
3286 case msg_del_nrpt_cfg:
3287 {
3288 DWORD ovpn_pid = proc_info->dwProcessId;
3289 ack.error_number = HandleDNSConfigNrptMessage(&msg.nrpt_dns, ovpn_pid, lists);
3290 }
3291 break;
3292
3293 case msg_add_wins_cfg:
3294 case msg_del_wins_cfg:
3295 ack.error_number = HandleWINSConfigMessage(&msg.wins, lists);
3296 break;
3297
3298 case msg_enable_dhcp:
3299 if (msg.header.size == sizeof(msg.dhcp))
3300 {
3302 }
3303 break;
3304
3305 case msg_set_mtu:
3306 if (msg.header.size == sizeof(msg.mtu))
3307 {
3308 ack.error_number = HandleMTUMessage(&msg.mtu);
3309 }
3310 break;
3311
3312 case msg_create_adapter:
3313 if (msg.header.size == sizeof(msg.create_adapter))
3314 {
3315 ack.error_number = HandleCreateAdapterMessage(&msg.create_adapter);
3316 }
3317 break;
3318
3319 default:
3321 MsgToEventLog(MSG_FLAGS_ERROR, L"Unknown message type %d", msg.header.type);
3322 break;
3323 }
3324
3325out:
3326 WritePipeAsync(pipe, &ack, sizeof(ack), count, events);
3327}
3328
3329
3330static VOID
3332{
3333 undo_type_t type;
3334 wfp_block_data_t *interface_data;
3335 for (type = 0; type < _undo_type_max; type++)
3336 {
3337 list_item_t **pnext = &(*lists)[type];
3338 while (*pnext)
3339 {
3340 list_item_t *item = *pnext;
3341 switch (type)
3342 {
3343 case address:
3344 DeleteAddress(item->data);
3345 break;
3346
3347 case route:
3348 DeleteRoute(item->data);
3349 break;
3350
3351 case undo_dns4:
3352 ResetNameServers(item->data, AF_INET);
3353 break;
3354
3355 case undo_dns6:
3356 ResetNameServers(item->data, AF_INET6);
3357 break;
3358
3359 case undo_nrpt:
3360 UndoNrptRules(*(PDWORD)item->data);
3361 break;
3362
3363 case undo_domains:
3365 break;
3366
3367 case undo_wins:
3368 netsh_wins_cmd(L"delete", *(PDWORD)item->data, NULL);
3369 break;
3370
3371 case wfp_block:
3372 interface_data = (wfp_block_data_t *)(item->data);
3373 delete_wfp_block_filters(interface_data->engine);
3374 if (interface_data->metric_v4 >= 0)
3375 {
3376 set_interface_metric(interface_data->index, AF_INET,
3377 interface_data->metric_v4);
3378 }
3379 if (interface_data->metric_v6 >= 0)
3380 {
3381 set_interface_metric(interface_data->index, AF_INET6,
3382 interface_data->metric_v6);
3383 }
3384 break;
3385
3386 case _undo_type_max:
3387 /* unreachable */
3388 break;
3389 }
3390
3391 /* Remove from the list and free memory */
3392 *pnext = item->next;
3393 free(item->data);
3394 free(item);
3395 }
3396 }
3397}
3398
3399static DWORD WINAPI
3400RunOpenvpn(LPVOID p)
3401{
3402 HANDLE pipe = p;
3403 HANDLE ovpn_pipe = NULL, svc_pipe = NULL;
3404 PTOKEN_USER svc_user = NULL, ovpn_user = NULL;
3405 HANDLE svc_token = NULL, imp_token = NULL, pri_token = NULL;
3406 HANDLE stdin_read = NULL, stdin_write = NULL;
3407 HANDLE stdout_write = NULL;
3408 DWORD pipe_mode, len, exit_code = 0;
3409 STARTUP_DATA sud = { 0, 0, 0 };
3410 STARTUPINFOW startup_info;
3411 PROCESS_INFORMATION proc_info;
3412 LPVOID user_env = NULL;
3413 WCHAR ovpn_pipe_name[256]; /* The entire pipe name string can be up to 256 characters long
3414 according to MSDN. */
3415 LPCWSTR exe_path;
3416 WCHAR *cmdline = NULL;
3417 size_t cmdline_size;
3418 undo_lists_t undo_lists;
3419 WCHAR errmsg[512] = L"";
3420 BOOL flush_pipe = TRUE;
3421
3422 SECURITY_ATTRIBUTES inheritable = { .nLength = sizeof(inheritable),
3423 .lpSecurityDescriptor = NULL,
3424 .bInheritHandle = TRUE };
3425
3426 PACL ovpn_dacl;
3427 EXPLICIT_ACCESS ea[2];
3428 SECURITY_DESCRIPTOR ovpn_sd;
3429 SECURITY_ATTRIBUTES ovpn_sa = { .nLength = sizeof(ovpn_sa),
3430 .lpSecurityDescriptor = &ovpn_sd,
3431 .bInheritHandle = FALSE };
3432
3433 ZeroMemory(&ea, sizeof(ea));
3434 ZeroMemory(&startup_info, sizeof(startup_info));
3435 ZeroMemory(&undo_lists, sizeof(undo_lists));
3436 ZeroMemory(&proc_info, sizeof(proc_info));
3437
3438 if (!GetStartupData(pipe, &sud))
3439 {
3440 flush_pipe = FALSE; /* client did not provide startup data */
3441 goto out;
3442 }
3443
3444 if (!InitializeSecurityDescriptor(&ovpn_sd, SECURITY_DESCRIPTOR_REVISION))
3445 {
3446 ReturnLastError(pipe, L"InitializeSecurityDescriptor");
3447 goto out;
3448 }
3449
3450 /* Get SID of user the service is running under */
3451 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &svc_token))
3452 {
3453 ReturnLastError(pipe, L"OpenProcessToken");
3454 goto out;
3455 }
3456 len = 0;
3457 while (!GetTokenInformation(svc_token, TokenUser, svc_user, len, &len))
3458 {
3459 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
3460 {
3461 ReturnLastError(pipe, L"GetTokenInformation (service token)");
3462 goto out;
3463 }
3464 free(svc_user);
3465 svc_user = malloc(len);
3466 if (svc_user == NULL)
3467 {
3468 ReturnLastError(pipe, L"malloc (service token user)");
3469 goto out;
3470 }
3471 }
3472 if (!IsValidSid(svc_user->User.Sid))
3473 {
3474 ReturnLastError(pipe, L"IsValidSid (service token user)");
3475 goto out;
3476 }
3477
3478 if (!ImpersonateNamedPipeClient(pipe))
3479 {
3480 ReturnLastError(pipe, L"ImpersonateNamedPipeClient");
3481 goto out;
3482 }
3483 if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &imp_token))
3484 {
3485 ReturnLastError(pipe, L"OpenThreadToken");
3486 goto out;
3487 }
3488 len = 0;
3489 while (!GetTokenInformation(imp_token, TokenUser, ovpn_user, len, &len))
3490 {
3491 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
3492 {
3493 ReturnLastError(pipe, L"GetTokenInformation (impersonation token)");
3494 goto out;
3495 }
3496 free(ovpn_user);
3497 ovpn_user = malloc(len);
3498 if (ovpn_user == NULL)
3499 {
3500 ReturnLastError(pipe, L"malloc (impersonation token user)");
3501 goto out;
3502 }
3503 }
3504 if (!IsValidSid(ovpn_user->User.Sid))
3505 {
3506 ReturnLastError(pipe, L"IsValidSid (impersonation token user)");
3507 goto out;
3508 }
3509
3510 /*
3511 * Only authorized users are allowed to use any command line options or
3512 * have the config file in locations other than the global config directory.
3513 *
3514 * Check options are white-listed and config is in the global directory
3515 * OR user is authorized to run any config.
3516 */
3517 if (!ValidateOptions(pipe, sud.directory, sud.options, errmsg, _countof(errmsg))
3518 && !IsAuthorizedUser(ovpn_user->User.Sid, imp_token, settings.ovpn_admin_group,
3520 {
3521 ReturnError(pipe, ERROR_STARTUP_DATA, errmsg, 1, &exit_event);
3522 goto out;
3523 }
3524
3525 /* OpenVPN process DACL entry for access by service and user */
3526 ea[0].grfAccessPermissions = SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL;
3527 ea[0].grfAccessMode = SET_ACCESS;
3528 ea[0].grfInheritance = NO_INHERITANCE;
3529 ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
3530 ea[0].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
3531 ea[0].Trustee.ptstrName = (LPWSTR)svc_user->User.Sid;
3532 ea[1].grfAccessPermissions = READ_CONTROL | PROCESS_VM_READ | SYNCHRONIZE
3533 | PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION;
3534 ea[1].grfAccessMode = SET_ACCESS;
3535 ea[1].grfInheritance = NO_INHERITANCE;
3536 ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
3537 ea[1].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
3538 ea[1].Trustee.ptstrName = (LPWSTR)ovpn_user->User.Sid;
3539
3540 /* Set owner and DACL of OpenVPN security descriptor */
3541 if (!SetSecurityDescriptorOwner(&ovpn_sd, svc_user->User.Sid, FALSE))
3542 {
3543 ReturnLastError(pipe, L"SetSecurityDescriptorOwner");
3544 goto out;
3545 }
3546 if (SetEntriesInAcl(2, ea, NULL, &ovpn_dacl) != ERROR_SUCCESS)
3547 {
3548 ReturnLastError(pipe, L"SetEntriesInAcl");
3549 goto out;
3550 }
3551 if (!SetSecurityDescriptorDacl(&ovpn_sd, TRUE, ovpn_dacl, FALSE))
3552 {
3553 ReturnLastError(pipe, L"SetSecurityDescriptorDacl");
3554 goto out;
3555 }
3556
3557 /* Create primary token from impersonation token */
3558 if (!DuplicateTokenEx(imp_token, TOKEN_ALL_ACCESS, NULL, 0, TokenPrimary, &pri_token))
3559 {
3560 ReturnLastError(pipe, L"DuplicateTokenEx");
3561 goto out;
3562 }
3563
3564 /* use /dev/null for stdout of openvpn (client should use --log for output) */
3565 stdout_write = CreateFile(_L("NUL"), GENERIC_WRITE, FILE_SHARE_WRITE, &inheritable,
3566 OPEN_EXISTING, 0, NULL);
3567 if (stdout_write == INVALID_HANDLE_VALUE)
3568 {
3569 ReturnLastError(pipe, L"CreateFile for stdout");
3570 goto out;
3571 }
3572
3573 if (!CreatePipe(&stdin_read, &stdin_write, &inheritable, 0)
3574 || !SetHandleInformation(stdin_write, HANDLE_FLAG_INHERIT, 0))
3575 {
3576 ReturnLastError(pipe, L"CreatePipe");
3577 goto out;
3578 }
3579
3580 UUID pipe_uuid;
3581 RPC_STATUS rpc_stat = UuidCreate(&pipe_uuid);
3582 if (rpc_stat != RPC_S_OK)
3583 {
3584 ReturnError(pipe, rpc_stat, L"UuidCreate", 1, &exit_event);
3585 goto out;
3586 }
3587
3588 RPC_WSTR pipe_uuid_str = NULL;
3589 rpc_stat = UuidToStringW(&pipe_uuid, &pipe_uuid_str);
3590 if (rpc_stat != RPC_S_OK)
3591 {
3592 ReturnError(pipe, rpc_stat, L"UuidToString", 1, &exit_event);
3593 goto out;
3594 }
3595 swprintf(ovpn_pipe_name, _countof(ovpn_pipe_name),
3596 L"\\\\.\\pipe\\" _L(PACKAGE) L"%ls\\service_%lu_%ls", service_instance,
3597 GetCurrentThreadId(), pipe_uuid_str);
3598 RpcStringFreeW(&pipe_uuid_str);
3599
3600 /* make a security descriptor for the named pipe with access
3601 * restricted to the user and SYSTEM
3602 */
3603
3604 SECURITY_ATTRIBUTES sa;
3605 PSECURITY_DESCRIPTOR pSD = NULL;
3606 LPCWSTR szSDDL = L"D:(A;;GA;;;SY)(A;;GA;;;OW)";
3607 if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(
3608 szSDDL, SDDL_REVISION_1, &pSD, NULL))
3609 {
3610 ReturnLastError(pipe, L"ConvertSDDL");
3611 goto out;
3612 }
3613 sa.nLength = sizeof(sa);
3614 sa.lpSecurityDescriptor = pSD;
3615 sa.bInheritHandle = FALSE;
3616
3617 ovpn_pipe = CreateNamedPipe(
3618 ovpn_pipe_name, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED,
3619 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS, 1, 128, 128, 0, &sa);
3620 if (ovpn_pipe == INVALID_HANDLE_VALUE)
3621 {
3622 ReturnLastError(pipe, L"CreateNamedPipe");
3623 goto out;
3624 }
3625
3626 svc_pipe = CreateFile(ovpn_pipe_name, GENERIC_READ | GENERIC_WRITE, 0, &inheritable,
3627 OPEN_EXISTING, 0, NULL);
3628 if (svc_pipe == INVALID_HANDLE_VALUE)
3629 {
3630 ReturnLastError(pipe, L"CreateFile");
3631 goto out;
3632 }
3633
3634 pipe_mode = PIPE_READMODE_MESSAGE;
3635 if (!SetNamedPipeHandleState(svc_pipe, &pipe_mode, NULL, NULL))
3636 {
3637 ReturnLastError(pipe, L"SetNamedPipeHandleState");
3638 goto out;
3639 }
3640
3641 cmdline_size = wcslen(sud.options) + 128;
3642 cmdline = malloc(cmdline_size * sizeof(*cmdline));
3643 if (cmdline == NULL)
3644 {
3645 ReturnLastError(pipe, L"malloc");
3646 goto out;
3647 }
3648 /* there seem to be no common printf specifier that works on all
3649 * mingw/msvc platforms without trickery, so convert to void* and use
3650 * PRIuPTR to print that as best compromise */
3651 swprintf(cmdline, cmdline_size, L"openvpn %ls --msg-channel %" PRIuPTR, sud.options,
3652 (uintptr_t)svc_pipe);
3653
3654 if (!CreateEnvironmentBlock(&user_env, imp_token, FALSE))
3655 {
3656 ReturnLastError(pipe, L"CreateEnvironmentBlock");
3657 goto out;
3658 }
3659
3660 startup_info.cb = sizeof(startup_info);
3661 startup_info.dwFlags = STARTF_USESTDHANDLES;
3662 startup_info.hStdInput = stdin_read;
3663 startup_info.hStdOutput = stdout_write;
3664 startup_info.hStdError = stdout_write;
3665
3666 exe_path = settings.exe_path;
3667
3668 /* TODO: make sure HKCU is correct or call LoadUserProfile() */
3669 if (!CreateProcessAsUserW(pri_token, exe_path, cmdline, &ovpn_sa, NULL, TRUE,
3670 settings.priority | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
3671 user_env, sud.directory, &startup_info, &proc_info))
3672 {
3673 ReturnLastError(pipe, L"CreateProcessAsUser");
3674 goto out;
3675 }
3676
3677 if (!RevertToSelf())
3678 {
3679 TerminateProcess(proc_info.hProcess, 1);
3680 ReturnLastError(pipe, L"RevertToSelf");
3681 goto out;
3682 }
3683
3684 ReturnProcessId(pipe, proc_info.dwProcessId, 1, &exit_event);
3685
3686 CloseHandleEx(&stdout_write);
3687 CloseHandleEx(&stdin_read);
3688 CloseHandleEx(&svc_pipe);
3689
3690 DWORD input_size = WideCharToMultiByte(CP_UTF8, 0, sud.std_input, -1, NULL, 0, NULL, NULL);
3691 LPSTR input = NULL;
3692 if (input_size && (input = malloc(input_size)))
3693 {
3694 DWORD written;
3695 WideCharToMultiByte(CP_UTF8, 0, sud.std_input, -1, input, input_size, NULL, NULL);
3696 WriteFile(stdin_write, input, (DWORD)strlen(input), &written, NULL);
3697 free(input);
3698 }
3699
3700 while (TRUE)
3701 {
3702 DWORD bytes = PeekNamedPipeAsync(ovpn_pipe, 1, &exit_event);
3703 if (bytes == 0)
3704 {
3705 break;
3706 }
3707
3708 if (bytes > sizeof(pipe_message_t))
3709 {
3710 /* process at the other side of the pipe is misbehaving, shut it down */
3713 L"OpenVPN process sent too large payload length to the pipe (%lu bytes), it will be terminated",
3714 bytes);
3715 break;
3716 }
3717
3718 HandleMessage(ovpn_pipe, &proc_info, bytes, 1, &exit_event, &undo_lists);
3719 }
3720
3721 WaitForSingleObject(proc_info.hProcess, IO_TIMEOUT);
3722 GetExitCodeProcess(proc_info.hProcess, &exit_code);
3723 if (exit_code == STILL_ACTIVE)
3724 {
3725 TerminateProcess(proc_info.hProcess, 1);
3726 }
3727 else if (exit_code != 0)
3728 {
3729 WCHAR buf[256];
3730 swprintf(buf, _countof(buf), L"OpenVPN exited with error: exit code = %lu", exit_code);
3732 }
3733 Undo(&undo_lists);
3734
3735out:
3736 if (flush_pipe)
3737 {
3738 FlushFileBuffers(pipe);
3739 }
3740 DisconnectNamedPipe(pipe);
3741
3742 free(ovpn_user);
3743 free(svc_user);
3744 free(cmdline);
3745 DestroyEnvironmentBlock(user_env);
3746 FreeStartupData(&sud);
3747 CloseHandleEx(&proc_info.hProcess);
3748 CloseHandleEx(&proc_info.hThread);
3749 CloseHandleEx(&stdin_read);
3750 CloseHandleEx(&stdin_write);
3751 CloseHandleEx(&stdout_write);
3752 CloseHandleEx(&svc_token);
3753 CloseHandleEx(&imp_token);
3754 CloseHandleEx(&pri_token);
3755 CloseHandleEx(&ovpn_pipe);
3756 CloseHandleEx(&svc_pipe);
3757 CloseHandleEx(&pipe);
3758
3759 return 0;
3760}
3761
3762
3763static DWORD WINAPI
3764ServiceCtrlInteractive(DWORD ctrl_code, DWORD event, LPVOID data, LPVOID ctx)
3765{
3766 SERVICE_STATUS *status = ctx;
3767 switch (ctrl_code)
3768 {
3769 case SERVICE_CONTROL_STOP:
3770 status->dwCurrentState = SERVICE_STOP_PENDING;
3772 if (exit_event)
3773 {
3774 SetEvent(exit_event);
3775 }
3776 return NO_ERROR;
3777
3778 case SERVICE_CONTROL_INTERROGATE:
3779 return NO_ERROR;
3780
3781 default:
3782 return ERROR_CALL_NOT_IMPLEMENTED;
3783 }
3784}
3785
3786
3787static HANDLE
3789{
3790 /*
3791 * allow all access for local system
3792 * deny FILE_CREATE_PIPE_INSTANCE for everyone
3793 * allow read/write for authenticated users
3794 * deny all access to anonymous
3795 */
3796 const WCHAR *sddlString =
3797 L"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)";
3798
3799 PSECURITY_DESCRIPTOR sd = NULL;
3800 if (!ConvertStringSecurityDescriptorToSecurityDescriptor(sddlString, SDDL_REVISION_1, &sd,
3801 NULL))
3802 {
3803 MsgToEventLog(M_SYSERR, L"ConvertStringSecurityDescriptorToSecurityDescriptor failed.");
3804 return INVALID_HANDLE_VALUE;
3805 }
3806
3807 /* Set up SECURITY_ATTRIBUTES */
3808 SECURITY_ATTRIBUTES sa = { 0 };
3809 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
3810 sa.lpSecurityDescriptor = sd;
3811 sa.bInheritHandle = FALSE;
3812
3813 DWORD flags = PIPE_ACCESS_DUPLEX | WRITE_DAC | FILE_FLAG_OVERLAPPED;
3814
3815 static BOOL first = TRUE;
3816 if (first)
3817 {
3818 flags |= FILE_FLAG_FIRST_PIPE_INSTANCE;
3819 first = FALSE;
3820 }
3821
3822 WCHAR pipe_name[256]; /* The entire pipe name string can be up to 256 characters long according
3823 to MSDN. */
3824 swprintf(pipe_name, _countof(pipe_name), L"\\\\.\\pipe\\" _L(PACKAGE) L"%ls\\service",
3826 HANDLE pipe = CreateNamedPipe(
3827 pipe_name, flags, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
3828 PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0, &sa);
3829
3830 LocalFree(sd);
3831
3832 if (pipe == INVALID_HANDLE_VALUE)
3833 {
3834 MsgToEventLog(M_SYSERR, L"Could not create named pipe");
3835 return INVALID_HANDLE_VALUE;
3836 }
3837
3838 return pipe;
3839}
3840
3841
3842static DWORD
3843UpdateWaitHandles(LPHANDLE *handles_ptr, LPDWORD count, HANDLE io_event, HANDLE exit_event,
3844 list_item_t *threads)
3845{
3846 static DWORD size = 10;
3847 static LPHANDLE handles = NULL;
3848 DWORD pos = 0;
3849
3850 if (handles == NULL)
3851 {
3852 handles = malloc(size * sizeof(HANDLE));
3853 *handles_ptr = handles;
3854 if (handles == NULL)
3855 {
3856 return ERROR_OUTOFMEMORY;
3857 }
3858 }
3859
3860 handles[pos++] = io_event;
3861
3862 if (!threads)
3863 {
3864 handles[pos++] = exit_event;
3865 }
3866
3867 while (threads)
3868 {
3869 if (pos == size)
3870 {
3871 LPHANDLE tmp;
3872 size += 10;
3873 tmp = realloc(handles, size * sizeof(HANDLE));
3874 if (tmp == NULL)
3875 {
3876 size -= 10;
3877 *count = pos;
3878 return ERROR_OUTOFMEMORY;
3879 }
3880 handles = tmp;
3881 *handles_ptr = handles;
3882 }
3883 handles[pos++] = threads->data;
3884 threads = threads->next;
3885 }
3886
3887 *count = pos;
3888 return NO_ERROR;
3889}
3890
3891
3892static VOID
3894{
3895 free(h);
3896}
3897
3898static BOOL
3899CmpHandle(LPVOID item, LPVOID hnd)
3900{
3901 return item == hnd;
3902}
3903
3904
3905VOID WINAPI
3906ServiceStartInteractiveOwn(DWORD dwArgc, LPWSTR *lpszArgv)
3907{
3908 status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
3909 ServiceStartInteractive(dwArgc, lpszArgv);
3910}
3911
3917static void
3919{
3920 BOOL changed = FALSE;
3921
3922 /* Clean up leftover NRPT rules */
3923 BOOL gpol_nrpt;
3924 changed = DeleteNrptRules(0, &gpol_nrpt);
3925
3926 /* Clean up leftover DNS search list fragments */
3927 HKEY key;
3928 BOOL gpol_list;
3929 GetDnsSearchListKey(NULL, &gpol_list, &key);
3930 if (key != INVALID_HANDLE_VALUE)
3931 {
3933 {
3934 changed = TRUE;
3935 }
3936 RegCloseKey(key);
3937 }
3938
3939 if (changed)
3940 {
3941 ApplyDnsSettings(gpol_nrpt || gpol_list);
3942 }
3943}
3944
3945VOID WINAPI
3946ServiceStartInteractive(DWORD dwArgc, LPWSTR *lpszArgv)
3947{
3948 HANDLE pipe, io_event = NULL;
3949 OVERLAPPED overlapped;
3950 DWORD error = NO_ERROR;
3951 list_item_t *threads = NULL;
3952 PHANDLE handles = NULL;
3953 DWORD handle_count;
3954
3955 service =
3956 RegisterServiceCtrlHandlerEx(interactive_service.name, ServiceCtrlInteractive, &status);
3957 if (!service)
3958 {
3959 return;
3960 }
3961
3962 status.dwCurrentState = SERVICE_START_PENDING;
3963 status.dwServiceSpecificExitCode = NO_ERROR;
3964 status.dwWin32ExitCode = NO_ERROR;
3965 status.dwWaitHint = 3000;
3967
3968 /* Clean up potentially left over registry values */
3970
3971 /* Read info from registry in key HKLM\SOFTWARE\OpenVPN */
3972 error = GetOpenvpnSettings(&settings);
3973 if (error != ERROR_SUCCESS)
3974 {
3975 goto out;
3976 }
3977
3978 io_event = InitOverlapped(&overlapped);
3979 exit_event = CreateEvent(NULL, TRUE, FALSE, NULL);
3980 if (!exit_event || !io_event)
3981 {
3982 error = MsgToEventLog(M_SYSERR, L"Could not create event");
3983 goto out;
3984 }
3985
3986 rdns_semaphore = CreateSemaphoreW(NULL, 1, 1, NULL);
3987 if (!rdns_semaphore)
3988 {
3989 error = MsgToEventLog(M_SYSERR, L"Could not create semaphore for register-dns");
3990 goto out;
3991 }
3992
3993 error = UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
3994 if (error != NO_ERROR)
3995 {
3996 goto out;
3997 }
3998
3999 pipe = CreateClientPipeInstance();
4000 if (pipe == INVALID_HANDLE_VALUE)
4001 {
4002 goto out;
4003 }
4004
4005 status.dwCurrentState = SERVICE_RUNNING;
4006 status.dwWaitHint = 0;
4008
4009 while (TRUE)
4010 {
4011 if (!ConnectNamedPipe(pipe, &overlapped))
4012 {
4013 DWORD connect_error = GetLastError();
4014 if (connect_error == ERROR_NO_DATA)
4015 {
4016 /*
4017 * Client connected and disconnected before we could process it.
4018 * Disconnect and retry instead of aborting the service.
4019 */
4020 MsgToEventLog(M_ERR, L"ConnectNamedPipe returned ERROR_NO_DATA (client dropped)");
4021 DisconnectNamedPipe(pipe);
4022 ResetOverlapped(&overlapped);
4023 continue;
4024 }
4025 else if (connect_error == ERROR_PIPE_CONNECTED)
4026 {
4027 /* No async I/O pending in this case; signal manually. */
4028 SetEvent(overlapped.hEvent);
4029 }
4030 else if (connect_error != ERROR_IO_PENDING)
4031 {
4032 MsgToEventLog(M_SYSERR, L"Could not connect pipe");
4033 break;
4034 }
4035 }
4036
4037 error = WaitForMultipleObjects(handle_count, handles, FALSE, INFINITE);
4038 if (error == WAIT_OBJECT_0)
4039 {
4040 /* Client connected, spawn a worker thread for it */
4041 HANDLE next_pipe = CreateClientPipeInstance();
4042
4043 /* Avoid exceeding WaitForMultipleObjects MAXIMUM_WAIT_OBJECTS */
4044 if (handle_count + 1 > MAXIMUM_WAIT_OBJECTS)
4045 {
4046 ReturnError(pipe, ERROR_CANT_WAIT, L"Too many concurrent clients", 1, &exit_event);
4047 CloseHandleEx(&pipe);
4048 pipe = next_pipe;
4049 ResetOverlapped(&overlapped);
4050 continue;
4051 }
4052
4053 HANDLE thread = CreateThread(NULL, 0, RunOpenvpn, pipe, CREATE_SUSPENDED, NULL);
4054 if (thread)
4055 {
4056 error = AddListItem(&threads, thread);
4057 if (!error)
4058 {
4059 error =
4060 UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
4061 }
4062 if (error)
4063 {
4064 ReturnError(pipe, error, L"Insufficient resources to service new clients", 1,
4065 &exit_event);
4066 /* Update wait handles again after removing the last worker thread */
4067 RemoveListItem(&threads, CmpHandle, thread);
4068 UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
4069 TerminateThread(thread, 1);
4070 CloseHandleEx(&thread);
4071 CloseHandleEx(&pipe);
4072 }
4073 else
4074 {
4075 ResumeThread(thread);
4076 }
4077 }
4078 else
4079 {
4080 CloseHandleEx(&pipe);
4081 }
4082
4083 ResetOverlapped(&overlapped);
4084 pipe = next_pipe;
4085 }
4086 else
4087 {
4088 CancelIo(pipe);
4089 if (error == WAIT_FAILED)
4090 {
4091 MsgToEventLog(M_SYSERR, L"WaitForMultipleObjects failed");
4092 SetEvent(exit_event);
4093 /* Give some time for worker threads to exit and then terminate */
4094 Sleep(1000);
4095 break;
4096 }
4097 if (!threads)
4098 {
4099 /* exit event signaled */
4100 CloseHandleEx(&pipe);
4101 ResetEvent(exit_event);
4102 error = NO_ERROR;
4103 break;
4104 }
4105
4106 /* Worker thread ended */
4107 HANDLE thread = RemoveListItem(&threads, CmpHandle, handles[error]);
4108 UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
4109 CloseHandleEx(&thread);
4110 }
4111 }
4112
4113out:
4114 FreeWaitHandles(handles);
4115 CloseHandleEx(&io_event);
4118
4119 status.dwCurrentState = SERVICE_STOPPED;
4120 status.dwWin32ExitCode = error;
4122}
wchar_t * utf8to16_size(const char *utf8, int size)
Convert a UTF-8 string to UTF-16.
Definition common.c:294
DWORD MsgToEventLog(DWORD flags, LPCWSTR format,...)
Definition common.c:253
LPCWSTR service_instance
Definition common.c:29
DWORD GetOpenvpnSettings(settings_t *s)
Definition common.c:76
#define M_INFO
Definition errlevel.h:54
static LSTATUS GetItfDnsServersV4(HKEY itf_key, PSTR addrs, PDWORD size)
Get DNS server IPv4 addresses of an interface.
static LSTATUS SetNameServerAddresses(PWSTR itf_id, const nrpt_address_t *addresses)
Set name servers from a NRPT address list.
static VOID ReturnLastError(HANDLE pipe, LPCWSTR func)
static BOOL GetInterfacesKey(short family, PHKEY key)
Return the interfaces registry key for the specified address family.
static DWORD ReadPipeAsync(HANDLE pipe, LPVOID buffer, DWORD size, DWORD count, LPHANDLE events)
static void UndoNrptRules(DWORD ovpn_pid)
Delete a process' NRPT rules and apply the reduced set of rules.
static BOOL ApplyGpolSettings(void)
Signal the DNS resolver (and others potentially) to reload the group policy (DNS) settings.
static VOID ReturnProcessId(HANDLE pipe, DWORD pid, DWORD count, LPHANDLE events)
static BOOL GetDnsSearchListKey(PCSTR itf_name, PBOOL gpol, PHKEY key)
Find the registry key for storing the DNS domains for the VPN interface.
static DWORD HandleWINSConfigMessage(const wins_cfg_message_t *msg, undo_lists_t *lists)
static BOOL CmpAddress(LPVOID item, LPVOID address)
static LSTATUS GetItfDnsDomains(HKEY itf, PCWSTR search_domains, PWSTR domains, PDWORD size)
Return interface specific domain suffix(es)
static DWORD PeekNamedPipeAsyncTimed(HANDLE pipe, DWORD count, LPHANDLE events)
static DWORD PeekNamedPipeAsync(HANDLE pipe, DWORD count, LPHANDLE events)
static BOOL ResetOverlapped(LPOVERLAPPED overlapped)
static DWORD SetNameServers(PCWSTR itf_id, short family, PCSTR addrs)
Set the DNS name servers in a registry interface configuration.
static void SetNrptExcludeRules(HKEY nrpt_key, DWORD ovpn_pid, PCWSTR search_domains)
Set NRPT exclude rules to accompany a catch all rule.
static DWORD ExecCommand(const WCHAR *argv0, const WCHAR *cmdline, DWORD timeout)
static DWORD HandleEnableDHCPMessage(const enable_dhcp_message_t *dhcp)
static BOOL ResetDnsSearchDomains(HKEY key)
Reset the DNS search list to its original value.
static DWORD AddWfpBlock(const wfp_block_message_t *msg, undo_lists_t *lists)
static HANDLE CreateClientPipeInstance(VOID)
static DWORD DeleteWfpBlock(undo_lists_t *lists)
static void GetNrptExcludeData(PCWSTR search_domains, nrpt_exclude_data_t *data, size_t data_size)
Collect interface DNS settings to be used in excluding NRPT rules.
static DWORD SetNameServersValue(PCWSTR itf_id, short family, PCSTR value)
Set the DNS name servers in a registry interface configuration.
static BOOL GetStartupData(HANDLE pipe, STARTUP_DATA *sud)
static BOOL DeleteNrptRules(DWORD pid, PBOOL gpol)
Delete OpenVPN NRPT rules from the registry.
static VOID Undo(undo_lists_t *lists)
static BOOL ApplyDnsSettings(BOOL apply_gpol)
Signal the DNS resolver to reload its settings.
#define ERROR_STARTUP_DATA
Definition interactive.c:47
static DWORD WINAPI RunOpenvpn(LPVOID p)
static settings_t settings
Definition interactive.c:54
VOID WINAPI ServiceStartInteractive(DWORD dwArgc, LPWSTR *lpszArgv)
static DWORD DeleteRoute(PMIB_IPFORWARD_ROW2 fwd_row)
static SERVICE_STATUS status
Definition interactive.c:52
static DWORD HandleDNSConfigNrptMessage(const nrpt_dns_cfg_message_t *msg, DWORD ovpn_pid, undo_lists_t *lists)
Add Name Resolution Policy Table (NRPT) rules as documented in https://msdn.microsoft....
static DWORD SetDnsSearchDomains(PCSTR itf_name, PCSTR domains, PBOOL gpol, undo_lists_t *lists)
Add or remove DNS search domains.
static void CleanupRegistry(void)
Clean up remains of previous sessions in registry.
static DWORD netsh_wins_cmd(const wchar_t *action, DWORD if_index, const wchar_t *addr)
Run the command: netsh interface ip $action wins $if_index [static] $addr.
#define ERROR_MESSAGE_TYPE
Definition interactive.c:49
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 BOOL ApplyGpolSettings64(void)
Signal the DNS resolver (and others potentially) to reload the group policy (DNS) settings on 64 bit ...
static DWORD HandleAddressMessage(address_message_t *msg, undo_lists_t *lists)
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)
static DWORD ResetNameServers(PCWSTR itf_id, short family)
Delete all DNS name servers from a registry interface configuration.
static LSTATUS OpenNrptBaseKey(PHKEY key, PBOOL gpol)
Return the registry key where NRPT rules are stored.
#define RDNS_TIMEOUT
Definition interactive.c:56
undo_type_t
Definition interactive.c:84
@ wfp_block
Definition interactive.c:87
@ _undo_type_max
Definition interactive.c:93
@ undo_dns6
Definition interactive.c:89
@ undo_dns4
Definition interactive.c:88
@ undo_wins
Definition interactive.c:92
@ route
Definition interactive.c:86
@ undo_nrpt
Definition interactive.c:90
@ address
Definition interactive.c:85
@ undo_domains
Definition interactive.c:91
static BOOL HasValidSearchList(HKEY key)
Check for a valid search list in a certain key of the registry.
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)
static BOOL StoreInitialDnsSearchList(HKEY key, PCWSTR list)
Prepare DNS domain "SearchList" registry value, so additional VPN domains can be added and its origin...
struct _list_item list_item_t
static DWORD RegWStringSize(PCWSTR string)
Return correct size for registry value to set for string.
static DWORD DeleteAddress(PMIB_UNICASTIPADDRESS_ROW addr_row)
static BOOL IsInterfaceConnected(PWSTR iid_str)
Check if an interface is connected and up.
#define ERROR_OPENVPN_STARTUP
Definition interactive.c:46
static DWORD SetNrptRules(HKEY nrpt_key, const nrpt_address_t *addresses, const char *domains, const char *search_domains, BOOL dnssec, DWORD ovpn_pid)
Set NRPT rules for a openvpn process.
static LSTATUS GetItfDnsServersV6(HKEY itf_key, PSTR addrs, PDWORD size)
Get DNS server IPv6 addresses of an interface.
static BOOL AppendSearchList(PWSTR list, size_t list_cap, PCWSTR add)
Append a comma-separated list of domains to another comma-separated list, in place.
static DWORD SetNrptRule(HKEY nrpt_key, PCWSTR subkey, PCSTR address, PCWSTR domains, DWORD dom_size, BOOL dnssec)
Set a NRPT rule (subkey) and its values in the registry.
static BOOL AddDnsSearchDomains(HKEY key, BOOL have_list, PCWSTR domains)
Append domain suffixes to an existing search list.
static VOID FreeWaitHandles(LPHANDLE h)
openvpn_service_t interactive_service
Definition interactive.c:61
VOID WINAPI ServiceStartInteractiveOwn(DWORD dwArgc, LPWSTR *lpszArgv)
static size_t RemoveSearchListTokens(PWSTR list, PCWSTR remove)
Remove tokens from a comma-separated search list with multiset semantics: for each comma-separated to...
static DWORD AsyncPipeOp(async_op_t op, HANDLE pipe, LPVOID buffer, DWORD size, DWORD count, LPHANDLE events)
#define IO_TIMEOUT
Definition interactive.c:44
static BOOL ListContainsDomain(PCWSTR list, PCWSTR domain, size_t len)
Check if a domain is contained in a comma separated list of domains.
static BOOL IsDhcpEnabled(HKEY key)
Checks if DHCP is enabled for an interface.
static DWORD HandleFlushNeighborsMessage(flush_neighbors_message_t *msg)
static BOOL ApplyGpolSettings32(void)
Signal the DNS resolver (and others potentially) to reload the group policy (DNS) settings on 32 bit ...
static DWORD HandleMTUMessage(const set_mtu_message_t *mtu)
list_item_t * undo_lists_t[_undo_type_max]
Definition interactive.c:95
static VOID HandleMessage(HANDLE pipe, PPROCESS_INFORMATION proc_info, DWORD bytes, DWORD count, LPHANDLE events, undo_lists_t *lists)
static DWORD HandleRegisterDNSMessage(void)
static void RemoveDnsSearchDomains(HKEY key, PCWSTR domains)
Remove domain suffixes from an existing search list.
static BOOL InitialSearchListExists(HKEY key)
Check if a initial list had already been created.
#define ERROR_MESSAGE_DATA
Definition interactive.c:48
static HANDLE exit_event
Definition interactive.c:53
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:55
static DWORD InterfaceLuid(const char *iface_name, PNET_LUID luid)
static LSTATUS ConvertItfDnsDomains(PCWSTR search_domains, PWSTR domains, PDWORD size, const DWORD capacity)
Convert interface specific domain suffix(es) from comma-separated string to MULTI_SZ string.
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 BOOL CmpAny(LPVOID item, LPVOID any)
async_op_t
@ peek
@ write
@ peek_timed
@ read
static DWORD HandleCreateAdapterMessage(const create_adapter_message_t *msg)
Creates a VPN adapter of the specified type by invoking tapctl.exe.
static DWORD InterfaceIdString(PCSTR itf_name, PWSTR str, size_t len)
Get the string interface UUID (with braces) for an interface alias name.
static SERVICE_STATUS_HANDLE service
Definition interactive.c:51
static DWORD WritePipeAsync(HANDLE pipe, LPVOID data, DWORD size, DWORD count, LPHANDLE events)
static void UndoDnsSearchDomains(dns_domains_undo_data_t *undo_data)
Removes DNS domains from a search list they were previously added to.
@ nrpt_dnssec
@ wfp_block_dns
Definition openvpn-msg.h:77
#define TUN_ADAPTER_INDEX_INVALID
Definition openvpn-msg.h:69
char nrpt_address_t[NRPT_ADDR_SIZE]
@ msg_add_nrpt_cfg
Definition openvpn-msg.h:38
@ msg_del_address
Definition openvpn-msg.h:33
@ msg_add_wins_cfg
Definition openvpn-msg.h:49
@ msg_add_address
Definition openvpn-msg.h:32
@ msg_del_wfp_block
Definition openvpn-msg.h:44
@ msg_enable_dhcp
Definition openvpn-msg.h:46
@ msg_add_wfp_block
Definition openvpn-msg.h:43
@ msg_add_route
Definition openvpn-msg.h:34
@ msg_create_adapter
Definition openvpn-msg.h:51
@ msg_del_wins_cfg
Definition openvpn-msg.h:50
@ msg_acknowledgement
Definition openvpn-msg.h:31
@ msg_add_dns_cfg
Definition openvpn-msg.h:36
@ msg_register_dns
Definition openvpn-msg.h:45
@ msg_del_nrpt_cfg
Definition openvpn-msg.h:39
@ msg_del_route
Definition openvpn-msg.h:35
@ msg_set_mtu
Definition openvpn-msg.h:48
@ msg_flush_neighbors
Definition openvpn-msg.h:42
@ msg_del_dns_cfg
Definition openvpn-msg.h:37
@ ADAPTER_TYPE_DCO
@ ADAPTER_TYPE_TAP
#define NRPT_ADDR_SIZE
#define NRPT_ADDR_NUM
#define M_ERR
Definition error.h:106
#define msg(flags,...)
Definition error.h:152
BOOL ReportStatusToSCMgr(SERVICE_STATUS_HANDLE service, SERVICE_STATUS *status)
Definition service.c:22
#define SERVICE_DEPENDENCIES
Definition service.h:37
#define M_SYSERR
Definition service.h:45
#define MSG_FLAGS_ERROR
Definition service.h:42
@ interactive
Definition service.h:50
static wchar_t * utf8to16(const char *utf8)
Convert a zero terminated UTF-8 string to UTF-16.
Definition service.h:122
static int pos(char c)
Definition base64.c:104
LPVOID data
Definition interactive.c:78
struct _list_item * next
Definition interactive.c:77
WCHAR * directory
Definition interactive.c:68
WCHAR * options
Definition interactive.c:69
WCHAR * std_input
Definition interactive.c:70
message_header_t header
Definition argv.h:35
Wrapper structure for dynamically allocated memory.
Definition buffer.h:60
Definition dhcp.h:62
interface_t iface
char name[256]
Definition openvpn-msg.h:71
Container for unidirectional cipher and HMAC key material.
Definition crypto.h:152
message_type_t type
Definition openvpn-msg.h:56
nrpt_address_t addresses[NRPT_ADDR_NUM]
CHAR addresses[NRPT_ADDR_NUM *NRPT_ADDR_SIZE]
interface_t iface
WCHAR ovpn_admin_group[MAX_NAME]
Definition service.h:71
WCHAR bin_dir[MAX_PATH]
Definition service.h:68
WCHAR ovpn_service_user[MAX_NAME]
Definition service.h:72
DWORD priority
Definition service.h:73
WCHAR exe_path[MAX_PATH]
Definition service.h:66
#define _L(q)
Definition basic.h:38
static int cleanup(void **state)
const char * msg2
const char * msg1
struct in6_addr ipv6
Definition openvpn-msg.h:64
struct in_addr ipv4
Definition openvpn-msg.h:63
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
route_message_t route
nrpt_dns_cfg_message_t nrpt_dns
set_mtu_message_t mtu
create_adapter_message_t create_adapter
BOOL IsAuthorizedUser(PSID sid, const HANDLE token, const WCHAR *ovpn_admin_group, const WCHAR *ovpn_service_user)
Definition validate.c:138
BOOL CheckOption(const WCHAR *workdir, int argc, WCHAR *argv[], const settings_t *s)
Definition validate.c:316
static BOOL IsOption(const WCHAR *o)
Definition validate.h:47
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:369
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:408
DWORD delete_wfp_block_filters(HANDLE engine_handle)
Definition wfp_block.c:344
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:153
#define WFP_BLOCK_IFACE_METRIC
Definition wfp_block.h:33
char * get_win_sys_path(void)
Definition win32.c:1108