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
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 convert_status;
545 LPWSTR wide_name = utf8to16(iface_name);
546
547 if (wide_name)
548 {
549 convert_status = ConvertInterfaceAliasToLuid(wide_name, luid);
550 free(wide_name);
551 }
552 else
553 {
554 convert_status = ERROR_OUTOFMEMORY;
555 }
556 return convert_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 control_status;
1187 if (ControlService(dnssvc, SERVICE_CONTROL_PARAMCHANGE, &control_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 PWSTR wide_name = utf8to16(itf_name);
1228 MsgToEventLog(M_ERR, L"%S: failed to convert itf alias '%s'", __func__, wide_name);
1229 free(wide_name);
1230 goto out;
1231 }
1232 err = ConvertInterfaceLuidToGuid(&luid, &guid);
1233 if (err)
1234 {
1235 PWSTR wide_name = utf8to16(itf_name);
1236 MsgToEventLog(M_ERR, L"%S: Failed to convert itf '%s' LUID", __func__, wide_name);
1237 free(wide_name);
1238 goto out;
1239 }
1240
1241 if (StringFromIID(&guid, &iid_str) != S_OK)
1242 {
1243 PWSTR wide_name = utf8to16(itf_name);
1244 MsgToEventLog(M_ERR, L"%S: Failed to convert itf '%s' IID", __func__, wide_name);
1245 free(wide_name);
1246 err = ERROR_OUTOFMEMORY;
1247 goto out;
1248 }
1249 if (wcslen(iid_str) + 1 > len)
1250 {
1251 err = ERROR_INVALID_PARAMETER;
1252 goto out;
1253 }
1254
1255 wcsncpy(str, iid_str, len);
1256
1257out:
1258 if (iid_str)
1259 {
1260 CoTaskMemFree(iid_str);
1261 }
1262 return err;
1263}
1264
1278static BOOL
1280{
1281 char data[64];
1282 DWORD size = sizeof(data);
1283 LSTATUS err = RegGetValueA(key, NULL, "SearchList", RRF_RT_REG_SZ, NULL, (PBYTE)data, &size);
1284 if (!err || err == ERROR_MORE_DATA)
1285 {
1286 data[sizeof(data) - 1] = '\0';
1287 for (size_t i = 0; i < strlen(data); ++i)
1288 {
1289 if (isalnum(data[i]) || data[i] == '-' || data[i] == '.')
1290 {
1291 return TRUE;
1292 }
1293 }
1294 }
1295 return FALSE;
1296}
1297
1315static BOOL
1316GetDnsSearchListKey(PCSTR itf_name, PBOOL gpol, PHKEY key)
1317{
1318 LSTATUS err;
1319
1320 *gpol = FALSE;
1321
1322 /* Try the group policy search list */
1323 err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient",
1324 0, KEY_ALL_ACCESS, key);
1325 if (!err)
1326 {
1327 if (HasValidSearchList(*key))
1328 {
1329 *gpol = TRUE;
1330 return TRUE;
1331 }
1332 RegCloseKey(*key);
1333 }
1334
1335 /* Try the system-wide search list */
1336 err =
1337 RegOpenKeyExA(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\TCPIP\\Parameters",
1338 0, KEY_ALL_ACCESS, key);
1339 if (!err)
1340 {
1341 if (HasValidSearchList(*key))
1342 {
1343 return TRUE;
1344 }
1345 RegCloseKey(*key);
1346 }
1347
1348 if (itf_name)
1349 {
1350 /* Always return the VPN interface key (if it exists) */
1351 WCHAR iid[64];
1352 DWORD iid_err = InterfaceIdString(itf_name, iid, _countof(iid));
1353 if (!iid_err)
1354 {
1355 HKEY itfs;
1356 err =
1357 RegOpenKeyExA(HKEY_LOCAL_MACHINE,
1358 "System\\CurrentControlSet\\Services\\TCPIP\\Parameters\\Interfaces",
1359 0, KEY_ALL_ACCESS, &itfs);
1360 if (!err)
1361 {
1362 err = RegOpenKeyExW(itfs, iid, 0, KEY_ALL_ACCESS, key);
1363 RegCloseKey(itfs);
1364 if (!err)
1365 {
1366 return FALSE; /* No need to preserve the VPN itf search list */
1367 }
1368 }
1369 }
1370 }
1371
1372 *key = INVALID_HANDLE_VALUE;
1373 return FALSE;
1374}
1375
1383static BOOL
1385{
1386 LSTATUS err;
1387
1388 err = RegGetValueA(key, NULL, "InitialSearchList", RRF_RT_REG_SZ, NULL, NULL, NULL);
1389 if (err)
1390 {
1391 if (err == ERROR_FILE_NOT_FOUND)
1392 {
1393 return FALSE;
1394 }
1395 MsgToEventLog(M_ERR, L"%S: failed to get InitialSearchList (%lu)", __func__, err);
1396 }
1397
1398 return TRUE;
1399}
1400
1405static DWORD
1406RegWStringSize(PCWSTR string)
1407{
1408 size_t length = (wcslen(string) + 1) * sizeof(wchar_t);
1409 if (length > UINT_MAX)
1410 {
1411 length = UINT_MAX;
1412 }
1413 return (DWORD)length;
1414}
1415
1426static BOOL
1428{
1429 if (!list || wcslen(list) == 0)
1430 {
1431 MsgToEventLog(M_ERR, L"%S: empty search list", __func__);
1432 return FALSE;
1433 }
1434
1436 {
1437 /* Initial list had already been stored */
1438 return TRUE;
1439 }
1440
1441 DWORD size = RegWStringSize(list);
1442 LSTATUS err = RegSetValueExW(key, L"InitialSearchList", 0, REG_SZ, (PBYTE)list, size);
1443 if (err)
1444 {
1445 MsgToEventLog(M_ERR, L"%S: failed to set InitialSearchList value (%lu)", __func__, err);
1446 return FALSE;
1447 }
1448
1449 return TRUE;
1450}
1451
1466static BOOL
1467AppendSearchList(PWSTR list, size_t list_cap, PCWSTR add)
1468{
1469 size_t list_len = wcslen(list);
1470 size_t add_len = wcslen(add);
1471 if (add_len == 0)
1472 {
1473 return TRUE;
1474 }
1475
1476 size_t sep_len = (list_len > 0) ? 1 : 0;
1477 if (list_len + sep_len + add_len + 1 > list_cap)
1478 {
1479 return FALSE;
1480 }
1481
1482 if (sep_len)
1483 {
1484 list[list_len++] = L',';
1485 }
1486 wmemcpy(list + list_len, add, add_len + 1);
1487 return TRUE;
1488}
1489
1513static size_t
1514RemoveSearchListTokens(PWSTR list, PCWSTR remove)
1515{
1516 size_t removed = 0;
1517 PCWSTR domain = remove;
1518 while (*domain)
1519 {
1520 PCWSTR comma = wcschr(domain, L',');
1521 size_t domain_len = comma ? (size_t)(comma - domain) : wcslen(domain);
1522 if (domain_len > 0)
1523 {
1524 /* Find the last token in @p list that exactly equals @p domain. */
1525 PWSTR match = NULL;
1526 PWSTR match_end = NULL;
1527 for (PWSTR p = list; *p;)
1528 {
1529 PWSTR tok_end = wcschr(p, L',');
1530 size_t tok_len = tok_end ? (size_t)(tok_end - p) : wcslen(p);
1531 if (tok_len == domain_len && wcsncmp(p, domain, domain_len) == 0)
1532 {
1533 match = p;
1534 match_end = tok_end;
1535 }
1536 if (!tok_end)
1537 {
1538 break;
1539 }
1540 p = tok_end + 1;
1541 }
1542 if (match)
1543 {
1544 /* Splice the token out, eating its leading comma if it has
1545 * one, otherwise its trailing comma. */
1546 PWSTR cut_start, cut_end;
1547 if (match == list)
1548 {
1549 cut_start = match;
1550 cut_end = match_end ? match_end + 1 : match + domain_len;
1551 }
1552 else
1553 {
1554 cut_start = match - 1;
1555 cut_end = match + domain_len;
1556 }
1557 wmemmove(cut_start, cut_end, wcslen(cut_end) + 1);
1558 removed++;
1559 }
1560 }
1561 if (!comma)
1562 {
1563 break;
1564 }
1565 domain = comma + 1;
1566 }
1567 return removed;
1568}
1569
1586static BOOL
1587AddDnsSearchDomains(HKEY key, BOOL have_list, PCWSTR domains)
1588{
1589 WCHAR list[2048] = { 0 };
1590
1591 if (have_list)
1592 {
1593 DWORD size = sizeof(list);
1594 LSTATUS err =
1595 RegGetValueW(key, NULL, L"SearchList", RRF_RT_REG_SZ, NULL, list, &size);
1596 if (err)
1597 {
1598 MsgToEventLog(M_SYSERR, L"%S: could not get SearchList from registry (%lu)", __func__,
1599 err);
1600 return FALSE;
1601 }
1602
1603 if (!StoreInitialDnsSearchList(key, list))
1604 {
1605 return FALSE;
1606 }
1607 }
1608
1609 if (!AppendSearchList(list, _countof(list), domains))
1610 {
1611 MsgToEventLog(M_SYSERR, L"%S: not enough space in list for search domains", __func__);
1612 return FALSE;
1613 }
1614
1615 DWORD size = RegWStringSize(list);
1616 LSTATUS err = RegSetValueExW(key, L"SearchList", 0, REG_SZ, (PBYTE)list, size);
1617 if (err)
1618 {
1619 MsgToEventLog(M_SYSERR, L"%S: could not set SearchList to registry (%lu)", __func__, err);
1620 return FALSE;
1621 }
1622
1623 return TRUE;
1624}
1625
1637static BOOL
1639{
1640 LSTATUS err;
1641 BOOL ret = FALSE;
1642 WCHAR list[2048];
1643 DWORD size = sizeof(list);
1644
1645 err = RegGetValueW(key, NULL, L"InitialSearchList", RRF_RT_REG_SZ, NULL, list, &size);
1646 if (err)
1647 {
1648 if (err != ERROR_FILE_NOT_FOUND)
1649 {
1650 MsgToEventLog(M_SYSERR, L"%S: could not get InitialSearchList from registry (%lu)",
1651 __func__, err);
1652 }
1653 goto out;
1654 }
1655
1656 size = RegWStringSize(list);
1657 err = RegSetValueExW(key, L"SearchList", 0, REG_SZ, (PBYTE)list, size);
1658 if (err)
1659 {
1660 MsgToEventLog(M_SYSERR, L"%S: could not set SearchList in registry (%lu)", __func__, err);
1661 goto out;
1662 }
1663
1664 RegDeleteValueA(key, "InitialSearchList");
1665 ret = TRUE;
1666
1667out:
1668 return ret;
1669}
1670
1685static void
1686RemoveDnsSearchDomains(HKEY key, PCWSTR domains)
1687{
1688 WCHAR list[2048];
1689 DWORD size = sizeof(list);
1690 LSTATUS err = RegGetValueW(key, NULL, L"SearchList", RRF_RT_REG_SZ, NULL, list, &size);
1691 if (err)
1692 {
1693 MsgToEventLog(M_SYSERR, L"%S: could not get SearchList from registry (%lu)", __func__, err);
1694 return;
1695 }
1696
1697 if (RemoveSearchListTokens(list, domains) == 0)
1698 {
1699 MsgToEventLog(M_ERR, L"%S: could not find domains in search list", __func__);
1700 return;
1701 }
1702
1703 if (list[0] != L'\0')
1704 {
1705 /* If the shortened list equals the snapshot we took at first
1706 * touch, the user's pre-VPN state is fully restored -- wipe both
1707 * SearchList and InitialSearchList. */
1708 WCHAR initial[2048];
1709 size = sizeof(initial);
1710 err = RegGetValueW(key, NULL, L"InitialSearchList", RRF_RT_REG_SZ, NULL, initial, &size);
1711 if (!err && wcscmp(list, initial) == 0)
1712 {
1714 return;
1715 }
1716 if (err && err != ERROR_FILE_NOT_FOUND)
1717 {
1718 MsgToEventLog(M_SYSERR, L"%S: could not get InitialSearchList from registry (%lu)",
1719 __func__, err);
1720 return;
1721 }
1722 }
1723
1724 size = RegWStringSize(list);
1725 err = RegSetValueExW(key, L"SearchList", 0, REG_SZ, (PBYTE)list, size);
1726 if (err)
1727 {
1728 MsgToEventLog(M_SYSERR, L"%S: could not set SearchList in registry (%lu)", __func__, err);
1729 }
1730}
1731
1737static void
1739{
1740 BOOL gpol;
1741 HKEY dns_searchlist_key;
1742 GetDnsSearchListKey(undo_data->itf_name, &gpol, &dns_searchlist_key);
1743 if (dns_searchlist_key != INVALID_HANDLE_VALUE)
1744 {
1745 RemoveDnsSearchDomains(dns_searchlist_key, undo_data->domains);
1746 RegCloseKey(dns_searchlist_key);
1747 ApplyDnsSettings(gpol);
1748
1749 free(undo_data->domains);
1750 undo_data->domains = NULL;
1751 }
1752}
1753
1775static DWORD
1776SetDnsSearchDomains(PCSTR itf_name, PCSTR domains, PBOOL gpol, undo_lists_t *lists)
1777{
1778 DWORD err = ERROR_OUTOFMEMORY;
1779
1780 HKEY list_key;
1781 BOOL have_list = GetDnsSearchListKey(itf_name, gpol, &list_key);
1782 if (list_key == INVALID_HANDLE_VALUE)
1783 {
1784 MsgToEventLog(M_SYSERR, L"%S: could not get search list registry key", __func__);
1785 return ERROR_FILE_NOT_FOUND;
1786 }
1787
1788 /* Remove previously installed search domains */
1789 dns_domains_undo_data_t *undo_data = RemoveListItem(&(*lists)[undo_domains], CmpAny, NULL);
1790 if (undo_data)
1791 {
1792 RemoveDnsSearchDomains(list_key, undo_data->domains);
1793 free(undo_data->domains);
1794 free(undo_data);
1795 undo_data = NULL;
1796 }
1797
1798 /* If there are search domains, add them */
1799 if (domains && *domains)
1800 {
1801 wchar_t *wide_domains = utf8to16(domains); /* utf8 to wide-char */
1802 if (!wide_domains)
1803 {
1804 goto out;
1805 }
1806
1807 undo_data = malloc(sizeof(*undo_data));
1808 if (!undo_data)
1809 {
1810 free(wide_domains);
1811 wide_domains = NULL;
1812 goto out;
1813 }
1814 strncpy(undo_data->itf_name, itf_name, sizeof(undo_data->itf_name));
1815 undo_data->domains = wide_domains;
1816
1817 if (AddDnsSearchDomains(list_key, have_list, wide_domains) == FALSE
1818 || AddListItem(&(*lists)[undo_domains], undo_data) != NO_ERROR)
1819 {
1820 RemoveDnsSearchDomains(list_key, wide_domains);
1821 free(wide_domains);
1822 free(undo_data);
1823 undo_data = NULL;
1824 goto out;
1825 }
1826 }
1827
1828 err = NO_ERROR;
1829
1830out:
1831 RegCloseKey(list_key);
1832 return err;
1833}
1834
1842static BOOL
1843GetInterfacesKey(short family, PHKEY key)
1844{
1845 PCSTR itfs_key = family == AF_INET6
1846 ? "SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters\\Interfaces"
1847 : "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces";
1848
1849 LSTATUS err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, itfs_key, 0, KEY_ALL_ACCESS, key);
1850 if (err)
1851 {
1852 *key = INVALID_HANDLE_VALUE;
1853 MsgToEventLog(M_SYSERR, L"%S: could not open interfaces registry key for family %d (%lu)",
1854 __func__, family, err);
1855 }
1856
1857 return err ? FALSE : TRUE;
1858}
1859
1869static DWORD
1870SetNameServersValue(PCWSTR itf_id, short family, PCSTR value)
1871{
1872 DWORD err;
1873
1874 HKEY itfs;
1875 if (!GetInterfacesKey(family, &itfs))
1876 {
1877 return ERROR_FILE_NOT_FOUND;
1878 }
1879
1880 HKEY itf = INVALID_HANDLE_VALUE;
1881 err = RegOpenKeyExW(itfs, itf_id, 0, KEY_ALL_ACCESS, &itf);
1882 if (err)
1883 {
1884 MsgToEventLog(M_SYSERR, L"%S: could not open interface key for %s family %d (%lu)",
1885 __func__, itf_id, family, err);
1886 goto out;
1887 }
1888
1889 err = RegSetValueExA(itf, "NameServer", 0, REG_SZ, (PBYTE)value, (DWORD)strlen(value) + 1);
1890 if (err)
1891 {
1892 MsgToEventLog(M_SYSERR, L"%S: could not set name servers '%S' for %s family %d (%lu)",
1893 __func__, value, itf_id, family, err);
1894 }
1895
1896out:
1897 if (itf != INVALID_HANDLE_VALUE)
1898 {
1899 RegCloseKey(itf);
1900 }
1901 if (itfs != INVALID_HANDLE_VALUE)
1902 {
1903 RegCloseKey(itfs);
1904 }
1905 return err;
1906}
1907
1917static DWORD
1918SetNameServers(PCWSTR itf_id, short family, PCSTR addrs)
1919{
1920 return SetNameServersValue(itf_id, family, addrs);
1921}
1922
1931static DWORD
1932ResetNameServers(PCWSTR itf_id, short family)
1933{
1934 return SetNameServersValue(itf_id, family, "");
1935}
1936
1937static DWORD
1939{
1940 DWORD err = 0;
1941 undo_type_t undo_type = (msg->family == AF_INET6) ? undo_dns6 : undo_dns4;
1942 unsigned int addr_len = msg->addr_len;
1943
1944 /* sanity check */
1945 const unsigned int max_addrs = _countof(msg->addr);
1946 if (addr_len > max_addrs)
1947 {
1948 addr_len = max_addrs;
1949 }
1950
1951 if (!msg->iface.name[0]) /* interface name is required */
1952 {
1953 return ERROR_MESSAGE_DATA;
1954 }
1955
1956 /* use a non-const reference with limited scope to enforce null-termination of strings from
1957 * client */
1958 {
1960 msgptr->iface.name[_countof(msg->iface.name) - 1] = '\0';
1961 msgptr->domains[_countof(msg->domains) - 1] = '\0';
1962 }
1963
1964 WCHAR iid[64];
1965 err = InterfaceIdString(msg->iface.name, iid, _countof(iid));
1966 if (err)
1967 {
1968 return err;
1969 }
1970
1971 /* We delete all current addresses before adding any
1972 * OR if the message type is del_dns_cfg
1973 */
1974 if (addr_len > 0 || msg->header.type == msg_del_dns_cfg)
1975 {
1976 err = ResetNameServers(iid, msg->family);
1977 if (err)
1978 {
1979 return err;
1980 }
1981 free(RemoveListItem(&(*lists)[undo_type], CmpAny, iid));
1982 }
1983
1984 if (msg->header.type == msg_del_dns_cfg)
1985 {
1986 BOOL gpol = FALSE;
1987 if (msg->domains[0])
1988 {
1989 /* setting an empty domain list removes any previous value */
1990 err = SetDnsSearchDomains(msg->iface.name, NULL, &gpol, lists);
1991 }
1992 ApplyDnsSettings(gpol);
1993 return err; /* job done */
1994 }
1995
1996 if (addr_len > 0)
1997 {
1998 /* prepare the comma separated address list */
1999 /* cannot use max_addrs here as that is not considered compile
2000 * time constant by all compilers and constexpr is C23 */
2001 CHAR addrs[_countof(msg->addr) * 64]; /* 64 is enough for one IPv4/6 address */
2002 size_t offset = 0;
2003 for (unsigned int i = 0; i < addr_len; ++i)
2004 {
2005 if (i != 0)
2006 {
2007 addrs[offset++] = ',';
2008 }
2009 if (msg->family == AF_INET6)
2010 {
2011 RtlIpv6AddressToStringA(&msg->addr[i].ipv6, addrs + offset);
2012 }
2013 else
2014 {
2015 RtlIpv4AddressToStringA(&msg->addr[i].ipv4, addrs + offset);
2016 }
2017 offset = strlen(addrs);
2018 }
2019
2020 err = SetNameServers(iid, msg->family, addrs);
2021 if (err)
2022 {
2023 return err;
2024 }
2025
2026 wchar_t *tmp_iid = _wcsdup(iid);
2027 if (!tmp_iid || AddListItem(&(*lists)[undo_type], tmp_iid))
2028 {
2029 free(tmp_iid);
2030 ResetNameServers(iid, msg->family);
2031 return ERROR_OUTOFMEMORY;
2032 }
2033 }
2034
2035 BOOL gpol = FALSE;
2036 if (msg->domains[0])
2037 {
2038 err = SetDnsSearchDomains(msg->iface.name, msg->domains, &gpol, lists);
2039 }
2040 ApplyDnsSettings(gpol);
2041
2042 return err;
2043}
2044
2053static BOOL
2055{
2056 DWORD dhcp;
2057 DWORD size = sizeof(dhcp);
2058 LSTATUS err;
2059
2060 err = RegGetValueA(key, NULL, "EnableDHCP", RRF_RT_REG_DWORD, NULL, (PBYTE)&dhcp, &size);
2061 if (err != NO_ERROR)
2062 {
2063 MsgToEventLog(M_SYSERR, L"%S: Could not read DHCP status (%lu)", __func__, err);
2064 return FALSE;
2065 }
2066
2067 return dhcp ? TRUE : FALSE;
2068}
2069
2078static LSTATUS
2079SetNameServerAddresses(PWSTR itf_id, const nrpt_address_t *addresses)
2080{
2081 const short families[] = { AF_INET, AF_INET6 };
2082 for (size_t i = 0; i < _countof(families); i++)
2083 {
2084 short family = families[i];
2085
2086 /* Create a comma sparated list of addresses of this family */
2087 size_t offset = 0;
2088 char addr_list[NRPT_ADDR_SIZE * NRPT_ADDR_NUM];
2089 for (int j = 0; j < NRPT_ADDR_NUM && addresses[j][0]; j++)
2090 {
2091 if ((family == AF_INET6 && strchr(addresses[j], ':') == NULL)
2092 || (family == AF_INET && strchr(addresses[j], ':') != NULL))
2093 {
2094 /* Address family doesn't match, skip this one */
2095 continue;
2096 }
2097 if (offset)
2098 {
2099 addr_list[offset++] = ',';
2100 }
2101 strcpy(addr_list + offset, addresses[j]);
2102 offset += strlen(addresses[j]);
2103 }
2104
2105 if (offset == 0)
2106 {
2107 /* No address for this family to set */
2108 continue;
2109 }
2110
2111 /* Set name server addresses */
2112 LSTATUS err = SetNameServers(itf_id, family, addr_list);
2113 if (err)
2114 {
2115 return err;
2116 }
2117 }
2118 return NO_ERROR;
2119}
2120
2131static LSTATUS
2132GetItfDnsServersV4(HKEY itf_key, PSTR addrs, PDWORD size)
2133{
2134 addrs[*size - 1] = '\0';
2135
2136 LSTATUS err;
2137 DWORD s = *size;
2138 err = RegGetValueA(itf_key, NULL, "NameServer", RRF_RT_REG_SZ, NULL, (PBYTE)addrs, &s);
2139 if (err && err != ERROR_FILE_NOT_FOUND)
2140 {
2141 *size = 0;
2142 return err;
2143 }
2144
2145 /* Try DHCP addresses if we don't have some already */
2146 if (!strchr(addrs, '.') && IsDhcpEnabled(itf_key))
2147 {
2148 s = *size;
2149 RegGetValueA(itf_key, NULL, "DhcpNameServer", RRF_RT_REG_SZ, NULL, (PBYTE)addrs, &s);
2150 if (err)
2151 {
2152 *size = 0;
2153 return err;
2154 }
2155 }
2156
2157 if (strchr(addrs, '.'))
2158 {
2159 *size = s;
2160 return NO_ERROR;
2161 }
2162
2163 *size = 0;
2164 return ERROR_FILE_NOT_FOUND;
2165}
2166
2176static LSTATUS
2177GetItfDnsServersV6(HKEY itf_key, PSTR addrs, PDWORD size)
2178{
2179 addrs[*size - 1] = '\0';
2180
2181 LSTATUS err;
2182 DWORD s = *size;
2183 err = RegGetValueA(itf_key, NULL, "NameServer", RRF_RT_REG_SZ, NULL, (PBYTE)addrs, &s);
2184 if (err && err != ERROR_FILE_NOT_FOUND)
2185 {
2186 *size = 0;
2187 return err;
2188 }
2189
2190 /* Try DHCP addresses if we don't have some already */
2191 if (!strchr(addrs, ':') && IsDhcpEnabled(itf_key))
2192 {
2193 IN6_ADDR in_addrs[8];
2194 DWORD in_addrs_size = sizeof(in_addrs);
2195 err = RegGetValueA(itf_key, NULL, "Dhcpv6DNSServers", RRF_RT_REG_BINARY, NULL,
2196 (PBYTE)in_addrs, &in_addrs_size);
2197 if (err)
2198 {
2199 *size = 0;
2200 return err;
2201 }
2202
2203 s = *size;
2204 PSTR pos = addrs;
2205 size_t in_addrs_read = in_addrs_size / sizeof(IN6_ADDR);
2206 for (size_t i = 0; i < in_addrs_read; ++i)
2207 {
2208 if (i != 0)
2209 {
2210 /* Add separator */
2211 *pos++ = ',';
2212 s--;
2213 }
2214
2215 if (inet_ntop(AF_INET6, &in_addrs[i], pos, s) != NULL)
2216 {
2217 *size = 0;
2218 return ERROR_MORE_DATA;
2219 }
2220
2221 size_t addr_len = strlen(pos);
2222 pos += addr_len;
2223 s -= (DWORD)addr_len;
2224 }
2225 s = (DWORD)strlen(addrs) + 1;
2226 }
2227
2228 if (strchr(addrs, ':'))
2229 {
2230 *size = s;
2231 return NO_ERROR;
2232 }
2233
2234 *size = 0;
2235 return ERROR_FILE_NOT_FOUND;
2236}
2237
2247static BOOL
2248ListContainsDomain(PCWSTR list, PCWSTR domain, size_t len)
2249{
2250 PCWSTR entry = list;
2251 while (entry && *entry)
2252 {
2253 PCWSTR comma = wcschr(entry, L',');
2254 size_t entry_len = comma ? (size_t)(comma - entry) : wcslen(entry);
2255 if (entry_len == len && wcsncmp(entry, domain, len) == 0)
2256 {
2257 return TRUE;
2258 }
2259 if (!comma)
2260 {
2261 break;
2262 }
2263 entry = comma + 1;
2264 }
2265 return FALSE;
2266}
2267
2290static LSTATUS
2291ConvertItfDnsDomains(PCWSTR search_domains, PWSTR domains, PDWORD size, const DWORD capacity)
2292{
2293 const size_t glyph_size = sizeof(*domains);
2294 const size_t max_len = (size_t)capacity / glyph_size;
2295
2296 /* Space required for leading dot and two terminating zeros */
2297 const size_t dot_len = 1;
2298 const size_t term_len = 2;
2299
2300 LSTATUS ret = NO_ERROR;
2301 size_t tmp_len = 0;
2302 WCHAR *tmp = malloc(capacity);
2303 if (tmp == NULL)
2304 {
2305 ret = ERROR_OUTOFMEMORY;
2306 goto done;
2307 }
2308
2309 PWCHAR tmp_pos = tmp;
2310 PCWCHAR domain = domains;
2311
2312 while (domain && *domain)
2313 {
2314 PWCHAR comma = wcschr(domain, L',');
2315 size_t domain_len = comma ? (size_t)(comma - domain) : wcslen(domain);
2316
2317 if (ListContainsDomain(search_domains, domain, domain_len))
2318 {
2319 /* Skip this domain */
2320 domain = comma ? comma + 1 : domain + domain_len;
2321 continue;
2322 }
2323
2324 /* Check for enough space to convert this domain */
2325 if (tmp_len + dot_len + domain_len + term_len > max_len)
2326 {
2327 /* Domain doesn't fit, bad luck if it's the first one */
2328 *tmp_pos = L'\0';
2329 if (tmp_len > 0)
2330 {
2331 tmp_len += 1;
2332 }
2333 ret = ERROR_MORE_DATA;
2334 goto done;
2335 }
2336
2337 /* Write leading dot and domain into tmp buffer */
2338 *tmp_pos++ = L'.';
2339 wcsncpy(tmp_pos, domain, domain_len);
2340 tmp_pos += domain_len;
2341 *tmp_pos++ = L'\0';
2342 tmp_len += dot_len + domain_len + 1;
2343
2344 domain = comma ? comma + 1 : domain + domain_len;
2345 }
2346
2347 if (tmp_len == 0)
2348 {
2349 ret = ERROR_FILE_NOT_FOUND;
2350 goto done;
2351 }
2352
2353 /* REG_MULTI_SZ second zero terminator */
2354 *tmp_pos = L'\0';
2355 tmp_len += 1;
2356
2357done:
2358 if (tmp)
2359 {
2360 wmemcpy(domains, tmp, tmp_len);
2361 free(tmp);
2362 }
2363 *size = (DWORD)(tmp_len * glyph_size);
2364 return ret;
2365}
2366
2388static LSTATUS
2389GetItfDnsDomains(HKEY itf, PCWSTR search_domains, PWSTR domains, PDWORD size)
2390{
2391 if (domains == NULL || size == NULL || *size == 0)
2392 {
2393 return ERROR_INVALID_PARAMETER;
2394 }
2395
2396 LSTATUS err = ERROR_FILE_NOT_FOUND;
2397 const DWORD buf_size = *size;
2398 const DWORD glyph_size = sizeof(*domains);
2399 PWSTR values[] = { L"SearchList", L"Domain", L"DhcpDomainSearchList", L"DhcpDomain", NULL };
2400
2401 for (int i = 0; values[i]; i++)
2402 {
2403 *size = buf_size;
2404 err = RegGetValueW(itf, NULL, values[i], RRF_RT_REG_SZ, NULL, (PBYTE)domains, size);
2405 if (!err && *size > glyph_size && domains[(*size / glyph_size) - 1] == '\0' && wcschr(domains, '.'))
2406 {
2407 return ConvertItfDnsDomains(search_domains, domains, size, buf_size);
2408 }
2409 }
2410
2411 *size = 0;
2412 return err;
2413}
2414
2423static BOOL
2425{
2426 GUID iid;
2427 BOOL res = FALSE;
2428 MIB_IF_ROW2 itf_row;
2429
2430 /* Get GUID from string */
2431 if (IIDFromString(iid_str, &iid) != S_OK)
2432 {
2433 MsgToEventLog(M_SYSERR, L"%S: could not convert interface %s GUID string", __func__,
2434 iid_str);
2435 goto out;
2436 }
2437
2438 /* Get LUID from GUID */
2439 if (ConvertInterfaceGuidToLuid(&iid, &itf_row.InterfaceLuid) != NO_ERROR)
2440 {
2441 goto out;
2442 }
2443
2444 /* Look up interface status */
2445 if (GetIfEntry2(&itf_row) != NO_ERROR)
2446 {
2447 MsgToEventLog(M_SYSERR, L"%S: could not get interface %s status", __func__, iid_str);
2448 goto out;
2449 }
2450
2451 if (itf_row.MediaConnectState == MediaConnectStateConnected
2452 && itf_row.OperStatus == IfOperStatusUp)
2453 {
2454 res = TRUE;
2455 }
2456
2457out:
2458 return res;
2459}
2460
2470static void
2471GetNrptExcludeData(PCWSTR search_domains, nrpt_exclude_data_t *data, size_t data_size)
2472{
2473 HKEY v4_itfs = INVALID_HANDLE_VALUE;
2474 HKEY v6_itfs = INVALID_HANDLE_VALUE;
2475
2476 if (!GetInterfacesKey(AF_INET, &v4_itfs) || !GetInterfacesKey(AF_INET6, &v6_itfs))
2477 {
2478 goto out;
2479 }
2480
2481 size_t i = 0;
2482 DWORD enum_index = 0;
2483 while (i < data_size)
2484 {
2485 WCHAR itf_guid[MAX_PATH];
2486 DWORD itf_guid_len = _countof(itf_guid);
2487 LSTATUS err =
2488 RegEnumKeyExW(v4_itfs, enum_index++, itf_guid, &itf_guid_len, NULL, NULL, NULL, NULL);
2489 if (err)
2490 {
2491 if (err != ERROR_NO_MORE_ITEMS)
2492 {
2493 MsgToEventLog(M_SYSERR, L"%S: could not enumerate interfaces (%lu)", __func__, err);
2494 }
2495 goto out;
2496 }
2497
2498 /* Ignore interfaces that are not connected or disabled */
2499 if (!IsInterfaceConnected(itf_guid))
2500 {
2501 continue;
2502 }
2503
2504 HKEY v4_itf;
2505 if (RegOpenKeyExW(v4_itfs, itf_guid, 0, KEY_READ, &v4_itf) != NO_ERROR)
2506 {
2507 MsgToEventLog(M_SYSERR, L"%S: could not open interface %s v4 registry key", __func__,
2508 itf_guid);
2509 goto out;
2510 }
2511
2512 /* Get the DNS domain(s) for exclude routing */
2513 data[i].domains_size = sizeof(data[0].domains);
2514 memset(data[i].domains, 0, data[i].domains_size);
2515 err = GetItfDnsDomains(v4_itf, search_domains, data[i].domains, &data[i].domains_size);
2516 if (err)
2517 {
2518 if (err != ERROR_FILE_NOT_FOUND)
2519 {
2520 MsgToEventLog(M_SYSERR, L"%S: could not read interface %s domain suffix", __func__,
2521 itf_guid);
2522 }
2523 goto next_itf;
2524 }
2525
2526 /* Get the IPv4 DNS servers */
2527 DWORD v4_addrs_size = sizeof(data[0].addresses);
2528 err = GetItfDnsServersV4(v4_itf, data[i].addresses, &v4_addrs_size);
2529 if (err && err != ERROR_FILE_NOT_FOUND)
2530 {
2531 MsgToEventLog(M_SYSERR, L"%S: could not read interface %s v4 name servers (%ld)",
2532 __func__, itf_guid, err);
2533 goto next_itf;
2534 }
2535
2536 /* Get the IPv6 DNS servers, if there's space left */
2537 PSTR v6_addrs = data[i].addresses + v4_addrs_size;
2538 DWORD v6_addrs_size = sizeof(data[0].addresses) - v4_addrs_size;
2539 if (v6_addrs_size > NRPT_ADDR_SIZE)
2540 {
2541 HKEY v6_itf;
2542 if (RegOpenKeyExW(v6_itfs, itf_guid, 0, KEY_READ, &v6_itf) != NO_ERROR)
2543 {
2544 MsgToEventLog(M_SYSERR, L"%S: could not open interface %s v6 registry key",
2545 __func__, itf_guid);
2546 goto next_itf;
2547 }
2548 err = GetItfDnsServersV6(v6_itf, v6_addrs, &v6_addrs_size);
2549 RegCloseKey(v6_itf);
2550 if (err && err != ERROR_FILE_NOT_FOUND)
2551 {
2552 MsgToEventLog(M_SYSERR, L"%S: could not read interface %s v6 name servers (%ld)",
2553 __func__, itf_guid, err);
2554 goto next_itf;
2555 }
2556 }
2557
2558 if (v4_addrs_size || v6_addrs_size)
2559 {
2560 /* Replace delimiters with semicolons, as required by NRPT */
2561 for (size_t j = 0; j < sizeof(data[0].addresses) && data[i].addresses[j]; j++)
2562 {
2563 if (data[i].addresses[j] == ',' || data[i].addresses[j] == ' ')
2564 {
2565 data[i].addresses[j] = ';';
2566 }
2567 }
2568 ++i;
2569 }
2570
2571next_itf:
2572 RegCloseKey(v4_itf);
2573 }
2574
2575out:
2576 RegCloseKey(v6_itfs);
2577 RegCloseKey(v4_itfs);
2578}
2579
2592static DWORD
2593SetNrptRule(HKEY nrpt_key, PCWSTR subkey, PCSTR address, PCWSTR domains, DWORD dom_size,
2594 BOOL dnssec)
2595{
2596 /* Create rule subkey */
2597 DWORD err = NO_ERROR;
2598 HKEY rule_key;
2599 err = RegCreateKeyExW(nrpt_key, subkey, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &rule_key, NULL);
2600 if (err)
2601 {
2602 return err;
2603 }
2604
2605 /* Set name(s) for DNS routing */
2606 err = RegSetValueExW(rule_key, L"Name", 0, REG_MULTI_SZ, (PBYTE)domains, dom_size);
2607 if (err)
2608 {
2609 goto out;
2610 }
2611
2612 /* Set DNS Server address */
2613 err = RegSetValueExA(rule_key, "GenericDNSServers", 0, REG_SZ, (PBYTE)address,
2614 (DWORD)strlen(address) + 1);
2615 if (err)
2616 {
2617 goto out;
2618 }
2619
2620 DWORD reg_val;
2621 /* Set DNSSEC if required */
2622 if (dnssec)
2623 {
2624 reg_val = 1;
2625 err = RegSetValueExA(rule_key, "DNSSECValidationRequired", 0, REG_DWORD, (PBYTE)&reg_val,
2626 sizeof(reg_val));
2627 if (err)
2628 {
2629 goto out;
2630 }
2631
2632 reg_val = 0;
2633 err = RegSetValueExA(rule_key, "DNSSECQueryIPSECRequired", 0, REG_DWORD, (PBYTE)&reg_val,
2634 sizeof(reg_val));
2635 if (err)
2636 {
2637 goto out;
2638 }
2639
2640 reg_val = 0;
2641 err = RegSetValueExA(rule_key, "DNSSECQueryIPSECEncryption", 0, REG_DWORD, (PBYTE)&reg_val,
2642 sizeof(reg_val));
2643 if (err)
2644 {
2645 goto out;
2646 }
2647 }
2648
2649 /* Set NRPT config options */
2650 reg_val = dnssec ? 0x0000000A : 0x00000008;
2651 err = RegSetValueExA(rule_key, "ConfigOptions", 0, REG_DWORD, (const PBYTE)&reg_val,
2652 sizeof(reg_val));
2653 if (err)
2654 {
2655 goto out;
2656 }
2657
2658 /* Mandatory NRPT version */
2659 reg_val = 2;
2660 err = RegSetValueExA(rule_key, "Version", 0, REG_DWORD, (const PBYTE)&reg_val, sizeof(reg_val));
2661 if (err)
2662 {
2663 goto out;
2664 }
2665
2666out:
2667 if (err)
2668 {
2669 RegDeleteKeyW(nrpt_key, subkey);
2670 }
2671 RegCloseKey(rule_key);
2672 return err;
2673}
2674
2684static void
2685SetNrptExcludeRules(HKEY nrpt_key, DWORD ovpn_pid, PCWSTR search_domains)
2686{
2687 nrpt_exclude_data_t data[8]; /* data from up to 8 interfaces */
2688 memset(data, 0, sizeof(data));
2689 GetNrptExcludeData(search_domains, data, _countof(data));
2690
2691 unsigned n = 0;
2692 for (size_t i = 0; i < _countof(data); ++i)
2693 {
2694 const nrpt_exclude_data_t *d = &data[i];
2695 if (d->domains_size == 0)
2696 {
2697 break;
2698 }
2699
2700 DWORD err;
2701 WCHAR subkey[48];
2702 swprintf(subkey, _countof(subkey), L"OpenVPNDNSRoutingX-%02x-%lu", ++n, ovpn_pid);
2703 err = SetNrptRule(nrpt_key, subkey, d->addresses, d->domains, d->domains_size, FALSE);
2704 if (err)
2705 {
2706 MsgToEventLog(M_ERR, L"%S: failed to set rule %s (%lu)", __func__, subkey, err);
2707 }
2708 }
2709}
2710
2723static DWORD
2724SetNrptRules(HKEY nrpt_key, const nrpt_address_t *addresses, const char *domains,
2725 const char *search_domains, BOOL dnssec, DWORD ovpn_pid)
2726{
2727 DWORD err = NO_ERROR;
2728 PWSTR wide_domains = L".\0"; /* DNS route everything by default */
2729 DWORD dom_size = 6;
2730
2731 /* Prepare DNS routing domains / split DNS */
2732 if (domains[0])
2733 {
2734 size_t domains_len = strlen(domains);
2735 dom_size = (DWORD)domains_len + 2; /* len + the trailing NULs */
2736
2737 wide_domains = utf8to16_size(domains, dom_size);
2738 if (!wide_domains)
2739 {
2740 return ERROR_OUTOFMEMORY;
2741 }
2742 domains_len = wcslen(wide_domains);
2743 dom_size = (DWORD)(domains_len + 2) * sizeof(*wide_domains);
2744
2745 /* Make a MULTI_SZ from a comma separated list */
2746 for (size_t i = 0; i < domains_len; ++i)
2747 {
2748 if (wide_domains[i] == ',')
2749 {
2750 wide_domains[i] = 0;
2751 }
2752 }
2753 }
2754 else
2755 {
2756 PWSTR wide_search_domains;
2757 wide_search_domains = utf8to16(search_domains);
2758 if (!wide_search_domains)
2759 {
2760 return ERROR_OUTOFMEMORY;
2761 }
2762 SetNrptExcludeRules(nrpt_key, ovpn_pid, wide_search_domains);
2763 free(wide_search_domains);
2764 }
2765
2766 if (addresses[0][0])
2767 {
2768 /* Create address string list */
2769 CHAR addr_list[NRPT_ADDR_NUM * NRPT_ADDR_SIZE];
2770 PSTR pos = addr_list;
2771 for (int i = 0; i < NRPT_ADDR_NUM && addresses[i][0]; ++i)
2772 {
2773 if (i != 0)
2774 {
2775 *pos++ = ';';
2776 }
2777 strcpy(pos, addresses[i]);
2778 pos += strlen(pos);
2779 }
2780
2781 WCHAR subkey[MAX_PATH];
2782 swprintf(subkey, _countof(subkey), L"OpenVPNDNSRouting-%lu", ovpn_pid);
2783 err = SetNrptRule(nrpt_key, subkey, addr_list, wide_domains, dom_size, dnssec);
2784 if (err)
2785 {
2786 MsgToEventLog(M_ERR, L"%S: failed to set rule %s (%lu)", __func__, subkey, err);
2787 }
2788 }
2789
2790 if (domains[0])
2791 {
2792 free(wide_domains);
2793 }
2794 return err;
2795}
2796
2805static LSTATUS
2806OpenNrptBaseKey(PHKEY key, PBOOL gpol)
2807{
2808 /*
2809 * Registry keys Name Service Policy Table (NRPT) rules can be stored at.
2810 * When the group policy key exists, NRPT rules must be placed there.
2811 * It is created when NRPT rules are pushed via group policy and it
2812 * remains in the registry even if the last GP-NRPT rule is deleted.
2813 */
2814 static PCSTR gpol_key = "SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient\\DnsPolicyConfig";
2815 static PCSTR sys_key =
2816 "SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters\\DnsPolicyConfig";
2817
2818 HKEY nrpt;
2819 *gpol = TRUE;
2820 LSTATUS err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, gpol_key, 0, KEY_ALL_ACCESS, &nrpt);
2821 if (err == ERROR_FILE_NOT_FOUND)
2822 {
2823 *gpol = FALSE;
2824 err = RegCreateKeyExA(HKEY_LOCAL_MACHINE, sys_key, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &nrpt,
2825 NULL);
2826 if (err)
2827 {
2828 nrpt = INVALID_HANDLE_VALUE;
2829 }
2830 }
2831 *key = nrpt;
2832 return err;
2833}
2834
2846static BOOL
2847DeleteNrptRules(DWORD pid, PBOOL gpol)
2848{
2849 HKEY key;
2850 LSTATUS err = OpenNrptBaseKey(&key, gpol);
2851 if (err)
2852 {
2853 MsgToEventLog(M_SYSERR, L"%S: could not open NRPT base key (%lu)", __func__, err);
2854 return FALSE;
2855 }
2856
2857 /* PID suffix string to compare against later */
2858 WCHAR pid_str[16];
2859 size_t pidlen = 0;
2860 if (pid)
2861 {
2862 swprintf(pid_str, _countof(pid_str), L"-%lu", pid);
2863 pidlen = wcslen(pid_str);
2864 }
2865
2866 int deleted = 0;
2867 DWORD enum_index = 0;
2868 while (TRUE)
2869 {
2870 WCHAR name[MAX_PATH];
2871 DWORD namelen = _countof(name);
2872 err = RegEnumKeyExW(key, enum_index++, name, &namelen, NULL, NULL, NULL, NULL);
2873 if (err)
2874 {
2875 if (err != ERROR_NO_MORE_ITEMS)
2876 {
2877 MsgToEventLog(M_SYSERR, L"%S: could not enumerate NRPT rules (%lu)", __func__, err);
2878 }
2879 break;
2880 }
2881
2882 /* Keep rule if name doesn't match */
2883 if (wcsncmp(name, L"OpenVPNDNSRouting", 17) != 0
2884 || (pid && wcsncmp(name + namelen - pidlen, pid_str, pidlen) != 0))
2885 {
2886 continue;
2887 }
2888
2889 if (RegDeleteKeyW(key, name) == NO_ERROR)
2890 {
2891 enum_index--;
2892 deleted++;
2893 }
2894 }
2895
2896 RegCloseKey(key);
2897 return deleted ? TRUE : FALSE;
2898}
2899
2905static void
2906UndoNrptRules(DWORD ovpn_pid)
2907{
2908 BOOL gpol;
2909 if (DeleteNrptRules(ovpn_pid, &gpol))
2910 {
2911 ApplyDnsSettings(gpol);
2912 }
2913}
2914
2926static DWORD
2928{
2929 /*
2930 * Use a non-const reference with limited scope to
2931 * enforce null-termination of strings from client
2932 */
2933 {
2935 msgptr->iface.name[_countof(msg->iface.name) - 1] = '\0';
2936 msgptr->search_domains[_countof(msg->search_domains) - 1] = '\0';
2937 msgptr->resolve_domains[_countof(msg->resolve_domains) - 1] = '\0';
2938 for (size_t i = 0; i < NRPT_ADDR_NUM; ++i)
2939 {
2940 msgptr->addresses[i][_countof(msg->addresses[0]) - 1] = '\0';
2941 }
2942 }
2943
2944 /* Make sure we have the VPN interface name */
2945 if (msg->iface.name[0] == 0)
2946 {
2947 return ERROR_MESSAGE_DATA;
2948 }
2949
2950 /* Some sanity checks on the add message data */
2951 if (msg->header.type == msg_add_nrpt_cfg)
2952 {
2953 /* At least one name server address is set */
2954 if (msg->addresses[0][0] == 0)
2955 {
2956 return ERROR_MESSAGE_DATA;
2957 }
2958 /* Resolve domains are double zero terminated (MULTI_SZ) */
2959 const char *rdom = msg->resolve_domains;
2960 size_t rdom_size = sizeof(msg->resolve_domains);
2961 size_t rdom_len = strlen(rdom);
2962 if (rdom_len && (rdom_len + 1 >= rdom_size || rdom[rdom_len + 1] != 0))
2963 {
2964 return ERROR_MESSAGE_DATA;
2965 }
2966 }
2967
2968 BOOL gpol_nrpt = FALSE;
2969 BOOL gpol_list = FALSE;
2970
2971 WCHAR iid[64];
2972 DWORD iid_err = InterfaceIdString(msg->iface.name, iid, _countof(iid));
2973 if (iid_err)
2974 {
2975 return iid_err;
2976 }
2977
2978 /* Delete previously set values for this instance first, if any */
2979 PDWORD undo_pid = RemoveListItem(&(*lists)[undo_nrpt], CmpAny, NULL);
2980 if (undo_pid)
2981 {
2982 if (*undo_pid != ovpn_pid)
2983 {
2985 L"%S: PID stored for undo doesn't match: %lu vs %lu. "
2986 "This is likely an error. Cleaning up anyway.",
2987 __func__, *undo_pid, ovpn_pid);
2988 }
2989 DeleteNrptRules(*undo_pid, &gpol_nrpt);
2990 free(undo_pid);
2991
2992 ResetNameServers(iid, AF_INET);
2993 ResetNameServers(iid, AF_INET6);
2994 }
2995 SetDnsSearchDomains(msg->iface.name, NULL, &gpol_list, lists);
2996
2997 if (msg->header.type == msg_del_nrpt_cfg)
2998 {
2999 ApplyDnsSettings(gpol_nrpt || gpol_list);
3000 return NO_ERROR; /* Done dealing with del message */
3001 }
3002
3003 HKEY key;
3004 LSTATUS err = OpenNrptBaseKey(&key, &gpol_nrpt);
3005 if (err)
3006 {
3007 goto out;
3008 }
3009
3010 /* Add undo information first in case there's no heap left */
3011 PDWORD pid = malloc(sizeof(ovpn_pid));
3012 if (!pid)
3013 {
3014 err = ERROR_OUTOFMEMORY;
3015 goto out;
3016 }
3017 *pid = ovpn_pid;
3018 if (AddListItem(&(*lists)[undo_nrpt], pid))
3019 {
3020 err = ERROR_OUTOFMEMORY;
3021 free(pid);
3022 goto out;
3023 }
3024
3025 /* Set NRPT rules */
3026 BOOL dnssec = (msg->flags & nrpt_dnssec) != 0;
3027 err = SetNrptRules(key, msg->addresses, msg->resolve_domains, msg->search_domains, dnssec,
3028 ovpn_pid);
3029 if (err)
3030 {
3031 goto out;
3032 }
3033
3034 /*
3035 * Set DNS on the adapter for search domains to be considered.
3036 * If split DNS is configured, do this only when search domains
3037 * are given, so that look-ups for other domains do not go over
3038 * the VPN all the time.
3039 */
3040 if (msg->search_domains[0] || !msg->resolve_domains[0])
3041 {
3042 err = SetNameServerAddresses(iid, msg->addresses);
3043 if (err)
3044 {
3045 goto out;
3046 }
3047 }
3048
3049 /* Set search domains, if any */
3050 if (msg->search_domains[0])
3051 {
3052 err = SetDnsSearchDomains(msg->iface.name, msg->search_domains, &gpol_list, lists);
3053 }
3054
3055 ApplyDnsSettings(gpol_nrpt || gpol_list);
3056
3057out:
3058 return err;
3059}
3060
3061static DWORD
3063{
3064 DWORD err = NO_ERROR;
3065 wchar_t addr[16]; /* large enough to hold string representation of an ipv4 */
3066 unsigned int addr_len = msg->addr_len;
3067
3068 /* sanity check */
3069 if (addr_len > _countof(msg->addr))
3070 {
3071 addr_len = _countof(msg->addr);
3072 }
3073
3074 if (!msg->iface.index) /* interface index is required */
3075 {
3076 return ERROR_MESSAGE_DATA;
3077 }
3078
3079 /* We delete all current addresses before adding any
3080 * OR if the message type is del_wins_cfg
3081 */
3082 if (addr_len > 0 || msg->header.type == msg_del_wins_cfg)
3083 {
3084 err = netsh_wins_cmd(L"delete", msg->iface.index, NULL);
3085 if (err)
3086 {
3087 goto out;
3088 }
3089 free(RemoveListItem(&(*lists)[undo_wins], CmpAny, NULL));
3090 }
3091
3092 if (addr_len == 0 || msg->header.type == msg_del_wins_cfg)
3093 {
3094 goto out; /* job done */
3095 }
3096
3097 for (unsigned int i = 0; i < addr_len; ++i)
3098 {
3099 RtlIpv4AddressToStringW(&msg->addr[i].ipv4, addr);
3100 err = netsh_wins_cmd(i == 0 ? L"set" : L"add", msg->iface.index, addr);
3101 if (i == 0 && err)
3102 {
3103 goto out;
3104 }
3105 /* We do not check for duplicate addresses, so any error in adding
3106 * additional addresses is ignored.
3107 */
3108 }
3109
3110 PDWORD if_index = malloc(sizeof(msg->iface.index));
3111 if (if_index)
3112 {
3113 *if_index = msg->iface.index;
3114 }
3115
3116 if (!if_index || AddListItem(&(*lists)[undo_wins], if_index))
3117 {
3118 free(if_index);
3119 netsh_wins_cmd(L"delete", msg->iface.index, NULL);
3120 err = ERROR_OUTOFMEMORY;
3121 goto out;
3122 }
3123
3124 err = 0;
3125
3126out:
3127 return err;
3128}
3129
3130static DWORD
3132{
3133 DWORD err = 0;
3134 DWORD timeout = 5000; /* in milli seconds */
3135 wchar_t argv0[MAX_PATH];
3136
3137 /* Path of netsh */
3138 swprintf(argv0, _countof(argv0), L"%ls\\%ls", get_win_sys_path(), L"netsh.exe");
3139
3140 /* cmd template:
3141 * netsh interface ipv4 set address name=$if_index source=dhcp
3142 */
3143 const wchar_t *fmt = L"netsh interface ipv4 set address name=\"%lu\" source=dhcp";
3144
3145 /* max cmdline length in wchars -- include room for if index:
3146 * 10 chars for 32 bit int in decimal and +1 for NUL
3147 */
3148 size_t ncmdline = wcslen(fmt) + 10 + 1;
3149 wchar_t *cmdline = malloc(ncmdline * sizeof(wchar_t));
3150 if (!cmdline)
3151 {
3152 err = ERROR_OUTOFMEMORY;
3153 return err;
3154 }
3155
3156 swprintf(cmdline, ncmdline, fmt, dhcp->iface.index);
3157
3158 err = ExecCommand(argv0, cmdline, timeout);
3159
3160 /* Note: This could fail if dhcp is already enabled, so the caller
3161 * may not want to treat errors as FATAL.
3162 */
3163
3164 free(cmdline);
3165 return err;
3166}
3167
3168static DWORD
3170{
3171 DWORD err = 0;
3172 MIB_IPINTERFACE_ROW ipiface;
3173 InitializeIpInterfaceEntry(&ipiface);
3174 ipiface.Family = mtu->family;
3175 ipiface.InterfaceIndex = mtu->iface.index;
3176 err = GetIpInterfaceEntry(&ipiface);
3177 if (err != NO_ERROR)
3178 {
3179 return err;
3180 }
3181 if (mtu->family == AF_INET)
3182 {
3183 ipiface.SitePrefixLength = 0;
3184 }
3185 ipiface.NlMtu = mtu->mtu;
3186
3187 err = SetIpInterfaceEntry(&ipiface);
3188 return err;
3189}
3190
3198static DWORD
3200{
3201 const WCHAR *hwid;
3202
3203 switch (msg->adapter_type)
3204 {
3205 case ADAPTER_TYPE_DCO:
3206 hwid = L"ovpn-dco";
3207 break;
3208
3209 case ADAPTER_TYPE_TAP:
3210 hwid = L"root\\tap0901";
3211 break;
3212
3213 default:
3214 return ERROR_INVALID_PARAMETER;
3215 }
3216
3217 WCHAR cmd[MAX_PATH];
3218 WCHAR args[MAX_PATH];
3219
3220 if (swprintf_s(cmd, _countof(cmd), L"%s\\tapctl.exe", settings.bin_dir) < 0)
3221 {
3222 return ERROR_BUFFER_OVERFLOW;
3223 }
3224
3225 if (swprintf_s(args, _countof(args), L"tapctl create --hwid %s", hwid) < 0)
3226 {
3227 return ERROR_BUFFER_OVERFLOW;
3228 }
3229
3230 return ExecCommand(cmd, args, 10000);
3231}
3232
3233static VOID
3234HandleMessage(HANDLE pipe, PPROCESS_INFORMATION proc_info, DWORD bytes, DWORD count,
3235 LPHANDLE events, undo_lists_t *lists)
3236{
3238 ack_message_t ack = {
3239 .header = { .type = msg_acknowledgement, .size = sizeof(ack), .message_id = -1 },
3240 .error_number = ERROR_MESSAGE_DATA
3241 };
3242
3243 DWORD read = ReadPipeAsync(pipe, &msg, bytes, count, events);
3244 if (read != bytes || read < sizeof(msg.header) || read != msg.header.size)
3245 {
3246 goto out;
3247 }
3248
3249 ack.header.message_id = msg.header.message_id;
3250
3251 switch (msg.header.type)
3252 {
3253 case msg_add_address:
3254 case msg_del_address:
3255 if (msg.header.size == sizeof(msg.address))
3256 {
3257 ack.error_number = HandleAddressMessage(&msg.address, lists);
3258 }
3259 break;
3260
3261 case msg_add_route:
3262 case msg_del_route:
3263 if (msg.header.size == sizeof(msg.route))
3264 {
3265 ack.error_number = HandleRouteMessage(&msg.route, lists);
3266 }
3267 break;
3268
3270 if (msg.header.size == sizeof(msg.flush_neighbors))
3271 {
3272 ack.error_number = HandleFlushNeighborsMessage(&msg.flush_neighbors);
3273 }
3274 break;
3275
3276 case msg_add_wfp_block:
3277 case msg_del_wfp_block:
3278 if (msg.header.size == sizeof(msg.wfp_block))
3279 {
3280 ack.error_number = HandleWfpBlockMessage(&msg.wfp_block, lists);
3281 }
3282 break;
3283
3284 case msg_register_dns:
3286 break;
3287
3288 case msg_add_dns_cfg:
3289 case msg_del_dns_cfg:
3290 ack.error_number = HandleDNSConfigMessage(&msg.dns, lists);
3291 break;
3292
3293 case msg_add_nrpt_cfg:
3294 case msg_del_nrpt_cfg:
3295 {
3296 DWORD ovpn_pid = proc_info->dwProcessId;
3297 ack.error_number = HandleDNSConfigNrptMessage(&msg.nrpt_dns, ovpn_pid, lists);
3298 }
3299 break;
3300
3301 case msg_add_wins_cfg:
3302 case msg_del_wins_cfg:
3303 ack.error_number = HandleWINSConfigMessage(&msg.wins, lists);
3304 break;
3305
3306 case msg_enable_dhcp:
3307 if (msg.header.size == sizeof(msg.dhcp))
3308 {
3310 }
3311 break;
3312
3313 case msg_set_mtu:
3314 if (msg.header.size == sizeof(msg.mtu))
3315 {
3316 ack.error_number = HandleMTUMessage(&msg.mtu);
3317 }
3318 break;
3319
3320 case msg_create_adapter:
3321 if (msg.header.size == sizeof(msg.create_adapter))
3322 {
3323 ack.error_number = HandleCreateAdapterMessage(&msg.create_adapter);
3324 }
3325 break;
3326
3327 default:
3329 MsgToEventLog(MSG_FLAGS_ERROR, L"Unknown message type %d", msg.header.type);
3330 break;
3331 }
3332
3333out:
3334 WritePipeAsync(pipe, &ack, sizeof(ack), count, events);
3335}
3336
3337
3338static VOID
3340{
3341 undo_type_t type;
3342 wfp_block_data_t *interface_data;
3343 for (type = 0; type < _undo_type_max; type++)
3344 {
3345 list_item_t **pnext = &(*lists)[type];
3346 while (*pnext)
3347 {
3348 list_item_t *item = *pnext;
3349 switch (type)
3350 {
3351 case address:
3352 DeleteAddress(item->data);
3353 break;
3354
3355 case route:
3356 DeleteRoute(item->data);
3357 break;
3358
3359 case undo_dns4:
3360 ResetNameServers(item->data, AF_INET);
3361 break;
3362
3363 case undo_dns6:
3364 ResetNameServers(item->data, AF_INET6);
3365 break;
3366
3367 case undo_nrpt:
3368 UndoNrptRules(*(PDWORD)item->data);
3369 break;
3370
3371 case undo_domains:
3373 break;
3374
3375 case undo_wins:
3376 netsh_wins_cmd(L"delete", *(PDWORD)item->data, NULL);
3377 break;
3378
3379 case wfp_block:
3380 interface_data = (wfp_block_data_t *)(item->data);
3381 delete_wfp_block_filters(interface_data->engine);
3382 if (interface_data->metric_v4 >= 0)
3383 {
3384 set_interface_metric(interface_data->index, AF_INET,
3385 interface_data->metric_v4);
3386 }
3387 if (interface_data->metric_v6 >= 0)
3388 {
3389 set_interface_metric(interface_data->index, AF_INET6,
3390 interface_data->metric_v6);
3391 }
3392 break;
3393
3394 case _undo_type_max:
3395 /* unreachable */
3396 break;
3397 }
3398
3399 /* Remove from the list and free memory */
3400 *pnext = item->next;
3401 free(item->data);
3402 free(item);
3403 }
3404 }
3405}
3406
3407static DWORD WINAPI
3408RunOpenvpn(LPVOID p)
3409{
3410 HANDLE pipe = p;
3411 HANDLE ovpn_pipe = NULL, svc_pipe = NULL;
3412 PTOKEN_USER svc_user = NULL, ovpn_user = NULL;
3413 HANDLE svc_token = NULL, imp_token = NULL, pri_token = NULL;
3414 HANDLE stdin_read = NULL, stdin_write = NULL;
3415 HANDLE stdout_write = NULL;
3416 DWORD pipe_mode, len, exit_code = 0;
3417 STARTUP_DATA sud = { 0, 0, 0 };
3418 STARTUPINFOW startup_info;
3419 PROCESS_INFORMATION proc_info;
3420 LPVOID user_env = NULL;
3421 WCHAR ovpn_pipe_name[256]; /* The entire pipe name string can be up to 256 characters long
3422 according to MSDN. */
3423 LPCWSTR exe_path;
3424 WCHAR *cmdline = NULL;
3425 size_t cmdline_size;
3426 undo_lists_t undo_lists;
3427 WCHAR errmsg[512] = L"";
3428 BOOL flush_pipe = TRUE;
3429
3430 SECURITY_ATTRIBUTES inheritable = { .nLength = sizeof(inheritable),
3431 .lpSecurityDescriptor = NULL,
3432 .bInheritHandle = TRUE };
3433
3434 PACL ovpn_dacl;
3435 EXPLICIT_ACCESS ea[2];
3436 SECURITY_DESCRIPTOR ovpn_sd;
3437 SECURITY_ATTRIBUTES ovpn_sa = { .nLength = sizeof(ovpn_sa),
3438 .lpSecurityDescriptor = &ovpn_sd,
3439 .bInheritHandle = FALSE };
3440
3441 ZeroMemory(&ea, sizeof(ea));
3442 ZeroMemory(&startup_info, sizeof(startup_info));
3443 ZeroMemory(&undo_lists, sizeof(undo_lists));
3444 ZeroMemory(&proc_info, sizeof(proc_info));
3445
3446 if (!GetStartupData(pipe, &sud))
3447 {
3448 flush_pipe = FALSE; /* client did not provide startup data */
3449 goto out;
3450 }
3451
3452 if (!InitializeSecurityDescriptor(&ovpn_sd, SECURITY_DESCRIPTOR_REVISION))
3453 {
3454 ReturnLastError(pipe, L"InitializeSecurityDescriptor");
3455 goto out;
3456 }
3457
3458 /* Get SID of user the service is running under */
3459 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &svc_token))
3460 {
3461 ReturnLastError(pipe, L"OpenProcessToken");
3462 goto out;
3463 }
3464 len = 0;
3465 while (!GetTokenInformation(svc_token, TokenUser, svc_user, len, &len))
3466 {
3467 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
3468 {
3469 ReturnLastError(pipe, L"GetTokenInformation (service token)");
3470 goto out;
3471 }
3472 free(svc_user);
3473 svc_user = malloc(len);
3474 if (svc_user == NULL)
3475 {
3476 ReturnLastError(pipe, L"malloc (service token user)");
3477 goto out;
3478 }
3479 }
3480 if (!IsValidSid(svc_user->User.Sid))
3481 {
3482 ReturnLastError(pipe, L"IsValidSid (service token user)");
3483 goto out;
3484 }
3485
3486 if (!ImpersonateNamedPipeClient(pipe))
3487 {
3488 ReturnLastError(pipe, L"ImpersonateNamedPipeClient");
3489 goto out;
3490 }
3491 if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &imp_token))
3492 {
3493 ReturnLastError(pipe, L"OpenThreadToken");
3494 goto out;
3495 }
3496 len = 0;
3497 while (!GetTokenInformation(imp_token, TokenUser, ovpn_user, len, &len))
3498 {
3499 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
3500 {
3501 ReturnLastError(pipe, L"GetTokenInformation (impersonation token)");
3502 goto out;
3503 }
3504 free(ovpn_user);
3505 ovpn_user = malloc(len);
3506 if (ovpn_user == NULL)
3507 {
3508 ReturnLastError(pipe, L"malloc (impersonation token user)");
3509 goto out;
3510 }
3511 }
3512 if (!IsValidSid(ovpn_user->User.Sid))
3513 {
3514 ReturnLastError(pipe, L"IsValidSid (impersonation token user)");
3515 goto out;
3516 }
3517
3518 /*
3519 * Only authorized users are allowed to use any command line options or
3520 * have the config file in locations other than the global config directory.
3521 *
3522 * Check options are white-listed and config is in the global directory
3523 * OR user is authorized to run any config.
3524 */
3525 if (!ValidateOptions(pipe, sud.directory, sud.options, errmsg, _countof(errmsg))
3526 && !IsAuthorizedUser(ovpn_user->User.Sid, imp_token, settings.ovpn_admin_group,
3528 {
3529 ReturnError(pipe, ERROR_STARTUP_DATA, errmsg, 1, &exit_event);
3530 goto out;
3531 }
3532
3533 /* OpenVPN process DACL entry for access by service and user */
3534 ea[0].grfAccessPermissions = SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL;
3535 ea[0].grfAccessMode = SET_ACCESS;
3536 ea[0].grfInheritance = NO_INHERITANCE;
3537 ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
3538 ea[0].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
3539 ea[0].Trustee.ptstrName = (LPWSTR)svc_user->User.Sid;
3540 ea[1].grfAccessPermissions = READ_CONTROL | PROCESS_VM_READ | SYNCHRONIZE
3541 | PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION;
3542 ea[1].grfAccessMode = SET_ACCESS;
3543 ea[1].grfInheritance = NO_INHERITANCE;
3544 ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
3545 ea[1].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
3546 ea[1].Trustee.ptstrName = (LPWSTR)ovpn_user->User.Sid;
3547
3548 /* Set owner and DACL of OpenVPN security descriptor */
3549 if (!SetSecurityDescriptorOwner(&ovpn_sd, svc_user->User.Sid, FALSE))
3550 {
3551 ReturnLastError(pipe, L"SetSecurityDescriptorOwner");
3552 goto out;
3553 }
3554 if (SetEntriesInAcl(2, ea, NULL, &ovpn_dacl) != ERROR_SUCCESS)
3555 {
3556 ReturnLastError(pipe, L"SetEntriesInAcl");
3557 goto out;
3558 }
3559 if (!SetSecurityDescriptorDacl(&ovpn_sd, TRUE, ovpn_dacl, FALSE))
3560 {
3561 ReturnLastError(pipe, L"SetSecurityDescriptorDacl");
3562 goto out;
3563 }
3564
3565 /* Create primary token from impersonation token */
3566 if (!DuplicateTokenEx(imp_token, TOKEN_ALL_ACCESS, NULL, 0, TokenPrimary, &pri_token))
3567 {
3568 ReturnLastError(pipe, L"DuplicateTokenEx");
3569 goto out;
3570 }
3571
3572 /* use /dev/null for stdout of openvpn (client should use --log for output) */
3573 stdout_write = CreateFile(_L("NUL"), GENERIC_WRITE, FILE_SHARE_WRITE, &inheritable,
3574 OPEN_EXISTING, 0, NULL);
3575 if (stdout_write == INVALID_HANDLE_VALUE)
3576 {
3577 ReturnLastError(pipe, L"CreateFile for stdout");
3578 goto out;
3579 }
3580
3581 if (!CreatePipe(&stdin_read, &stdin_write, &inheritable, 0)
3582 || !SetHandleInformation(stdin_write, HANDLE_FLAG_INHERIT, 0))
3583 {
3584 ReturnLastError(pipe, L"CreatePipe");
3585 goto out;
3586 }
3587
3588 UUID pipe_uuid;
3589 RPC_STATUS rpc_stat = UuidCreate(&pipe_uuid);
3590 if (rpc_stat != RPC_S_OK)
3591 {
3592 ReturnError(pipe, rpc_stat, L"UuidCreate", 1, &exit_event);
3593 goto out;
3594 }
3595
3596 RPC_WSTR pipe_uuid_str = NULL;
3597 rpc_stat = UuidToStringW(&pipe_uuid, &pipe_uuid_str);
3598 if (rpc_stat != RPC_S_OK)
3599 {
3600 ReturnError(pipe, rpc_stat, L"UuidToString", 1, &exit_event);
3601 goto out;
3602 }
3603 swprintf(ovpn_pipe_name, _countof(ovpn_pipe_name),
3604 L"\\\\.\\pipe\\" _L(PACKAGE) L"%ls\\service_%lu_%ls", service_instance,
3605 GetCurrentThreadId(), pipe_uuid_str);
3606 RpcStringFreeW(&pipe_uuid_str);
3607
3608 /* make a security descriptor for the named pipe with access
3609 * restricted to the user and SYSTEM
3610 */
3611
3612 SECURITY_ATTRIBUTES sa;
3613 PSECURITY_DESCRIPTOR pSD = NULL;
3614 LPCWSTR szSDDL = L"D:(A;;GA;;;SY)(A;;GA;;;OW)";
3615 if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(
3616 szSDDL, SDDL_REVISION_1, &pSD, NULL))
3617 {
3618 ReturnLastError(pipe, L"ConvertSDDL");
3619 goto out;
3620 }
3621 sa.nLength = sizeof(sa);
3622 sa.lpSecurityDescriptor = pSD;
3623 sa.bInheritHandle = FALSE;
3624
3625 ovpn_pipe = CreateNamedPipe(
3626 ovpn_pipe_name, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED,
3627 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS, 1, 128, 128, 0, &sa);
3628 if (ovpn_pipe == INVALID_HANDLE_VALUE)
3629 {
3630 ReturnLastError(pipe, L"CreateNamedPipe");
3631 goto out;
3632 }
3633
3634 svc_pipe = CreateFile(ovpn_pipe_name, GENERIC_READ | GENERIC_WRITE, 0, &inheritable,
3635 OPEN_EXISTING, 0, NULL);
3636 if (svc_pipe == INVALID_HANDLE_VALUE)
3637 {
3638 ReturnLastError(pipe, L"CreateFile");
3639 goto out;
3640 }
3641
3642 pipe_mode = PIPE_READMODE_MESSAGE;
3643 if (!SetNamedPipeHandleState(svc_pipe, &pipe_mode, NULL, NULL))
3644 {
3645 ReturnLastError(pipe, L"SetNamedPipeHandleState");
3646 goto out;
3647 }
3648
3649 cmdline_size = wcslen(sud.options) + 128;
3650 cmdline = malloc(cmdline_size * sizeof(*cmdline));
3651 if (cmdline == NULL)
3652 {
3653 ReturnLastError(pipe, L"malloc");
3654 goto out;
3655 }
3656 /* there seem to be no common printf specifier that works on all
3657 * mingw/msvc platforms without trickery, so convert to void* and use
3658 * PRIuPTR to print that as best compromise */
3659 swprintf(cmdline, cmdline_size, L"openvpn %ls --msg-channel %" PRIuPTR, sud.options,
3660 (uintptr_t)svc_pipe);
3661
3662 if (!CreateEnvironmentBlock(&user_env, imp_token, FALSE))
3663 {
3664 ReturnLastError(pipe, L"CreateEnvironmentBlock");
3665 goto out;
3666 }
3667
3668 startup_info.cb = sizeof(startup_info);
3669 startup_info.dwFlags = STARTF_USESTDHANDLES;
3670 startup_info.hStdInput = stdin_read;
3671 startup_info.hStdOutput = stdout_write;
3672 startup_info.hStdError = stdout_write;
3673
3674 exe_path = settings.exe_path;
3675
3676 /* TODO: make sure HKCU is correct or call LoadUserProfile() */
3677 if (!CreateProcessAsUserW(pri_token, exe_path, cmdline, &ovpn_sa, NULL, TRUE,
3678 settings.priority | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
3679 user_env, sud.directory, &startup_info, &proc_info))
3680 {
3681 ReturnLastError(pipe, L"CreateProcessAsUser");
3682 goto out;
3683 }
3684
3685 if (!RevertToSelf())
3686 {
3687 TerminateProcess(proc_info.hProcess, 1);
3688 ReturnLastError(pipe, L"RevertToSelf");
3689 goto out;
3690 }
3691
3692 ReturnProcessId(pipe, proc_info.dwProcessId, 1, &exit_event);
3693
3694 CloseHandleEx(&stdout_write);
3695 CloseHandleEx(&stdin_read);
3696 CloseHandleEx(&svc_pipe);
3697
3698 DWORD input_size = WideCharToMultiByte(CP_UTF8, 0, sud.std_input, -1, NULL, 0, NULL, NULL);
3699 LPSTR input = NULL;
3700 if (input_size && (input = malloc(input_size)))
3701 {
3702 DWORD written;
3703 WideCharToMultiByte(CP_UTF8, 0, sud.std_input, -1, input, input_size, NULL, NULL);
3704 WriteFile(stdin_write, input, (DWORD)strlen(input), &written, NULL);
3705 free(input);
3706 }
3707
3708 while (TRUE)
3709 {
3710 DWORD bytes = PeekNamedPipeAsync(ovpn_pipe, 1, &exit_event);
3711 if (bytes == 0)
3712 {
3713 break;
3714 }
3715
3716 if (bytes > sizeof(pipe_message_t))
3717 {
3718 /* process at the other side of the pipe is misbehaving, shut it down */
3721 L"OpenVPN process sent too large payload length to the pipe (%lu bytes), it will be terminated",
3722 bytes);
3723 break;
3724 }
3725
3726 HandleMessage(ovpn_pipe, &proc_info, bytes, 1, &exit_event, &undo_lists);
3727 }
3728
3729 WaitForSingleObject(proc_info.hProcess, IO_TIMEOUT);
3730 GetExitCodeProcess(proc_info.hProcess, &exit_code);
3731 if (exit_code == STILL_ACTIVE)
3732 {
3733 TerminateProcess(proc_info.hProcess, 1);
3734 }
3735 else if (exit_code != 0)
3736 {
3737 WCHAR buf[256];
3738 swprintf(buf, _countof(buf), L"OpenVPN exited with error: exit code = %lu", exit_code);
3740 }
3741 Undo(&undo_lists);
3742
3743out:
3744 if (flush_pipe)
3745 {
3746 FlushFileBuffers(pipe);
3747 }
3748 DisconnectNamedPipe(pipe);
3749
3750 free(ovpn_user);
3751 free(svc_user);
3752 free(cmdline);
3753 DestroyEnvironmentBlock(user_env);
3754 FreeStartupData(&sud);
3755 CloseHandleEx(&proc_info.hProcess);
3756 CloseHandleEx(&proc_info.hThread);
3757 CloseHandleEx(&stdin_read);
3758 CloseHandleEx(&stdin_write);
3759 CloseHandleEx(&stdout_write);
3760 CloseHandleEx(&svc_token);
3761 CloseHandleEx(&imp_token);
3762 CloseHandleEx(&pri_token);
3763 CloseHandleEx(&ovpn_pipe);
3764 CloseHandleEx(&svc_pipe);
3765 CloseHandleEx(&pipe);
3766
3767 return 0;
3768}
3769
3770
3771static DWORD WINAPI
3772ServiceCtrlInteractive(DWORD ctrl_code, DWORD event, LPVOID data, LPVOID ctx)
3773{
3774 SERVICE_STATUS *svc_status = ctx;
3775 switch (ctrl_code)
3776 {
3777 case SERVICE_CONTROL_STOP:
3778 svc_status->dwCurrentState = SERVICE_STOP_PENDING;
3779 ReportStatusToSCMgr(service, svc_status);
3780 if (exit_event)
3781 {
3782 SetEvent(exit_event);
3783 }
3784 return NO_ERROR;
3785
3786 case SERVICE_CONTROL_INTERROGATE:
3787 return NO_ERROR;
3788
3789 default:
3790 return ERROR_CALL_NOT_IMPLEMENTED;
3791 }
3792}
3793
3794
3795static HANDLE
3797{
3798 /*
3799 * allow all access for local system
3800 * deny FILE_CREATE_PIPE_INSTANCE for everyone
3801 * allow read/write for authenticated users
3802 * deny all access to anonymous
3803 */
3804 const WCHAR *sddlString =
3805 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)";
3806
3807 PSECURITY_DESCRIPTOR sd = NULL;
3808 if (!ConvertStringSecurityDescriptorToSecurityDescriptor(sddlString, SDDL_REVISION_1, &sd,
3809 NULL))
3810 {
3811 MsgToEventLog(M_SYSERR, L"ConvertStringSecurityDescriptorToSecurityDescriptor failed.");
3812 return INVALID_HANDLE_VALUE;
3813 }
3814
3815 /* Set up SECURITY_ATTRIBUTES */
3816 SECURITY_ATTRIBUTES sa = { 0 };
3817 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
3818 sa.lpSecurityDescriptor = sd;
3819 sa.bInheritHandle = FALSE;
3820
3821 DWORD flags = PIPE_ACCESS_DUPLEX | WRITE_DAC | FILE_FLAG_OVERLAPPED;
3822
3823 static BOOL first = TRUE;
3824 if (first)
3825 {
3826 flags |= FILE_FLAG_FIRST_PIPE_INSTANCE;
3827 first = FALSE;
3828 }
3829
3830 WCHAR pipe_name[256]; /* The entire pipe name string can be up to 256 characters long according
3831 to MSDN. */
3832 swprintf(pipe_name, _countof(pipe_name), L"\\\\.\\pipe\\" _L(PACKAGE) L"%ls\\service",
3834 HANDLE pipe = CreateNamedPipe(
3835 pipe_name, flags, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
3836 PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0, &sa);
3837
3838 LocalFree(sd);
3839
3840 if (pipe == INVALID_HANDLE_VALUE)
3841 {
3842 MsgToEventLog(M_SYSERR, L"Could not create named pipe");
3843 return INVALID_HANDLE_VALUE;
3844 }
3845
3846 return pipe;
3847}
3848
3849
3850static DWORD
3851UpdateWaitHandles(LPHANDLE *handles_ptr, LPDWORD count, HANDLE io_event, HANDLE exit_event,
3852 list_item_t *threads)
3853{
3854 static DWORD size = 10;
3855 static LPHANDLE handles = NULL;
3856 DWORD pos = 0;
3857
3858 if (handles == NULL)
3859 {
3860 handles = malloc(size * sizeof(HANDLE));
3861 *handles_ptr = handles;
3862 if (handles == NULL)
3863 {
3864 return ERROR_OUTOFMEMORY;
3865 }
3866 }
3867
3868 handles[pos++] = io_event;
3869
3870 if (!threads)
3871 {
3872 handles[pos++] = exit_event;
3873 }
3874
3875 while (threads)
3876 {
3877 if (pos == size)
3878 {
3879 LPHANDLE tmp;
3880 size += 10;
3881 tmp = realloc(handles, size * sizeof(HANDLE));
3882 if (tmp == NULL)
3883 {
3884 size -= 10;
3885 *count = pos;
3886 return ERROR_OUTOFMEMORY;
3887 }
3888 handles = tmp;
3889 *handles_ptr = handles;
3890 }
3891 handles[pos++] = threads->data;
3892 threads = threads->next;
3893 }
3894
3895 *count = pos;
3896 return NO_ERROR;
3897}
3898
3899
3900static VOID
3902{
3903 free(h);
3904}
3905
3906static BOOL
3907CmpHandle(LPVOID item, LPVOID hnd)
3908{
3909 return item == hnd;
3910}
3911
3912
3913VOID WINAPI
3914ServiceStartInteractiveOwn(DWORD dwArgc, LPWSTR *lpszArgv)
3915{
3916 status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
3917 ServiceStartInteractive(dwArgc, lpszArgv);
3918}
3919
3925static void
3927{
3928 BOOL changed = FALSE;
3929
3930 /* Clean up leftover NRPT rules */
3931 BOOL gpol_nrpt;
3932 changed = DeleteNrptRules(0, &gpol_nrpt);
3933
3934 /* Clean up leftover DNS search list fragments */
3935 HKEY key;
3936 BOOL gpol_list;
3937 GetDnsSearchListKey(NULL, &gpol_list, &key);
3938 if (key != INVALID_HANDLE_VALUE)
3939 {
3941 {
3942 changed = TRUE;
3943 }
3944 RegCloseKey(key);
3945 }
3946
3947 if (changed)
3948 {
3949 ApplyDnsSettings(gpol_nrpt || gpol_list);
3950 }
3951}
3952
3953VOID WINAPI
3954ServiceStartInteractive(DWORD dwArgc, LPWSTR *lpszArgv)
3955{
3956 HANDLE pipe, io_event = NULL;
3957 OVERLAPPED overlapped;
3958 DWORD error = NO_ERROR;
3959 list_item_t *threads = NULL;
3960 PHANDLE handles = NULL;
3961 DWORD handle_count;
3962
3963 service =
3964 RegisterServiceCtrlHandlerEx(interactive_service.name, ServiceCtrlInteractive, &status);
3965 if (!service)
3966 {
3967 return;
3968 }
3969
3970 status.dwCurrentState = SERVICE_START_PENDING;
3971 status.dwServiceSpecificExitCode = NO_ERROR;
3972 status.dwWin32ExitCode = NO_ERROR;
3973 status.dwWaitHint = 3000;
3975
3976 /* Clean up potentially left over registry values */
3978
3979 /* Read info from registry in key HKLM\SOFTWARE\OpenVPN */
3980 error = GetOpenvpnSettings(&settings);
3981 if (error != ERROR_SUCCESS)
3982 {
3983 goto out;
3984 }
3985
3986 io_event = InitOverlapped(&overlapped);
3987 exit_event = CreateEvent(NULL, TRUE, FALSE, NULL);
3988 if (!exit_event || !io_event)
3989 {
3990 error = MsgToEventLog(M_SYSERR, L"Could not create event");
3991 goto out;
3992 }
3993
3994 rdns_semaphore = CreateSemaphoreW(NULL, 1, 1, NULL);
3995 if (!rdns_semaphore)
3996 {
3997 error = MsgToEventLog(M_SYSERR, L"Could not create semaphore for register-dns");
3998 goto out;
3999 }
4000
4001 error = UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
4002 if (error != NO_ERROR)
4003 {
4004 goto out;
4005 }
4006
4007 pipe = CreateClientPipeInstance();
4008 if (pipe == INVALID_HANDLE_VALUE)
4009 {
4010 goto out;
4011 }
4012
4013 status.dwCurrentState = SERVICE_RUNNING;
4014 status.dwWaitHint = 0;
4016
4017 while (TRUE)
4018 {
4019 if (!ConnectNamedPipe(pipe, &overlapped))
4020 {
4021 DWORD connect_error = GetLastError();
4022 if (connect_error == ERROR_NO_DATA)
4023 {
4024 /*
4025 * Client connected and disconnected before we could process it.
4026 * Disconnect and retry instead of aborting the service.
4027 */
4028 MsgToEventLog(M_ERR, L"ConnectNamedPipe returned ERROR_NO_DATA (client dropped)");
4029 DisconnectNamedPipe(pipe);
4030 ResetOverlapped(&overlapped);
4031 continue;
4032 }
4033 else if (connect_error == ERROR_PIPE_CONNECTED)
4034 {
4035 /* No async I/O pending in this case; signal manually. */
4036 SetEvent(overlapped.hEvent);
4037 }
4038 else if (connect_error != ERROR_IO_PENDING)
4039 {
4040 MsgToEventLog(M_SYSERR, L"Could not connect pipe");
4041 break;
4042 }
4043 }
4044
4045 error = WaitForMultipleObjects(handle_count, handles, FALSE, INFINITE);
4046 if (error == WAIT_OBJECT_0)
4047 {
4048 /* Client connected, spawn a worker thread for it */
4049 HANDLE next_pipe = CreateClientPipeInstance();
4050
4051 /* Avoid exceeding WaitForMultipleObjects MAXIMUM_WAIT_OBJECTS */
4052 if (handle_count + 1 > MAXIMUM_WAIT_OBJECTS)
4053 {
4054 ReturnError(pipe, ERROR_CANT_WAIT, L"Too many concurrent clients", 1, &exit_event);
4055 CloseHandleEx(&pipe);
4056 pipe = next_pipe;
4057 ResetOverlapped(&overlapped);
4058 continue;
4059 }
4060
4061 HANDLE thread = CreateThread(NULL, 0, RunOpenvpn, pipe, CREATE_SUSPENDED, NULL);
4062 if (thread)
4063 {
4064 error = AddListItem(&threads, thread);
4065 if (!error)
4066 {
4067 error =
4068 UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
4069 }
4070 if (error)
4071 {
4072 ReturnError(pipe, error, L"Insufficient resources to service new clients", 1,
4073 &exit_event);
4074 /* Update wait handles again after removing the last worker thread */
4075 RemoveListItem(&threads, CmpHandle, thread);
4076 UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
4077 TerminateThread(thread, 1);
4078 CloseHandleEx(&thread);
4079 CloseHandleEx(&pipe);
4080 }
4081 else
4082 {
4083 ResumeThread(thread);
4084 }
4085 }
4086 else
4087 {
4088 CloseHandleEx(&pipe);
4089 }
4090
4091 ResetOverlapped(&overlapped);
4092 pipe = next_pipe;
4093 }
4094 else
4095 {
4096 CancelIo(pipe);
4097 if (error == WAIT_FAILED)
4098 {
4099 MsgToEventLog(M_SYSERR, L"WaitForMultipleObjects failed");
4100 SetEvent(exit_event);
4101 /* Give some time for worker threads to exit and then terminate */
4102 Sleep(1000);
4103 break;
4104 }
4105 if (!threads)
4106 {
4107 /* exit event signaled */
4108 CloseHandleEx(&pipe);
4109 ResetEvent(exit_event);
4110 error = NO_ERROR;
4111 break;
4112 }
4113
4114 /* Worker thread ended */
4115 HANDLE thread = RemoveListItem(&threads, CmpHandle, handles[error]);
4116 UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
4117 CloseHandleEx(&thread);
4118 }
4119 }
4120
4121out:
4122 FreeWaitHandles(handles);
4123 CloseHandleEx(&io_event);
4126
4127 status.dwCurrentState = SERVICE_STOPPED;
4128 status.dwWin32ExitCode = error;
4130}
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 PACKAGE_NAME
Definition config.h:492
#define PACKAGE
Definition config.h:486
#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:71
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:149
BOOL CheckOption(const WCHAR *workdir, int argc, WCHAR *argv[], const settings_t *s)
Definition validate.c:328
static BOOL IsOption(const WCHAR *o)
Definition validate.h:46
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:1155