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-2025 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
37#include <versionhelpers.h>
38
39#include "openvpn-msg.h"
40#include "validate.h"
41#include "wfp_block.h"
42
43#define IO_TIMEOUT 2000 /*ms*/
44
45#define ERROR_OPENVPN_STARTUP 0x20000000
46#define ERROR_STARTUP_DATA 0x20000001
47#define ERROR_MESSAGE_DATA 0x20000002
48#define ERROR_MESSAGE_TYPE 0x20000003
49
50static SERVICE_STATUS_HANDLE service;
51static SERVICE_STATUS status = { .dwServiceType = SERVICE_WIN32_SHARE_PROCESS };
52static HANDLE exit_event = NULL;
54static HANDLE rdns_semaphore = NULL;
55#define RDNS_TIMEOUT 600 /* seconds to wait for the semaphore */
56
57#define TUN_IOCTL_REGISTER_RINGS \
58 CTL_CODE(51820U, 0x970U, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
59
60openvpn_service_t interactive_service = { interactive, _L(PACKAGE_NAME) L"ServiceInteractive",
61 _L(PACKAGE_NAME) L" Interactive Service",
62 SERVICE_DEPENDENCIES, SERVICE_AUTO_START };
63
64
65typedef struct
66{
67 WCHAR *directory;
68 WCHAR *options;
69 WCHAR *std_input;
71
72
73/* Datatype for linked lists */
74typedef struct _list_item
75{
77 LPVOID data;
79
80
81/* Datatypes for undo information */
95
96typedef struct
97{
98 HANDLE engine;
99 int index;
103
104typedef struct
105{
106 char itf_name[256];
107 PWSTR domains;
109
124
125typedef struct
126{
127 CHAR addresses[NRPT_ADDR_NUM * NRPT_ADDR_SIZE];
128 WCHAR domains[512]; /* MULTI_SZ string */
129 DWORD domains_size; /* bytes in domains */
131
132
133static DWORD
134AddListItem(list_item_t **pfirst, LPVOID data)
135{
136 list_item_t *new_item = malloc(sizeof(list_item_t));
137 if (new_item == NULL)
138 {
139 return ERROR_OUTOFMEMORY;
140 }
141
142 new_item->next = *pfirst;
143 new_item->data = data;
144
145 *pfirst = new_item;
146 return NO_ERROR;
147}
148
149typedef BOOL (*match_fn_t)(LPVOID item, LPVOID ctx);
150
151static LPVOID
152RemoveListItem(list_item_t **pfirst, match_fn_t match, LPVOID ctx)
153{
154 LPVOID data = NULL;
155 list_item_t **pnext;
156
157 for (pnext = pfirst; *pnext; pnext = &(*pnext)->next)
158 {
159 list_item_t *item = *pnext;
160 if (!match(item->data, ctx))
161 {
162 continue;
163 }
164
165 /* Found item, remove from the list and free memory */
166 *pnext = item->next;
167 data = item->data;
168 free(item);
169 break;
170 }
171 return data;
172}
173
174
175static HANDLE
176CloseHandleEx(LPHANDLE handle)
177{
178 if (handle && *handle && *handle != INVALID_HANDLE_VALUE)
179 {
180 CloseHandle(*handle);
181 *handle = INVALID_HANDLE_VALUE;
182 }
183 return INVALID_HANDLE_VALUE;
184}
185
186static HANDLE
187InitOverlapped(LPOVERLAPPED overlapped)
188{
189 ZeroMemory(overlapped, sizeof(OVERLAPPED));
190 overlapped->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
191 return overlapped->hEvent;
192}
193
194static BOOL
195ResetOverlapped(LPOVERLAPPED overlapped)
196{
197 HANDLE io_event = overlapped->hEvent;
198 if (!ResetEvent(io_event))
199 {
200 return FALSE;
201 }
202 ZeroMemory(overlapped, sizeof(OVERLAPPED));
203 overlapped->hEvent = io_event;
204 return TRUE;
205}
206
207
208typedef enum
209{
212 write
214
215static DWORD
216AsyncPipeOp(async_op_t op, HANDLE pipe, LPVOID buffer, DWORD size, DWORD count, LPHANDLE events)
217{
218 DWORD i;
219 BOOL success;
220 HANDLE io_event;
221 DWORD res, bytes = 0;
222 OVERLAPPED overlapped;
223 LPHANDLE handles = NULL;
224
225 io_event = InitOverlapped(&overlapped);
226 if (!io_event)
227 {
228 goto out;
229 }
230
231 handles = malloc((count + 1) * sizeof(HANDLE));
232 if (!handles)
233 {
234 goto out;
235 }
236
237 if (op == write)
238 {
239 success = WriteFile(pipe, buffer, size, NULL, &overlapped);
240 }
241 else
242 {
243 success = ReadFile(pipe, buffer, size, NULL, &overlapped);
244 }
245 if (!success && GetLastError() != ERROR_IO_PENDING && GetLastError() != ERROR_MORE_DATA)
246 {
247 goto out;
248 }
249
250 handles[0] = io_event;
251 for (i = 0; i < count; i++)
252 {
253 handles[i + 1] = events[i];
254 }
255
256 res = WaitForMultipleObjects(count + 1, handles, FALSE, op == peek ? INFINITE : IO_TIMEOUT);
257 if (res != WAIT_OBJECT_0)
258 {
259 CancelIo(pipe);
260 goto out;
261 }
262
263 if (op == peek)
264 {
265 PeekNamedPipe(pipe, NULL, 0, NULL, &bytes, NULL);
266 }
267 else
268 {
269 GetOverlappedResult(pipe, &overlapped, &bytes, TRUE);
270 }
271
272out:
273 CloseHandleEx(&io_event);
274 free(handles);
275 return bytes;
276}
277
278static DWORD
279PeekNamedPipeAsync(HANDLE pipe, DWORD count, LPHANDLE events)
280{
281 return AsyncPipeOp(peek, pipe, NULL, 0, count, events);
282}
283
284static DWORD
285ReadPipeAsync(HANDLE pipe, LPVOID buffer, DWORD size, DWORD count, LPHANDLE events)
286{
287 return AsyncPipeOp(read, pipe, buffer, size, count, events);
288}
289
290static DWORD
291WritePipeAsync(HANDLE pipe, LPVOID data, DWORD size, DWORD count, LPHANDLE events)
292{
293 return AsyncPipeOp(write, pipe, data, size, count, events);
294}
295
296static VOID
297ReturnProcessId(HANDLE pipe, DWORD pid, DWORD count, LPHANDLE events)
298{
299 const WCHAR msg[] = L"Process ID";
300 WCHAR buf[22 + _countof(msg)]; /* 10 chars each for error and PID and 2 for line breaks */
301
302 /*
303 * Same format as error messages (3 line string) with error = 0 in
304 * 0x%08x format, PID on line 2 and a description "Process ID" on line 3
305 */
306 swprintf(buf, _countof(buf), L"0x%08x\n0x%08x\n%ls", 0, pid, msg);
307
308 WritePipeAsync(pipe, buf, (DWORD)(wcslen(buf) * 2), count, events);
309}
310
311static VOID
312ReturnError(HANDLE pipe, DWORD error, LPCWSTR func, DWORD count, LPHANDLE events)
313{
314 DWORD result_len;
315 LPWSTR result = L"0xffffffff\nFormatMessage failed\nCould not return result";
316 DWORD_PTR args[] = { (DWORD_PTR)error, (DWORD_PTR)func, (DWORD_PTR) "" };
317
318 if (error != ERROR_OPENVPN_STARTUP)
319 {
320 FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER
321 | FORMAT_MESSAGE_IGNORE_INSERTS,
322 0, error, 0, (LPWSTR)&args[2], 0, NULL);
323 }
324
325 result_len = FormatMessageW(
326 FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY,
327 L"0x%1!08x!\n%2!s!\n%3!s!", 0, 0, (LPWSTR)&result, 0, (va_list *)args);
328
329 WritePipeAsync(pipe, result, (DWORD)(wcslen(result) * 2), count, events);
331
332 if (error != ERROR_OPENVPN_STARTUP)
333 {
334 LocalFree((LPVOID)args[2]);
335 }
336 if (result_len)
337 {
338 LocalFree(result);
339 }
340}
341
342
343static VOID
344ReturnLastError(HANDLE pipe, LPCWSTR func)
345{
346 ReturnError(pipe, GetLastError(), func, 1, &exit_event);
347}
348
349/*
350 * Validate options against a white list. Also check the config_file is
351 * inside the config_dir. The white list is defined in validate.c
352 * Returns true on success, false on error with reason set in errmsg.
353 */
354static BOOL
355ValidateOptions(HANDLE pipe, const WCHAR *workdir, const WCHAR *options, WCHAR *errmsg,
356 DWORD capacity)
357{
358 WCHAR **argv;
359 int argc;
360 BOOL ret = FALSE;
361 int i;
362 const WCHAR *msg1 = L"You have specified a config file location (%ls relative to %ls)"
363 L" that requires admin approval. This error may be avoided"
364 L" by adding your account to the \"%ls\" group";
365
366 const WCHAR *msg2 = L"You have specified an option (%ls) that may be used"
367 L" only with admin approval. This error may be avoided"
368 L" by adding your account to the \"%ls\" group";
369
370 argv = CommandLineToArgvW(options, &argc);
371
372 if (!argv)
373 {
374 swprintf(errmsg, capacity,
375 L"Cannot validate options: CommandLineToArgvW failed with error = 0x%08x",
376 GetLastError());
377 goto out;
378 }
379
380 /* Note: argv[0] is the first option */
381 if (argc < 1) /* no options */
382 {
383 ret = TRUE;
384 goto out;
385 }
386
387 /*
388 * If only one argument, it is the config file
389 */
390 if (argc == 1)
391 {
392 WCHAR *argv_tmp[2] = { L"--config", argv[0] };
393
394 if (!CheckOption(workdir, 2, argv_tmp, &settings))
395 {
396 swprintf(errmsg, capacity, msg1, argv[0], workdir, settings.ovpn_admin_group);
397 }
398 goto out;
399 }
400
401 for (i = 0; i < argc; ++i)
402 {
403 if (!IsOption(argv[i]))
404 {
405 continue;
406 }
407
408 if (!CheckOption(workdir, argc - i, &argv[i], &settings))
409 {
410 if (wcscmp(L"--config", argv[i]) == 0 && argc - i > 1)
411 {
412 swprintf(errmsg, capacity, msg1, argv[i + 1], workdir, settings.ovpn_admin_group);
413 }
414 else
415 {
416 swprintf(errmsg, capacity, msg2, argv[i], settings.ovpn_admin_group);
417 }
418 goto out;
419 }
420 }
421
422 /* all options passed */
423 ret = TRUE;
424
425out:
426 if (argv)
427 {
428 LocalFree(argv);
429 }
430 return ret;
431}
432
433static BOOL
434GetStartupData(HANDLE pipe, STARTUP_DATA *sud)
435{
436 size_t size, len;
437 WCHAR *data = NULL;
438 DWORD bytes, read;
439
440 bytes = PeekNamedPipeAsync(pipe, 1, &exit_event);
441 if (bytes == 0)
442 {
443 MsgToEventLog(M_SYSERR, L"PeekNamedPipeAsync failed");
444 ReturnLastError(pipe, L"PeekNamedPipeAsync");
445 goto err;
446 }
447
448 size = bytes / sizeof(*data);
449 if ((size == 0) || (size > 4096)) /* our startup data is 1024 wchars at the moment */
450 {
451 MsgToEventLog(M_SYSERR, L"malformed startup data: %lu bytes received", size);
452 ReturnError(pipe, ERROR_STARTUP_DATA, L"GetStartupData", 1, &exit_event);
453 goto err;
454 }
455
456 data = malloc(bytes);
457 if (data == NULL)
458 {
459 MsgToEventLog(M_SYSERR, L"malloc failed");
460 ReturnLastError(pipe, L"malloc");
461 goto err;
462 }
463
464 read = ReadPipeAsync(pipe, data, bytes, 1, &exit_event);
465 if (bytes != read)
466 {
467 MsgToEventLog(M_SYSERR, L"ReadPipeAsync failed");
468 ReturnLastError(pipe, L"ReadPipeAsync");
469 goto err;
470 }
471
472 if (data[size - 1] != 0)
473 {
474 MsgToEventLog(M_ERR, L"Startup data is not NULL terminated");
475 ReturnError(pipe, ERROR_STARTUP_DATA, L"GetStartupData", 1, &exit_event);
476 goto err;
477 }
478
479 sud->directory = data;
480 len = wcslen(sud->directory) + 1;
481 size -= len;
482 if (size <= 0)
483 {
484 MsgToEventLog(M_ERR, L"Startup data ends at working directory");
485 ReturnError(pipe, ERROR_STARTUP_DATA, L"GetStartupData", 1, &exit_event);
486 goto err;
487 }
488
489 sud->options = sud->directory + len;
490 len = wcslen(sud->options) + 1;
491 size -= len;
492 if (size <= 0)
493 {
494 MsgToEventLog(M_ERR, L"Startup data ends at command line options");
495 ReturnError(pipe, ERROR_STARTUP_DATA, L"GetStartupData", 1, &exit_event);
496 goto err;
497 }
498
499 sud->std_input = sud->options + len;
500 return TRUE;
501
502err:
503 sud->directory = NULL; /* caller must not free() */
504 free(data);
505 return FALSE;
506}
507
508
509static VOID
511{
512 free(sud->directory);
513}
514
515
516static SOCKADDR_INET
517sockaddr_inet(short family, inet_address_t *addr)
518{
519 SOCKADDR_INET sa_inet;
520 ZeroMemory(&sa_inet, sizeof(sa_inet));
521 sa_inet.si_family = family;
522 if (family == AF_INET)
523 {
524 sa_inet.Ipv4.sin_addr = addr->ipv4;
525 }
526 else if (family == AF_INET6)
527 {
528 sa_inet.Ipv6.sin6_addr = addr->ipv6;
529 }
530 return sa_inet;
531}
532
533static DWORD
534InterfaceLuid(const char *iface_name, PNET_LUID luid)
535{
536 NETIO_STATUS status;
537 LPWSTR wide_name = utf8to16(iface_name);
538
539 if (wide_name)
540 {
541 status = ConvertInterfaceAliasToLuid(wide_name, luid);
542 free(wide_name);
543 }
544 else
545 {
546 status = ERROR_OUTOFMEMORY;
547 }
548 return status;
549}
550
551static BOOL
552CmpAddress(LPVOID item, LPVOID address)
553{
554 return memcmp(item, address, sizeof(MIB_UNICASTIPADDRESS_ROW)) == 0 ? TRUE : FALSE;
555}
556
557static DWORD
558DeleteAddress(PMIB_UNICASTIPADDRESS_ROW addr_row)
559{
560 return DeleteUnicastIpAddressEntry(addr_row);
561}
562
563static DWORD
565{
566 DWORD err;
567 PMIB_UNICASTIPADDRESS_ROW addr_row;
568 BOOL add = msg->header.type == msg_add_address;
569
570 addr_row = malloc(sizeof(*addr_row));
571 if (addr_row == NULL)
572 {
573 return ERROR_OUTOFMEMORY;
574 }
575
576 InitializeUnicastIpAddressEntry(addr_row);
577 addr_row->Address = sockaddr_inet(msg->family, &msg->address);
578 addr_row->OnLinkPrefixLength = (UINT8)msg->prefix_len;
579
580 if (msg->iface.index != -1)
581 {
582 addr_row->InterfaceIndex = msg->iface.index;
583 }
584 else
585 {
586 NET_LUID luid;
587 err = InterfaceLuid(msg->iface.name, &luid);
588 if (err)
589 {
590 goto out;
591 }
592 addr_row->InterfaceLuid = luid;
593 }
594
595 if (add)
596 {
597 err = CreateUnicastIpAddressEntry(addr_row);
598 if (err)
599 {
600 goto out;
601 }
602
603 err = AddListItem(&(*lists)[address], addr_row);
604 if (err)
605 {
606 DeleteAddress(addr_row);
607 }
608 }
609 else
610 {
611 err = DeleteAddress(addr_row);
612 if (err)
613 {
614 goto out;
615 }
616
617 free(RemoveListItem(&(*lists)[address], CmpAddress, addr_row));
618 }
619
620out:
621 if (!add || err)
622 {
623 free(addr_row);
624 }
625
626 return err;
627}
628
629static BOOL
630CmpRoute(LPVOID item, LPVOID route)
631{
632 return memcmp(item, route, sizeof(MIB_IPFORWARD_ROW2)) == 0 ? TRUE : FALSE;
633}
634
635static DWORD
636DeleteRoute(PMIB_IPFORWARD_ROW2 fwd_row)
637{
638 return DeleteIpForwardEntry2(fwd_row);
639}
640
641static DWORD
643{
644 DWORD err;
645 PMIB_IPFORWARD_ROW2 fwd_row;
646 BOOL add = msg->header.type == msg_add_route;
647
648 fwd_row = malloc(sizeof(*fwd_row));
649 if (fwd_row == NULL)
650 {
651 return ERROR_OUTOFMEMORY;
652 }
653
654 ZeroMemory(fwd_row, sizeof(*fwd_row));
655 fwd_row->ValidLifetime = 0xffffffff;
656 fwd_row->PreferredLifetime = 0xffffffff;
657 fwd_row->Protocol = MIB_IPPROTO_NETMGMT;
658 fwd_row->Metric = msg->metric;
659 fwd_row->DestinationPrefix.Prefix = sockaddr_inet(msg->family, &msg->prefix);
660 fwd_row->DestinationPrefix.PrefixLength = (UINT8)msg->prefix_len;
661 fwd_row->NextHop = sockaddr_inet(msg->family, &msg->gateway);
662
663 if (msg->iface.index != -1)
664 {
665 fwd_row->InterfaceIndex = msg->iface.index;
666 }
667 else if (strlen(msg->iface.name))
668 {
669 NET_LUID luid;
670 err = InterfaceLuid(msg->iface.name, &luid);
671 if (err)
672 {
673 goto out;
674 }
675 fwd_row->InterfaceLuid = luid;
676 }
677
678 if (add)
679 {
680 err = CreateIpForwardEntry2(fwd_row);
681 if (err)
682 {
683 goto out;
684 }
685
686 err = AddListItem(&(*lists)[route], fwd_row);
687 if (err)
688 {
689 DeleteRoute(fwd_row);
690 }
691 }
692 else
693 {
694 err = DeleteRoute(fwd_row);
695 if (err)
696 {
697 goto out;
698 }
699
700 free(RemoveListItem(&(*lists)[route], CmpRoute, fwd_row));
701 }
702
703out:
704 if (!add || err)
705 {
706 free(fwd_row);
707 }
708
709 return err;
710}
711
712
713static DWORD
715{
716 if (msg->family == AF_INET)
717 {
718 return FlushIpNetTable(msg->iface.index);
719 }
720
721 return FlushIpNetTable2(msg->family, msg->iface.index);
722}
723
724static void
725BlockDNSErrHandler(DWORD err, const char *msg)
726{
727 WCHAR buf[256];
728 LPCWSTR err_str;
729
730 if (!err)
731 {
732 return;
733 }
734
735 err_str = L"Unknown Win32 Error";
736
737 if (FormatMessageW(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
738 NULL, err, 0, buf, _countof(buf), NULL))
739 {
740 err_str = buf;
741 }
742
743 MsgToEventLog(M_ERR, L"%hs (status = %lu): %ls", msg, err, err_str);
744}
745
746/* Use an always-true match_fn to get the head of the list */
747static BOOL
748CmpAny(LPVOID item, LPVOID any)
749{
750 return TRUE;
751}
752
753static DWORD
755{
756 DWORD err = 0;
757 wfp_block_data_t *block_data = RemoveListItem(&(*lists)[wfp_block], CmpAny, NULL);
758
759 if (block_data)
760 {
761 err = delete_wfp_block_filters(block_data->engine);
762 if (block_data->metric_v4 >= 0)
763 {
764 set_interface_metric(block_data->index, AF_INET, block_data->metric_v4);
765 }
766 if (block_data->metric_v6 >= 0)
767 {
768 set_interface_metric(block_data->index, AF_INET6, block_data->metric_v6);
769 }
770 free(block_data);
771 }
772 else
773 {
774 MsgToEventLog(M_ERR, L"No previous block filters to delete");
775 }
776
777 return err;
778}
779
780static DWORD
782{
783 DWORD err = 0;
784 wfp_block_data_t *block_data = NULL;
785 HANDLE engine = NULL;
786 LPCWSTR exe_path;
787 BOOL dns_only;
788
789 exe_path = settings.exe_path;
790 dns_only = (msg->flags == wfp_block_dns);
791
792 err = add_wfp_block_filters(&engine, msg->iface.index, exe_path, BlockDNSErrHandler, dns_only);
793 if (!err)
794 {
795 block_data = malloc(sizeof(wfp_block_data_t));
796 if (!block_data)
797 {
798 err = ERROR_OUTOFMEMORY;
799 goto out;
800 }
801 block_data->engine = engine;
802 block_data->index = msg->iface.index;
803 int is_auto = 0;
804 block_data->metric_v4 = get_interface_metric(msg->iface.index, AF_INET, &is_auto);
805 if (is_auto)
806 {
807 block_data->metric_v4 = 0;
808 }
809 block_data->metric_v6 = get_interface_metric(msg->iface.index, AF_INET6, &is_auto);
810 if (is_auto)
811 {
812 block_data->metric_v6 = 0;
813 }
814
815 err = AddListItem(&(*lists)[wfp_block], block_data);
816 if (!err)
817 {
818 err = set_interface_metric(msg->iface.index, AF_INET, WFP_BLOCK_IFACE_METRIC);
819 if (!err)
820 {
821 /* for IPv6, we intentionally ignore errors, because
822 * otherwise block-dns activation will fail if a user or
823 * admin has disabled IPv6 on the tun/tap/dco interface
824 * (if OpenVPN wants IPv6 ifconfig, we'll fail there)
825 */
826 set_interface_metric(msg->iface.index, AF_INET6, WFP_BLOCK_IFACE_METRIC);
827 }
828 if (err)
829 {
830 /* delete the filters, remove undo item and free interface data */
831 DeleteWfpBlock(lists);
832 engine = NULL;
833 }
834 }
835 }
836
837out:
838 if (err && engine)
839 {
841 free(block_data);
842 }
843
844 return err;
845}
846
847static DWORD
849{
850 if (msg->header.type == msg_add_wfp_block)
851 {
852 return AddWfpBlock(msg, lists);
853 }
854 else
855 {
856 return DeleteWfpBlock(lists);
857 }
858}
859
860/*
861 * Execute a command and return its exit code. If timeout > 0, terminate
862 * the process if still running after timeout milliseconds. In that case
863 * the return value is the windows error code WAIT_TIMEOUT = 0x102
864 */
865static DWORD
866ExecCommand(const WCHAR *argv0, const WCHAR *cmdline, DWORD timeout)
867{
868 DWORD exit_code;
869 STARTUPINFOW si;
870 PROCESS_INFORMATION pi;
871 DWORD proc_flags = CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT;
872 WCHAR *cmdline_dup = NULL;
873
874 ZeroMemory(&si, sizeof(si));
875 ZeroMemory(&pi, sizeof(pi));
876
877 si.cb = sizeof(si);
878
879 /* CreateProcess needs a modifiable cmdline: make a copy */
880 cmdline_dup = _wcsdup(cmdline);
881 if (cmdline_dup
882 && CreateProcessW(argv0, cmdline_dup, NULL, NULL, FALSE, proc_flags, NULL, NULL, &si, &pi))
883 {
884 WaitForSingleObject(pi.hProcess, timeout ? timeout : INFINITE);
885 if (!GetExitCodeProcess(pi.hProcess, &exit_code))
886 {
887 MsgToEventLog(M_SYSERR, L"ExecCommand: Error getting exit_code:");
888 exit_code = GetLastError();
889 }
890 else if (exit_code == STILL_ACTIVE)
891 {
892 exit_code = WAIT_TIMEOUT; /* Windows error code 0x102 */
893
894 /* kill without impunity */
895 TerminateProcess(pi.hProcess, exit_code);
896 MsgToEventLog(M_ERR, L"ExecCommand: \"%ls %ls\" killed after timeout", argv0, cmdline);
897 }
898 else if (exit_code)
899 {
900 MsgToEventLog(M_ERR, L"ExecCommand: \"%ls %ls\" exited with status = %lu", argv0,
901 cmdline, exit_code);
902 }
903 else
904 {
905 MsgToEventLog(M_INFO, L"ExecCommand: \"%ls %ls\" completed", argv0, cmdline);
906 }
907
908 CloseHandle(pi.hProcess);
909 CloseHandle(pi.hThread);
910 }
911 else
912 {
913 exit_code = GetLastError();
914 MsgToEventLog(M_SYSERR, L"ExecCommand: could not run \"%ls %ls\" :", argv0, cmdline);
915 }
916
917 free(cmdline_dup);
918 return exit_code;
919}
920
921/*
922 * Entry point for register-dns thread.
923 */
924static DWORD WINAPI
925RegisterDNS(LPVOID unused)
926{
927 DWORD err;
928 size_t i;
929 DWORD timeout = RDNS_TIMEOUT * 1000; /* in milliseconds */
930
931 /* path of ipconfig command */
932 WCHAR ipcfg[MAX_PATH];
933
934 struct
935 {
936 WCHAR *argv0;
937 WCHAR *cmdline;
938 DWORD timeout;
939 } cmds[] = {
940 { ipcfg, L"ipconfig /flushdns", timeout },
941 { ipcfg, L"ipconfig /registerdns", timeout },
942 };
943
944 HANDLE wait_handles[2] = { rdns_semaphore, exit_event };
945
946 swprintf(ipcfg, MAX_PATH, L"%ls\\%ls", get_win_sys_path(), L"ipconfig.exe");
947
948 if (WaitForMultipleObjects(2, wait_handles, FALSE, timeout) == WAIT_OBJECT_0)
949 {
950 /* Semaphore locked */
951 for (i = 0; i < _countof(cmds); ++i)
952 {
953 ExecCommand(cmds[i].argv0, cmds[i].cmdline, cmds[i].timeout);
954 }
955 err = 0;
956 if (!ReleaseSemaphore(rdns_semaphore, 1, NULL))
957 {
958 err =
959 MsgToEventLog(M_SYSERR, L"RegisterDNS: Failed to release regsiter-dns semaphore:");
960 }
961 }
962 else
963 {
964 MsgToEventLog(M_ERR, L"RegisterDNS: Failed to lock register-dns semaphore");
965 err = ERROR_SEM_TIMEOUT; /* Windows error code 0x79 */
966 }
967 return err;
968}
969
970static DWORD
972{
973 DWORD err;
974 HANDLE thread = NULL;
975
976 /* Delegate this job to a sub-thread */
977 thread = CreateThread(NULL, 0, RegisterDNS, NULL, 0, NULL);
978
979 /*
980 * We don't add these thread handles to the undo list -- the thread and
981 * processes it spawns are all supposed to terminate or timeout by themselves.
982 */
983 if (thread)
984 {
985 err = 0;
986 CloseHandle(thread);
987 }
988 else
989 {
990 err = GetLastError();
991 }
992
993 return err;
994}
995
1005static DWORD
1006netsh_wins_cmd(const wchar_t *action, int if_index, const wchar_t *addr)
1007{
1008 DWORD err = 0;
1009 int timeout = 30000; /* in msec */
1010 wchar_t argv0[MAX_PATH];
1011 wchar_t *cmdline = NULL;
1012 const wchar_t *addr_static = (wcscmp(action, L"set") == 0) ? L"static" : L"";
1013
1014 if (!addr)
1015 {
1016 if (wcscmp(action, L"delete") == 0)
1017 {
1018 addr = L"all";
1019 }
1020 else /* nothing to do -- return success*/
1021 {
1022 goto out;
1023 }
1024 }
1025
1026 /* Path of netsh */
1027 swprintf(argv0, _countof(argv0), L"%ls\\%ls", get_win_sys_path(), L"netsh.exe");
1028
1029 /* cmd template:
1030 * netsh interface ip $action wins $if_name $static $addr
1031 */
1032 const wchar_t *fmt = L"netsh interface ip %ls wins %d %ls %ls";
1033
1034 /* max cmdline length in wchars -- include room for worst case and some */
1035 size_t ncmdline = wcslen(fmt) + 11 /*if_index*/ + wcslen(action) + wcslen(addr)
1036 + wcslen(addr_static) + 32 + 1;
1037 cmdline = malloc(ncmdline * sizeof(wchar_t));
1038 if (!cmdline)
1039 {
1040 err = ERROR_OUTOFMEMORY;
1041 goto out;
1042 }
1043
1044 swprintf(cmdline, ncmdline, fmt, action, if_index, addr_static, addr);
1045
1046 err = ExecCommand(argv0, cmdline, timeout);
1047
1048out:
1049 free(cmdline);
1050 return err;
1051}
1052
1059static BOOL
1061{
1062 typedef NTSTATUS(__stdcall * publish_fn_t)(DWORD StateNameLo, DWORD StateNameHi, DWORD TypeId,
1063 DWORD Buffer, DWORD Length, DWORD ExplicitScope);
1064 publish_fn_t RtlPublishWnfStateData;
1065 const DWORD WNF_GPOL_SYSTEM_CHANGES_HI = 0x0D891E2A;
1066 const DWORD WNF_GPOL_SYSTEM_CHANGES_LO = 0xA3BC0875;
1067
1068 HMODULE ntdll = LoadLibraryA("ntdll.dll");
1069 if (ntdll == NULL)
1070 {
1071 return FALSE;
1072 }
1073
1074 RtlPublishWnfStateData = (publish_fn_t)GetProcAddress(ntdll, "RtlPublishWnfStateData");
1075 if (RtlPublishWnfStateData == NULL)
1076 {
1077 return FALSE;
1078 }
1079
1080 if (RtlPublishWnfStateData(WNF_GPOL_SYSTEM_CHANGES_LO, WNF_GPOL_SYSTEM_CHANGES_HI, 0, 0, 0, 0)
1081 != ERROR_SUCCESS)
1082 {
1083 return FALSE;
1084 }
1085
1086 return TRUE;
1087}
1088
1095static BOOL
1097{
1098 typedef NTSTATUS (*publish_fn_t)(INT64 StateName, INT64 TypeId, INT64 Buffer,
1099 unsigned int Length, INT64 ExplicitScope);
1100 publish_fn_t RtlPublishWnfStateData;
1101 const INT64 WNF_GPOL_SYSTEM_CHANGES = 0x0D891E2AA3BC0875;
1102
1103 HMODULE ntdll = LoadLibraryA("ntdll.dll");
1104 if (ntdll == NULL)
1105 {
1106 return FALSE;
1107 }
1108
1109 RtlPublishWnfStateData = (publish_fn_t)GetProcAddress(ntdll, "RtlPublishWnfStateData");
1110 if (RtlPublishWnfStateData == NULL)
1111 {
1112 return FALSE;
1113 }
1114
1115 if (RtlPublishWnfStateData(WNF_GPOL_SYSTEM_CHANGES, 0, 0, 0, 0) != ERROR_SUCCESS)
1116 {
1117 return FALSE;
1118 }
1119
1120 return TRUE;
1121}
1122
1128static BOOL
1130{
1131 SYSTEM_INFO si;
1132 GetSystemInfo(&si);
1133 const BOOL win_32bit = si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL;
1134 return win_32bit ? ApplyGpolSettings32() : ApplyGpolSettings64();
1135}
1136
1144static BOOL
1145ApplyDnsSettings(BOOL apply_gpol)
1146{
1147 BOOL res = FALSE;
1148 SC_HANDLE scm = NULL;
1149 SC_HANDLE dnssvc = NULL;
1150
1151 if (apply_gpol && ApplyGpolSettings() == FALSE)
1152 {
1153 MsgToEventLog(M_ERR, L"%S: sending GPOL notification failed", __func__);
1154 }
1155
1156 scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
1157 if (scm == NULL)
1158 {
1159 MsgToEventLog(M_ERR, L"%S: OpenSCManager call failed (%lu)", __func__, GetLastError());
1160 goto out;
1161 }
1162
1163 dnssvc = OpenServiceA(scm, "Dnscache", SERVICE_PAUSE_CONTINUE);
1164 if (dnssvc == NULL)
1165 {
1166 MsgToEventLog(M_ERR, L"%S: OpenService call failed (%lu)", __func__, GetLastError());
1167 goto out;
1168 }
1169
1170 SERVICE_STATUS status;
1171 if (ControlService(dnssvc, SERVICE_CONTROL_PARAMCHANGE, &status) == 0)
1172 {
1173 MsgToEventLog(M_ERR, L"%S: ControlService call failed (%lu)", __func__, GetLastError());
1174 goto out;
1175 }
1176
1177 res = TRUE;
1178
1179out:
1180 if (dnssvc)
1181 {
1182 CloseServiceHandle(dnssvc);
1183 }
1184 if (scm)
1185 {
1186 CloseServiceHandle(scm);
1187 }
1188 return res;
1189}
1190
1200static DWORD
1201InterfaceIdString(PCSTR itf_name, PWSTR str, size_t len)
1202{
1203 DWORD err;
1204 GUID guid;
1205 NET_LUID luid;
1206 PWSTR iid_str = NULL;
1207
1208 err = InterfaceLuid(itf_name, &luid);
1209 if (err)
1210 {
1211 MsgToEventLog(M_ERR, L"%S: failed to convert itf alias '%s'", __func__, itf_name);
1212 goto out;
1213 }
1214 err = ConvertInterfaceLuidToGuid(&luid, &guid);
1215 if (err)
1216 {
1217 MsgToEventLog(M_ERR, L"%S: Failed to convert itf '%s' LUID", __func__, itf_name);
1218 goto out;
1219 }
1220
1221 if (StringFromIID(&guid, &iid_str) != S_OK)
1222 {
1223 MsgToEventLog(M_ERR, L"%S: Failed to convert itf '%s' IID", __func__, itf_name);
1224 err = ERROR_OUTOFMEMORY;
1225 goto out;
1226 }
1227 if (wcslen(iid_str) + 1 > len)
1228 {
1229 err = ERROR_INVALID_PARAMETER;
1230 goto out;
1231 }
1232
1233 wcsncpy(str, iid_str, len);
1234
1235out:
1236 if (iid_str)
1237 {
1238 CoTaskMemFree(iid_str);
1239 }
1240 return err;
1241}
1242
1256static BOOL
1258{
1259 char data[64];
1260 DWORD size = sizeof(data);
1261 LSTATUS err = RegGetValueA(key, NULL, "SearchList", RRF_RT_REG_SZ, NULL, (PBYTE)data, &size);
1262 if (!err || err == ERROR_MORE_DATA)
1263 {
1264 data[sizeof(data) - 1] = '\0';
1265 for (int i = 0; i < strlen(data); ++i)
1266 {
1267 if (isalnum(data[i]) || data[i] == '-' || data[i] == '.')
1268 {
1269 return TRUE;
1270 }
1271 }
1272 }
1273 return FALSE;
1274}
1275
1293static BOOL
1294GetDnsSearchListKey(PCSTR itf_name, PBOOL gpol, PHKEY key)
1295{
1296 LSTATUS err;
1297
1298 *gpol = FALSE;
1299
1300 /* Try the group policy search list */
1301 err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient",
1302 0, KEY_ALL_ACCESS, key);
1303 if (!err)
1304 {
1305 if (HasValidSearchList(*key))
1306 {
1307 *gpol = TRUE;
1308 return TRUE;
1309 }
1310 RegCloseKey(*key);
1311 }
1312
1313 /* Try the system-wide search list */
1314 err =
1315 RegOpenKeyExA(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\TCPIP\\Parameters",
1316 0, KEY_ALL_ACCESS, key);
1317 if (!err)
1318 {
1319 if (HasValidSearchList(*key))
1320 {
1321 return TRUE;
1322 }
1323 RegCloseKey(*key);
1324 }
1325
1326 if (itf_name)
1327 {
1328 /* Always return the VPN interface key (if it exists) */
1329 WCHAR iid[64];
1330 DWORD iid_err = InterfaceIdString(itf_name, iid, _countof(iid));
1331 if (!iid_err)
1332 {
1333 HKEY itfs;
1334 err =
1335 RegOpenKeyExA(HKEY_LOCAL_MACHINE,
1336 "System\\CurrentControlSet\\Services\\TCPIP\\Parameters\\Interfaces",
1337 0, KEY_ALL_ACCESS, &itfs);
1338 if (!err)
1339 {
1340 err = RegOpenKeyExW(itfs, iid, 0, KEY_ALL_ACCESS, key);
1341 RegCloseKey(itfs);
1342 if (!err)
1343 {
1344 return FALSE; /* No need to preserve the VPN itf search list */
1345 }
1346 }
1347 }
1348 }
1349
1350 *key = INVALID_HANDLE_VALUE;
1351 return FALSE;
1352}
1353
1361static BOOL
1363{
1364 LSTATUS err;
1365
1366 err = RegGetValueA(key, NULL, "InitialSearchList", RRF_RT_REG_SZ, NULL, NULL, NULL);
1367 if (err)
1368 {
1369 if (err == ERROR_FILE_NOT_FOUND)
1370 {
1371 return FALSE;
1372 }
1373 MsgToEventLog(M_ERR, L"%S: failed to get InitialSearchList (%lu)", __func__, err);
1374 }
1375
1376 return TRUE;
1377}
1378
1379#if defined(__GNUC__) || defined(__clang__)
1380#pragma GCC diagnostic push
1381#pragma GCC diagnostic ignored "-Wconversion"
1382#endif
1383
1394static BOOL
1396{
1397 if (!list || wcslen(list) == 0)
1398 {
1399 MsgToEventLog(M_ERR, L"%S: empty search list", __func__);
1400 return FALSE;
1401 }
1402
1404 {
1405 /* Initial list had already been stored */
1406 return TRUE;
1407 }
1408
1409 DWORD size = (wcslen(list) + 1) * sizeof(*list);
1410 LSTATUS err = RegSetValueExW(key, L"InitialSearchList", 0, REG_SZ, (PBYTE)list, size);
1411 if (err)
1412 {
1413 MsgToEventLog(M_ERR, L"%S: failed to set InitialSearchList value (%lu)", __func__, err);
1414 return FALSE;
1415 }
1416
1417 return TRUE;
1418}
1419
1429static BOOL
1430AddDnsSearchDomains(HKEY key, BOOL have_list, PCWSTR domains)
1431{
1432 LSTATUS err;
1433 WCHAR list[2048] = { 0 };
1434 DWORD size = sizeof(list);
1435
1436 if (have_list)
1437 {
1438 err = RegGetValueW(key, NULL, L"SearchList", RRF_RT_REG_SZ, NULL, list, &size);
1439 if (err)
1440 {
1441 MsgToEventLog(M_SYSERR, L"%S: could not get SearchList from registry (%lu)", __func__,
1442 err);
1443 return FALSE;
1444 }
1445
1446 if (!StoreInitialDnsSearchList(key, list))
1447 {
1448 return FALSE;
1449 }
1450
1451 size_t listlen = (size / sizeof(list[0])) - 1; /* returned size is in bytes */
1452 size_t domlen = wcslen(domains);
1453 if (listlen + domlen + 2 > _countof(list))
1454 {
1455 MsgToEventLog(M_SYSERR, L"%S: not enough space in list for search domains (len=%lu)",
1456 __func__, domlen);
1457 return FALSE;
1458 }
1459
1460 /* Append to end of the search list */
1461 PWSTR pos = list + listlen;
1462 *pos = ',';
1463 wcsncpy(pos + 1, domains, domlen + 1);
1464 }
1465 else
1466 {
1467 wcsncpy(list, domains, wcslen(domains) + 1);
1468 }
1469
1470 size = (wcslen(list) + 1) * sizeof(list[0]);
1471 err = RegSetValueExW(key, L"SearchList", 0, REG_SZ, (PBYTE)list, size);
1472 if (err)
1473 {
1474 MsgToEventLog(M_SYSERR, L"%S: could not set SearchList to registry (%lu)", __func__, err);
1475 return FALSE;
1476 }
1477
1478 return TRUE;
1479}
1480
1492static BOOL
1494{
1495 LSTATUS err;
1496 BOOL ret = FALSE;
1497 WCHAR list[2048];
1498 DWORD size = sizeof(list);
1499
1500 err = RegGetValueW(key, NULL, L"InitialSearchList", RRF_RT_REG_SZ, NULL, list, &size);
1501 if (err)
1502 {
1503 if (err != ERROR_FILE_NOT_FOUND)
1504 {
1505 MsgToEventLog(M_SYSERR, L"%S: could not get InitialSearchList from registry (%lu)",
1506 __func__, err);
1507 }
1508 goto out;
1509 }
1510
1511 size = (wcslen(list) + 1) * sizeof(list[0]);
1512 err = RegSetValueExW(key, L"SearchList", 0, REG_SZ, (PBYTE)list, size);
1513 if (err)
1514 {
1515 MsgToEventLog(M_SYSERR, L"%S: could not set SearchList in registry (%lu)", __func__, err);
1516 goto out;
1517 }
1518
1519 RegDeleteValueA(key, "InitialSearchList");
1520 ret = TRUE;
1521
1522out:
1523 return ret;
1524}
1525
1532static void
1533RemoveDnsSearchDomains(HKEY key, PCWSTR domains)
1534{
1535 LSTATUS err;
1536 WCHAR list[2048];
1537 DWORD size = sizeof(list);
1538
1539 err = RegGetValueW(key, NULL, L"SearchList", RRF_RT_REG_SZ, NULL, list, &size);
1540 if (err)
1541 {
1542 MsgToEventLog(M_SYSERR, L"%S: could not get SearchList from registry (%lu)", __func__, err);
1543 return;
1544 }
1545
1546 PWSTR dst = wcsstr(list, domains);
1547 if (!dst)
1548 {
1549 MsgToEventLog(M_ERR, L"%S: could not find domains in search list", __func__);
1550 return;
1551 }
1552
1553 /* Cut out domains from list */
1554 size_t domlen = wcslen(domains);
1555 PCWSTR src = dst + domlen;
1556 /* Also remove the leading comma, if there is one */
1557 dst = dst > list ? dst - 1 : dst;
1558 wmemmove(dst, src, domlen);
1559
1560 size_t list_len = wcslen(list);
1561 if (list_len)
1562 {
1563 /* Now check if the shortened list equals the initial search list */
1564 WCHAR initial[2048];
1565 size = sizeof(initial);
1566 err = RegGetValueW(key, NULL, L"InitialSearchList", RRF_RT_REG_SZ, NULL, initial, &size);
1567 if (err)
1568 {
1569 MsgToEventLog(M_SYSERR, L"%S: could not get InitialSearchList from registry (%lu)",
1570 __func__, err);
1571 return;
1572 }
1573
1574 /* If the search list is back to its initial state reset it */
1575 if (wcsncmp(list, initial, wcslen(list)) == 0)
1576 {
1578 return;
1579 }
1580 }
1581
1582 size = (list_len + 1) * sizeof(list[0]);
1583 err = RegSetValueExW(key, L"SearchList", 0, REG_SZ, (PBYTE)list, size);
1584 if (err)
1585 {
1586 MsgToEventLog(M_SYSERR, L"%S: could not set SearchList in registry (%lu)", __func__, err);
1587 }
1588}
1589
1595static void
1597{
1598 BOOL gpol;
1599 HKEY dns_searchlist_key;
1600 GetDnsSearchListKey(undo_data->itf_name, &gpol, &dns_searchlist_key);
1601 if (dns_searchlist_key != INVALID_HANDLE_VALUE)
1602 {
1603 RemoveDnsSearchDomains(dns_searchlist_key, undo_data->domains);
1604 RegCloseKey(dns_searchlist_key);
1605 ApplyDnsSettings(gpol);
1606
1607 free(undo_data->domains);
1608 undo_data->domains = NULL;
1609 }
1610}
1611
1633static DWORD
1634SetDnsSearchDomains(PCSTR itf_name, PCSTR domains, PBOOL gpol, undo_lists_t *lists)
1635{
1636 DWORD err = ERROR_OUTOFMEMORY;
1637
1638 HKEY list_key;
1639 BOOL have_list = GetDnsSearchListKey(itf_name, gpol, &list_key);
1640 if (list_key == INVALID_HANDLE_VALUE)
1641 {
1642 MsgToEventLog(M_SYSERR, L"%S: could not get search list registry key", __func__);
1643 return ERROR_FILE_NOT_FOUND;
1644 }
1645
1646 /* Remove previously installed search domains */
1647 dns_domains_undo_data_t *undo_data = RemoveListItem(&(*lists)[undo_domains], CmpAny, NULL);
1648 if (undo_data)
1649 {
1650 RemoveDnsSearchDomains(list_key, undo_data->domains);
1651 free(undo_data->domains);
1652 free(undo_data);
1653 undo_data = NULL;
1654 }
1655
1656 /* If there are search domains, add them */
1657 if (domains && *domains)
1658 {
1659 wchar_t *wide_domains = utf8to16(domains); /* utf8 to wide-char */
1660 if (!wide_domains)
1661 {
1662 goto out;
1663 }
1664
1665 undo_data = malloc(sizeof(*undo_data));
1666 if (!undo_data)
1667 {
1668 free(wide_domains);
1669 wide_domains = NULL;
1670 goto out;
1671 }
1672 strncpy(undo_data->itf_name, itf_name, sizeof(undo_data->itf_name));
1673 undo_data->domains = wide_domains;
1674
1675 if (AddDnsSearchDomains(list_key, have_list, wide_domains) == FALSE
1676 || AddListItem(&(*lists)[undo_domains], undo_data) != NO_ERROR)
1677 {
1678 RemoveDnsSearchDomains(list_key, wide_domains);
1679 free(wide_domains);
1680 free(undo_data);
1681 undo_data = NULL;
1682 goto out;
1683 }
1684 }
1685
1686 err = NO_ERROR;
1687
1688out:
1689 RegCloseKey(list_key);
1690 return err;
1691}
1692
1700static BOOL
1701GetInterfacesKey(short family, PHKEY key)
1702{
1703 PCSTR itfs_key = family == AF_INET6
1704 ? "SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters\\Interfaces"
1705 : "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces";
1706
1707 LSTATUS err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, itfs_key, 0, KEY_ALL_ACCESS, key);
1708 if (err)
1709 {
1710 *key = INVALID_HANDLE_VALUE;
1711 MsgToEventLog(M_SYSERR, L"%S: could not open interfaces registry key for family %d (%lu)",
1712 __func__, family, err);
1713 }
1714
1715 return err ? FALSE : TRUE;
1716}
1717
1727static DWORD
1728SetNameServersValue(PCWSTR itf_id, short family, PCSTR value)
1729{
1730 DWORD err;
1731
1732 HKEY itfs;
1733 if (!GetInterfacesKey(family, &itfs))
1734 {
1735 return ERROR_FILE_NOT_FOUND;
1736 }
1737
1738 HKEY itf = INVALID_HANDLE_VALUE;
1739 err = RegOpenKeyExW(itfs, itf_id, 0, KEY_ALL_ACCESS, &itf);
1740 if (err)
1741 {
1742 MsgToEventLog(M_SYSERR, L"%S: could not open interface key for %s family %d (%lu)",
1743 __func__, itf_id, family, err);
1744 goto out;
1745 }
1746
1747 err = RegSetValueExA(itf, "NameServer", 0, REG_SZ, (PBYTE)value, strlen(value) + 1);
1748 if (err)
1749 {
1750 MsgToEventLog(M_SYSERR, L"%S: could not set name servers '%S' for %s family %d (%lu)",
1751 __func__, value, itf_id, family, err);
1752 }
1753
1754out:
1755 if (itf != INVALID_HANDLE_VALUE)
1756 {
1757 RegCloseKey(itf);
1758 }
1759 if (itfs != INVALID_HANDLE_VALUE)
1760 {
1761 RegCloseKey(itfs);
1762 }
1763 return err;
1764}
1765
1775static DWORD
1776SetNameServers(PCWSTR itf_id, short family, PCSTR addrs)
1777{
1778 return SetNameServersValue(itf_id, family, addrs);
1779}
1780
1789static DWORD
1790ResetNameServers(PCWSTR itf_id, short family)
1791{
1792 return SetNameServersValue(itf_id, family, "");
1793}
1794
1795static DWORD
1797{
1798 DWORD err = 0;
1799 undo_type_t undo_type = (msg->family == AF_INET6) ? undo_dns6 : undo_dns4;
1800 int addr_len = msg->addr_len;
1801
1802 /* sanity check */
1803 const size_t max_addrs = _countof(msg->addr);
1804 if (addr_len > max_addrs)
1805 {
1806 addr_len = max_addrs;
1807 }
1808
1809 if (!msg->iface.name[0]) /* interface name is required */
1810 {
1811 return ERROR_MESSAGE_DATA;
1812 }
1813
1814 /* use a non-const reference with limited scope to enforce null-termination of strings from
1815 * client */
1816 {
1818 msgptr->iface.name[_countof(msg->iface.name) - 1] = '\0';
1819 msgptr->domains[_countof(msg->domains) - 1] = '\0';
1820 }
1821
1822 WCHAR iid[64];
1823 err = InterfaceIdString(msg->iface.name, iid, _countof(iid));
1824 if (err)
1825 {
1826 return err;
1827 }
1828
1829 /* We delete all current addresses before adding any
1830 * OR if the message type is del_dns_cfg
1831 */
1832 if (addr_len > 0 || msg->header.type == msg_del_dns_cfg)
1833 {
1834 err = ResetNameServers(iid, msg->family);
1835 if (err)
1836 {
1837 return err;
1838 }
1839 free(RemoveListItem(&(*lists)[undo_type], CmpAny, iid));
1840 }
1841
1842 if (msg->header.type == msg_del_dns_cfg)
1843 {
1844 BOOL gpol = FALSE;
1845 if (msg->domains[0])
1846 {
1847 /* setting an empty domain list removes any previous value */
1848 err = SetDnsSearchDomains(msg->iface.name, NULL, &gpol, lists);
1849 }
1850 ApplyDnsSettings(gpol);
1851 return err; /* job done */
1852 }
1853
1854 if (msg->addr_len > 0)
1855 {
1856 /* prepare the comma separated address list */
1857 /* cannot use max_addrs here as that is not considered compile
1858 * time constant by all compilers and constexpr is C23 */
1859 CHAR addrs[_countof(msg->addr) * 64]; /* 64 is enough for one IPv4/6 address */
1860 size_t offset = 0;
1861 for (int i = 0; i < addr_len; ++i)
1862 {
1863 if (i != 0)
1864 {
1865 addrs[offset++] = ',';
1866 }
1867 if (msg->family == AF_INET6)
1868 {
1869 RtlIpv6AddressToStringA(&msg->addr[i].ipv6, addrs + offset);
1870 }
1871 else
1872 {
1873 RtlIpv4AddressToStringA(&msg->addr[i].ipv4, addrs + offset);
1874 }
1875 offset = strlen(addrs);
1876 }
1877
1878 err = SetNameServers(iid, msg->family, addrs);
1879 if (err)
1880 {
1881 return err;
1882 }
1883
1884 wchar_t *tmp_iid = _wcsdup(iid);
1885 if (!tmp_iid || AddListItem(&(*lists)[undo_type], tmp_iid))
1886 {
1887 free(tmp_iid);
1888 ResetNameServers(iid, msg->family);
1889 return ERROR_OUTOFMEMORY;
1890 }
1891 }
1892
1893 BOOL gpol = FALSE;
1894 if (msg->domains[0])
1895 {
1896 err = SetDnsSearchDomains(msg->iface.name, msg->domains, &gpol, lists);
1897 }
1898 ApplyDnsSettings(gpol);
1899
1900 return err;
1901}
1902
1911static BOOL
1913{
1914 DWORD dhcp;
1915 DWORD size = sizeof(dhcp);
1916 LSTATUS err;
1917
1918 err = RegGetValueA(key, NULL, "EnableDHCP", RRF_RT_REG_DWORD, NULL, (PBYTE)&dhcp, &size);
1919 if (err != NO_ERROR)
1920 {
1921 MsgToEventLog(M_SYSERR, L"%S: Could not read DHCP status (%lu)", __func__, err);
1922 return FALSE;
1923 }
1924
1925 return dhcp ? TRUE : FALSE;
1926}
1927
1936static LSTATUS
1937SetNameServerAddresses(PWSTR itf_id, const nrpt_address_t *addresses)
1938{
1939 const short families[] = { AF_INET, AF_INET6 };
1940 for (int i = 0; i < _countof(families); i++)
1941 {
1942 short family = families[i];
1943
1944 /* Create a comma sparated list of addresses of this family */
1945 int offset = 0;
1946 char addr_list[NRPT_ADDR_SIZE * NRPT_ADDR_NUM];
1947 for (int j = 0; j < NRPT_ADDR_NUM && addresses[j][0]; j++)
1948 {
1949 if ((family == AF_INET6 && strchr(addresses[j], ':') == NULL)
1950 || (family == AF_INET && strchr(addresses[j], ':') != NULL))
1951 {
1952 /* Address family doesn't match, skip this one */
1953 continue;
1954 }
1955 if (offset)
1956 {
1957 addr_list[offset++] = ',';
1958 }
1959 strcpy(addr_list + offset, addresses[j]);
1960 offset += strlen(addresses[j]);
1961 }
1962
1963 if (offset == 0)
1964 {
1965 /* No address for this family to set */
1966 continue;
1967 }
1968
1969 /* Set name server addresses */
1970 LSTATUS err = SetNameServers(itf_id, family, addr_list);
1971 if (err)
1972 {
1973 return err;
1974 }
1975 }
1976 return NO_ERROR;
1977}
1978
1989static LSTATUS
1990GetItfDnsServersV4(HKEY itf_key, PSTR addrs, PDWORD size)
1991{
1992 addrs[*size - 1] = '\0';
1993
1994 LSTATUS err;
1995 DWORD s = *size;
1996 err = RegGetValueA(itf_key, NULL, "NameServer", RRF_RT_REG_SZ, NULL, (PBYTE)addrs, &s);
1997 if (err && err != ERROR_FILE_NOT_FOUND)
1998 {
1999 *size = 0;
2000 return err;
2001 }
2002
2003 /* Try DHCP addresses if we don't have some already */
2004 if (!strchr(addrs, '.') && IsDhcpEnabled(itf_key))
2005 {
2006 s = *size;
2007 RegGetValueA(itf_key, NULL, "DhcpNameServer", RRF_RT_REG_SZ, NULL, (PBYTE)addrs, &s);
2008 if (err)
2009 {
2010 *size = 0;
2011 return err;
2012 }
2013 }
2014
2015 if (strchr(addrs, '.'))
2016 {
2017 *size = s;
2018 return NO_ERROR;
2019 }
2020
2021 *size = 0;
2022 return ERROR_FILE_NOT_FOUND;
2023}
2024
2034static LSTATUS
2035GetItfDnsServersV6(HKEY itf_key, PSTR addrs, PDWORD size)
2036{
2037 addrs[*size - 1] = '\0';
2038
2039 LSTATUS err;
2040 DWORD s = *size;
2041 err = RegGetValueA(itf_key, NULL, "NameServer", RRF_RT_REG_SZ, NULL, (PBYTE)addrs, &s);
2042 if (err && err != ERROR_FILE_NOT_FOUND)
2043 {
2044 *size = 0;
2045 return err;
2046 }
2047
2048 /* Try DHCP addresses if we don't have some already */
2049 if (!strchr(addrs, ':') && IsDhcpEnabled(itf_key))
2050 {
2051 IN6_ADDR in_addrs[8];
2052 DWORD in_addrs_size = sizeof(in_addrs);
2053 err = RegGetValueA(itf_key, NULL, "Dhcpv6DNSServers", RRF_RT_REG_BINARY, NULL,
2054 (PBYTE)in_addrs, &in_addrs_size);
2055 if (err)
2056 {
2057 *size = 0;
2058 return err;
2059 }
2060
2061 s = *size;
2062 PSTR pos = addrs;
2063 size_t in_addrs_read = in_addrs_size / sizeof(IN6_ADDR);
2064 for (size_t i = 0; i < in_addrs_read; ++i)
2065 {
2066 if (i != 0)
2067 {
2068 /* Add separator */
2069 *pos++ = ',';
2070 s--;
2071 }
2072
2073 if (inet_ntop(AF_INET6, &in_addrs[i], pos, s) != NULL)
2074 {
2075 *size = 0;
2076 return ERROR_MORE_DATA;
2077 }
2078
2079 size_t addr_len = strlen(pos);
2080 pos += addr_len;
2081 s -= addr_len;
2082 }
2083 s = strlen(addrs) + 1;
2084 }
2085
2086 if (strchr(addrs, ':'))
2087 {
2088 *size = s;
2089 return NO_ERROR;
2090 }
2091
2092 *size = 0;
2093 return ERROR_FILE_NOT_FOUND;
2094}
2095
2105static BOOL
2106ListContainsDomain(PCWSTR list, PCWSTR domain, size_t len)
2107{
2108 PCWSTR match = list;
2109 while (match)
2110 {
2111 match = wcsstr(match, domain);
2112 if (!match)
2113 {
2114 /* Domain has not matched */
2115 break;
2116 }
2117 if ((match == list || *(match - 1) == ',')
2118 && (*(match + len) == ',' || *(match + len) == '\0'))
2119 {
2120 /* Domain has matched fully */
2121 return TRUE;
2122 }
2123 match += len;
2124 }
2125 return FALSE;
2126}
2127
2149static LSTATUS
2150GetItfDnsDomains(HKEY itf, PCWSTR search_domains, PWSTR domains, PDWORD size)
2151{
2152 if (domains == NULL || size == NULL || *size == 0)
2153 {
2154 return ERROR_INVALID_PARAMETER;
2155 }
2156
2157 LSTATUS err = ERROR_FILE_NOT_FOUND;
2158 const DWORD buf_size = *size;
2159 const size_t one_glyph = sizeof(*domains);
2160 PWSTR values[] = { L"SearchList", L"Domain", L"DhcpDomainSearchList", L"DhcpDomain", NULL };
2161
2162 for (int i = 0; values[i]; i++)
2163 {
2164 *size = buf_size;
2165 err = RegGetValueW(itf, NULL, values[i], RRF_RT_REG_SZ, NULL, (PBYTE)domains, size);
2166 if (!err && *size > one_glyph && domains[(*size / one_glyph) - 1] == '\0' && wcschr(domains, '.'))
2167 {
2168 /*
2169 * Found domain(s), now convert them:
2170 * - prefix each domain with a dot
2171 * - convert comma separated list to MULTI_SZ
2172 */
2173 PWCHAR pos = domains;
2174 const DWORD buf_len = buf_size / one_glyph;
2175 while (TRUE)
2176 {
2177 /* Terminate the domain at the next comma */
2178 PWCHAR comma = wcschr(pos, ',');
2179 if (comma)
2180 {
2181 *comma = '\0';
2182 }
2183
2184 /* Ignore itf domains which match a pushed search domain */
2185 size_t domain_len = wcslen(pos);
2186 if (ListContainsDomain(search_domains, pos, domain_len))
2187 {
2188 if (comma)
2189 {
2190 pos = comma + 1;
2191 continue;
2192 }
2193 else
2194 {
2195 /* This was the last domain */
2196 *pos = '\0';
2197 *size += one_glyph;
2198 return wcslen(domains) ? NO_ERROR : ERROR_FILE_NOT_FOUND;
2199 }
2200 }
2201
2202 /* Check for enough space to convert this domain */
2203 domain_len += 1; /* leading dot */
2204 size_t converted_size = pos - domains;
2205 size_t domain_size = domain_len * one_glyph;
2206 size_t extra_size = 2 * one_glyph;
2207 if (converted_size + domain_size + extra_size > buf_size)
2208 {
2209 /* Domain doesn't fit, bad luck if it's the first one */
2210 *pos = '\0';
2211 *size = converted_size == 0 ? 0 : *size + 1;
2212 return ERROR_MORE_DATA;
2213 }
2214
2215 /* Prefix domain at pos with the dot */
2216 memmove(pos + 1, pos, buf_size - converted_size - one_glyph);
2217 domains[buf_len - 1] = '\0';
2218 *pos = '.';
2219 *size += one_glyph;
2220
2221 if (!comma)
2222 {
2223 /* Conversion is done */
2224 *(pos + domain_len) = '\0';
2225 *size += one_glyph;
2226 return NO_ERROR;
2227 }
2228
2229 pos = comma + 1;
2230 }
2231 }
2232 }
2233
2234 *size = 0;
2235 return err;
2236}
2237
2246static BOOL
2248{
2249 GUID iid;
2250 BOOL res = FALSE;
2251 MIB_IF_ROW2 itf_row;
2252
2253 /* Get GUID from string */
2254 if (IIDFromString(iid_str, &iid) != S_OK)
2255 {
2256 MsgToEventLog(M_SYSERR, L"%S: could not convert interface %s GUID string", __func__,
2257 iid_str);
2258 goto out;
2259 }
2260
2261 /* Get LUID from GUID */
2262 if (ConvertInterfaceGuidToLuid(&iid, &itf_row.InterfaceLuid) != NO_ERROR)
2263 {
2264 goto out;
2265 }
2266
2267 /* Look up interface status */
2268 if (GetIfEntry2(&itf_row) != NO_ERROR)
2269 {
2270 MsgToEventLog(M_SYSERR, L"%S: could not get interface %s status", __func__, iid_str);
2271 goto out;
2272 }
2273
2274 if (itf_row.MediaConnectState == MediaConnectStateConnected
2275 && itf_row.OperStatus == IfOperStatusUp)
2276 {
2277 res = TRUE;
2278 }
2279
2280out:
2281 return res;
2282}
2283
2293static void
2294GetNrptExcludeData(PCWSTR search_domains, nrpt_exclude_data_t *data, size_t data_size)
2295{
2296 HKEY v4_itfs = INVALID_HANDLE_VALUE;
2297 HKEY v6_itfs = INVALID_HANDLE_VALUE;
2298
2299 if (!GetInterfacesKey(AF_INET, &v4_itfs) || !GetInterfacesKey(AF_INET6, &v6_itfs))
2300 {
2301 goto out;
2302 }
2303
2304 size_t i = 0;
2305 DWORD enum_index = 0;
2306 while (i < data_size)
2307 {
2308 WCHAR itf_guid[MAX_PATH];
2309 DWORD itf_guid_len = _countof(itf_guid);
2310 LSTATUS err =
2311 RegEnumKeyExW(v4_itfs, enum_index++, itf_guid, &itf_guid_len, NULL, NULL, NULL, NULL);
2312 if (err)
2313 {
2314 if (err != ERROR_NO_MORE_ITEMS)
2315 {
2316 MsgToEventLog(M_SYSERR, L"%S: could not enumerate interfaces (%lu)", __func__, err);
2317 }
2318 goto out;
2319 }
2320
2321 /* Ignore interfaces that are not connected or disabled */
2322 if (!IsInterfaceConnected(itf_guid))
2323 {
2324 continue;
2325 }
2326
2327 HKEY v4_itf;
2328 if (RegOpenKeyExW(v4_itfs, itf_guid, 0, KEY_READ, &v4_itf) != NO_ERROR)
2329 {
2330 MsgToEventLog(M_SYSERR, L"%S: could not open interface %s v4 registry key", __func__,
2331 itf_guid);
2332 goto out;
2333 }
2334
2335 /* Get the DNS domain(s) for exclude routing */
2336 data[i].domains_size = sizeof(data[0].domains);
2337 memset(data[i].domains, 0, data[i].domains_size);
2338 err = GetItfDnsDomains(v4_itf, search_domains, data[i].domains, &data[i].domains_size);
2339 if (err)
2340 {
2341 if (err != ERROR_FILE_NOT_FOUND)
2342 {
2343 MsgToEventLog(M_SYSERR, L"%S: could not read interface %s domain suffix", __func__,
2344 itf_guid);
2345 }
2346 goto next_itf;
2347 }
2348
2349 /* Get the IPv4 DNS servers */
2350 DWORD v4_addrs_size = sizeof(data[0].addresses);
2351 err = GetItfDnsServersV4(v4_itf, data[i].addresses, &v4_addrs_size);
2352 if (err && err != ERROR_FILE_NOT_FOUND)
2353 {
2354 MsgToEventLog(M_SYSERR, L"%S: could not read interface %s v4 name servers (%ld)",
2355 __func__, itf_guid, err);
2356 goto next_itf;
2357 }
2358
2359 /* Get the IPv6 DNS servers, if there's space left */
2360 PSTR v6_addrs = data[i].addresses + v4_addrs_size;
2361 DWORD v6_addrs_size = sizeof(data[0].addresses) - v4_addrs_size;
2362 if (v6_addrs_size > NRPT_ADDR_SIZE)
2363 {
2364 HKEY v6_itf;
2365 if (RegOpenKeyExW(v6_itfs, itf_guid, 0, KEY_READ, &v6_itf) != NO_ERROR)
2366 {
2367 MsgToEventLog(M_SYSERR, L"%S: could not open interface %s v6 registry key",
2368 __func__, itf_guid);
2369 goto next_itf;
2370 }
2371 err = GetItfDnsServersV6(v6_itf, v6_addrs, &v6_addrs_size);
2372 RegCloseKey(v6_itf);
2373 if (err && err != ERROR_FILE_NOT_FOUND)
2374 {
2375 MsgToEventLog(M_SYSERR, L"%S: could not read interface %s v6 name servers (%ld)",
2376 __func__, itf_guid, err);
2377 goto next_itf;
2378 }
2379 }
2380
2381 if (v4_addrs_size || v6_addrs_size)
2382 {
2383 /* Replace delimiters with semicolons, as required by NRPT */
2384 for (int j = 0; j < sizeof(data[0].addresses) && data[i].addresses[j]; j++)
2385 {
2386 if (data[i].addresses[j] == ',' || data[i].addresses[j] == ' ')
2387 {
2388 data[i].addresses[j] = ';';
2389 }
2390 }
2391 ++i;
2392 }
2393
2394next_itf:
2395 RegCloseKey(v4_itf);
2396 }
2397
2398out:
2399 RegCloseKey(v6_itfs);
2400 RegCloseKey(v4_itfs);
2401}
2402
2415static DWORD
2416SetNrptRule(HKEY nrpt_key, PCWSTR subkey, PCSTR address, PCWSTR domains, DWORD dom_size,
2417 BOOL dnssec)
2418{
2419 /* Create rule subkey */
2420 DWORD err = NO_ERROR;
2421 HKEY rule_key;
2422 err = RegCreateKeyExW(nrpt_key, subkey, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &rule_key, NULL);
2423 if (err)
2424 {
2425 return err;
2426 }
2427
2428 /* Set name(s) for DNS routing */
2429 err = RegSetValueExW(rule_key, L"Name", 0, REG_MULTI_SZ, (PBYTE)domains, dom_size);
2430 if (err)
2431 {
2432 goto out;
2433 }
2434
2435 /* Set DNS Server address */
2436 err = RegSetValueExA(rule_key, "GenericDNSServers", 0, REG_SZ, (PBYTE)address,
2437 strlen(address) + 1);
2438 if (err)
2439 {
2440 goto out;
2441 }
2442
2443 DWORD reg_val;
2444 /* Set DNSSEC if required */
2445 if (dnssec)
2446 {
2447 reg_val = 1;
2448 err = RegSetValueExA(rule_key, "DNSSECValidationRequired", 0, REG_DWORD, (PBYTE)&reg_val,
2449 sizeof(reg_val));
2450 if (err)
2451 {
2452 goto out;
2453 }
2454
2455 reg_val = 0;
2456 err = RegSetValueExA(rule_key, "DNSSECQueryIPSECRequired", 0, REG_DWORD, (PBYTE)&reg_val,
2457 sizeof(reg_val));
2458 if (err)
2459 {
2460 goto out;
2461 }
2462
2463 reg_val = 0;
2464 err = RegSetValueExA(rule_key, "DNSSECQueryIPSECEncryption", 0, REG_DWORD, (PBYTE)&reg_val,
2465 sizeof(reg_val));
2466 if (err)
2467 {
2468 goto out;
2469 }
2470 }
2471
2472 /* Set NRPT config options */
2473 reg_val = dnssec ? 0x0000000A : 0x00000008;
2474 err = RegSetValueExA(rule_key, "ConfigOptions", 0, REG_DWORD, (const PBYTE)&reg_val,
2475 sizeof(reg_val));
2476 if (err)
2477 {
2478 goto out;
2479 }
2480
2481 /* Mandatory NRPT version */
2482 reg_val = 2;
2483 err = RegSetValueExA(rule_key, "Version", 0, REG_DWORD, (const PBYTE)&reg_val, sizeof(reg_val));
2484 if (err)
2485 {
2486 goto out;
2487 }
2488
2489out:
2490 if (err)
2491 {
2492 RegDeleteKeyW(nrpt_key, subkey);
2493 }
2494 RegCloseKey(rule_key);
2495 return err;
2496}
2497
2507static void
2508SetNrptExcludeRules(HKEY nrpt_key, DWORD ovpn_pid, PCWSTR search_domains)
2509{
2510 nrpt_exclude_data_t data[8]; /* data from up to 8 interfaces */
2511 memset(data, 0, sizeof(data));
2512 GetNrptExcludeData(search_domains, data, _countof(data));
2513
2514 unsigned n = 0;
2515 for (int i = 0; i < _countof(data); ++i)
2516 {
2517 nrpt_exclude_data_t *d = &data[i];
2518 if (d->domains_size == 0)
2519 {
2520 break;
2521 }
2522
2523 DWORD err;
2524 WCHAR subkey[48];
2525 swprintf(subkey, _countof(subkey), L"OpenVPNDNSRoutingX-%02x-%lu", ++n, ovpn_pid);
2526 err = SetNrptRule(nrpt_key, subkey, d->addresses, d->domains, d->domains_size, FALSE);
2527 if (err)
2528 {
2529 MsgToEventLog(M_ERR, L"%S: failed to set rule %s (%lu)", __func__, subkey, err);
2530 }
2531 }
2532}
2533
2546static DWORD
2547SetNrptRules(HKEY nrpt_key, const nrpt_address_t *addresses, const char *domains,
2548 const char *search_domains, BOOL dnssec, DWORD ovpn_pid)
2549{
2550 DWORD err = NO_ERROR;
2551 PWSTR wide_domains = L".\0"; /* DNS route everything by default */
2552 DWORD dom_size = 6;
2553
2554 /* Prepare DNS routing domains / split DNS */
2555 if (domains[0])
2556 {
2557 size_t domains_len = strlen(domains);
2558 dom_size = domains_len + 2; /* len + the trailing NULs */
2559
2560 wide_domains = utf8to16_size(domains, dom_size);
2561 dom_size *= sizeof(*wide_domains);
2562 if (!wide_domains)
2563 {
2564 return ERROR_OUTOFMEMORY;
2565 }
2566 /* Make a MULTI_SZ from a comma separated list */
2567 for (size_t i = 0; i < domains_len; ++i)
2568 {
2569 if (wide_domains[i] == ',')
2570 {
2571 wide_domains[i] = 0;
2572 }
2573 }
2574 }
2575 else
2576 {
2577 PWSTR wide_search_domains;
2578 wide_search_domains = utf8to16(search_domains);
2579 if (!wide_search_domains)
2580 {
2581 return ERROR_OUTOFMEMORY;
2582 }
2583 SetNrptExcludeRules(nrpt_key, ovpn_pid, wide_search_domains);
2584 free(wide_search_domains);
2585 }
2586
2587 /* Create address string list */
2588 CHAR addr_list[NRPT_ADDR_NUM * NRPT_ADDR_SIZE];
2589 PSTR pos = addr_list;
2590 for (int i = 0; i < NRPT_ADDR_NUM && addresses[i][0]; ++i)
2591 {
2592 if (i != 0)
2593 {
2594 *pos++ = ';';
2595 }
2596 strcpy(pos, addresses[i]);
2597 pos += strlen(pos);
2598 }
2599
2600 WCHAR subkey[MAX_PATH];
2601 swprintf(subkey, _countof(subkey), L"OpenVPNDNSRouting-%lu", ovpn_pid);
2602 err = SetNrptRule(nrpt_key, subkey, addr_list, wide_domains, dom_size, dnssec);
2603 if (err)
2604 {
2605 MsgToEventLog(M_ERR, L"%S: failed to set rule %s (%lu)", __func__, subkey, err);
2606 }
2607
2608 if (domains[0])
2609 {
2610 free(wide_domains);
2611 }
2612 return err;
2613}
2614
2615#if defined(__GNUC__) || defined(__clang__)
2616#pragma GCC diagnostic pop
2617#endif
2618
2627static LSTATUS
2628OpenNrptBaseKey(PHKEY key, PBOOL gpol)
2629{
2630 /*
2631 * Registry keys Name Service Policy Table (NRPT) rules can be stored at.
2632 * When the group policy key exists, NRPT rules must be placed there.
2633 * It is created when NRPT rules are pushed via group policy and it
2634 * remains in the registry even if the last GP-NRPT rule is deleted.
2635 */
2636 static PCSTR gpol_key = "SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient\\DnsPolicyConfig";
2637 static PCSTR sys_key =
2638 "SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters\\DnsPolicyConfig";
2639
2640 HKEY nrpt;
2641 *gpol = TRUE;
2642 LSTATUS err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, gpol_key, 0, KEY_ALL_ACCESS, &nrpt);
2643 if (err == ERROR_FILE_NOT_FOUND)
2644 {
2645 *gpol = FALSE;
2646 err = RegCreateKeyExA(HKEY_LOCAL_MACHINE, sys_key, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &nrpt,
2647 NULL);
2648 if (err)
2649 {
2650 nrpt = INVALID_HANDLE_VALUE;
2651 }
2652 }
2653 *key = nrpt;
2654 return err;
2655}
2656
2668static BOOL
2669DeleteNrptRules(DWORD pid, PBOOL gpol)
2670{
2671 HKEY key;
2672 LSTATUS err = OpenNrptBaseKey(&key, gpol);
2673 if (err)
2674 {
2675 MsgToEventLog(M_SYSERR, L"%S: could not open NRPT base key (%lu)", __func__, err);
2676 return FALSE;
2677 }
2678
2679 /* PID suffix string to compare against later */
2680 WCHAR pid_str[16];
2681 size_t pidlen = 0;
2682 if (pid)
2683 {
2684 swprintf(pid_str, _countof(pid_str), L"-%lu", pid);
2685 pidlen = wcslen(pid_str);
2686 }
2687
2688 int deleted = 0;
2689 DWORD enum_index = 0;
2690 while (TRUE)
2691 {
2692 WCHAR name[MAX_PATH];
2693 DWORD namelen = _countof(name);
2694 err = RegEnumKeyExW(key, enum_index++, name, &namelen, NULL, NULL, NULL, NULL);
2695 if (err)
2696 {
2697 if (err != ERROR_NO_MORE_ITEMS)
2698 {
2699 MsgToEventLog(M_SYSERR, L"%S: could not enumerate NRPT rules (%lu)", __func__, err);
2700 }
2701 break;
2702 }
2703
2704 /* Keep rule if name doesn't match */
2705 if (wcsncmp(name, L"OpenVPNDNSRouting", 17) != 0
2706 || (pid && wcsncmp(name + namelen - pidlen, pid_str, pidlen) != 0))
2707 {
2708 continue;
2709 }
2710
2711 if (RegDeleteKeyW(key, name) == NO_ERROR)
2712 {
2713 enum_index--;
2714 deleted++;
2715 }
2716 }
2717
2718 RegCloseKey(key);
2719 return deleted ? TRUE : FALSE;
2720}
2721
2727static void
2728UndoNrptRules(DWORD ovpn_pid)
2729{
2730 BOOL gpol;
2731 if (DeleteNrptRules(ovpn_pid, &gpol))
2732 {
2733 ApplyDnsSettings(gpol);
2734 }
2735}
2736
2748static DWORD
2750{
2751 /*
2752 * Use a non-const reference with limited scope to
2753 * enforce null-termination of strings from client
2754 */
2755 {
2757 msgptr->iface.name[_countof(msg->iface.name) - 1] = '\0';
2758 msgptr->search_domains[_countof(msg->search_domains) - 1] = '\0';
2759 msgptr->resolve_domains[_countof(msg->resolve_domains) - 1] = '\0';
2760 for (size_t i = 0; i < NRPT_ADDR_NUM; ++i)
2761 {
2762 msgptr->addresses[i][_countof(msg->addresses[0]) - 1] = '\0';
2763 }
2764 }
2765
2766 /* Make sure we have the VPN interface name */
2767 if (msg->iface.name[0] == 0)
2768 {
2769 return ERROR_MESSAGE_DATA;
2770 }
2771
2772 /* Some sanity checks on the add message data */
2773 if (msg->header.type == msg_add_nrpt_cfg)
2774 {
2775 /* At least one name server address is set */
2776 if (msg->addresses[0][0] == 0)
2777 {
2778 return ERROR_MESSAGE_DATA;
2779 }
2780 /* Resolve domains are double zero terminated (MULTI_SZ) */
2781 const char *rdom = msg->resolve_domains;
2782 size_t rdom_size = sizeof(msg->resolve_domains);
2783 size_t rdom_len = strlen(rdom);
2784 if (rdom_len && (rdom_len + 1 >= rdom_size || rdom[rdom_len + 2] != 0))
2785 {
2786 return ERROR_MESSAGE_DATA;
2787 }
2788 }
2789
2790 BOOL gpol_nrpt = FALSE;
2791 BOOL gpol_list = FALSE;
2792
2793 WCHAR iid[64];
2794 DWORD iid_err = InterfaceIdString(msg->iface.name, iid, _countof(iid));
2795 if (iid_err)
2796 {
2797 return iid_err;
2798 }
2799
2800 /* Delete previously set values for this instance first, if any */
2801 PDWORD undo_pid = RemoveListItem(&(*lists)[undo_nrpt], CmpAny, NULL);
2802 if (undo_pid)
2803 {
2804 if (*undo_pid != ovpn_pid)
2805 {
2807 L"%S: PID stored for undo doesn't match: %lu vs %lu. "
2808 "This is likely an error. Cleaning up anyway.",
2809 __func__, *undo_pid, ovpn_pid);
2810 }
2811 DeleteNrptRules(*undo_pid, &gpol_nrpt);
2812 free(undo_pid);
2813
2814 ResetNameServers(iid, AF_INET);
2815 ResetNameServers(iid, AF_INET6);
2816 }
2817 SetDnsSearchDomains(msg->iface.name, NULL, &gpol_list, lists);
2818
2819 if (msg->header.type == msg_del_nrpt_cfg)
2820 {
2821 ApplyDnsSettings(gpol_nrpt || gpol_list);
2822 return NO_ERROR; /* Done dealing with del message */
2823 }
2824
2825 HKEY key;
2826 LSTATUS err = OpenNrptBaseKey(&key, &gpol_nrpt);
2827 if (err)
2828 {
2829 goto out;
2830 }
2831
2832 /* Add undo information first in case there's no heap left */
2833 PDWORD pid = malloc(sizeof(ovpn_pid));
2834 if (!pid)
2835 {
2836 err = ERROR_OUTOFMEMORY;
2837 goto out;
2838 }
2839 *pid = ovpn_pid;
2840 if (AddListItem(&(*lists)[undo_nrpt], pid))
2841 {
2842 err = ERROR_OUTOFMEMORY;
2843 free(pid);
2844 goto out;
2845 }
2846
2847 /* Set NRPT rules */
2848 BOOL dnssec = (msg->flags & nrpt_dnssec) != 0;
2849 err = SetNrptRules(key, msg->addresses, msg->resolve_domains, msg->search_domains, dnssec,
2850 ovpn_pid);
2851 if (err)
2852 {
2853 goto out;
2854 }
2855
2856 /* Set name servers */
2857 err = SetNameServerAddresses(iid, msg->addresses);
2858 if (err)
2859 {
2860 goto out;
2861 }
2862
2863 /* Set search domains, if any */
2864 if (msg->search_domains[0])
2865 {
2866 err = SetDnsSearchDomains(msg->iface.name, msg->search_domains, &gpol_list, lists);
2867 }
2868
2869 ApplyDnsSettings(gpol_nrpt || gpol_list);
2870
2871out:
2872 return err;
2873}
2874
2875static DWORD
2877{
2878 DWORD err = NO_ERROR;
2879 wchar_t addr[16]; /* large enough to hold string representation of an ipv4 */
2880 int addr_len = msg->addr_len;
2881
2882 /* sanity check */
2883 if (addr_len > _countof(msg->addr))
2884 {
2885 addr_len = _countof(msg->addr);
2886 }
2887
2888 if (!msg->iface.index) /* interface index is required */
2889 {
2890 return ERROR_MESSAGE_DATA;
2891 }
2892
2893 /* We delete all current addresses before adding any
2894 * OR if the message type is del_wins_cfg
2895 */
2896 if (addr_len > 0 || msg->header.type == msg_del_wins_cfg)
2897 {
2898 err = netsh_wins_cmd(L"delete", msg->iface.index, NULL);
2899 if (err)
2900 {
2901 goto out;
2902 }
2903 free(RemoveListItem(&(*lists)[undo_wins], CmpAny, NULL));
2904 }
2905
2906 if (addr_len == 0 || msg->header.type == msg_del_wins_cfg)
2907 {
2908 goto out; /* job done */
2909 }
2910
2911 for (int i = 0; i < addr_len; ++i)
2912 {
2913 RtlIpv4AddressToStringW(&msg->addr[i].ipv4, addr);
2914 err = netsh_wins_cmd(i == 0 ? L"set" : L"add", msg->iface.index, addr);
2915 if (i == 0 && err)
2916 {
2917 goto out;
2918 }
2919 /* We do not check for duplicate addresses, so any error in adding
2920 * additional addresses is ignored.
2921 */
2922 }
2923
2924 int *if_index = malloc(sizeof(msg->iface.index));
2925 if (if_index)
2926 {
2927 *if_index = msg->iface.index;
2928 }
2929
2930 if (!if_index || AddListItem(&(*lists)[undo_wins], if_index))
2931 {
2932 free(if_index);
2933 netsh_wins_cmd(L"delete", msg->iface.index, NULL);
2934 err = ERROR_OUTOFMEMORY;
2935 goto out;
2936 }
2937
2938 err = 0;
2939
2940out:
2941 return err;
2942}
2943
2944static DWORD
2946{
2947 DWORD err = 0;
2948 DWORD timeout = 5000; /* in milli seconds */
2949 wchar_t argv0[MAX_PATH];
2950
2951 /* Path of netsh */
2952 swprintf(argv0, _countof(argv0), L"%ls\\%ls", get_win_sys_path(), L"netsh.exe");
2953
2954 /* cmd template:
2955 * netsh interface ipv4 set address name=$if_index source=dhcp
2956 */
2957 const wchar_t *fmt = L"netsh interface ipv4 set address name=\"%d\" source=dhcp";
2958
2959 /* max cmdline length in wchars -- include room for if index:
2960 * 10 chars for 32 bit int in decimal and +1 for NUL
2961 */
2962 size_t ncmdline = wcslen(fmt) + 10 + 1;
2963 wchar_t *cmdline = malloc(ncmdline * sizeof(wchar_t));
2964 if (!cmdline)
2965 {
2966 err = ERROR_OUTOFMEMORY;
2967 return err;
2968 }
2969
2970 swprintf(cmdline, ncmdline, fmt, dhcp->iface.index);
2971
2972 err = ExecCommand(argv0, cmdline, timeout);
2973
2974 /* Note: This could fail if dhcp is already enabled, so the caller
2975 * may not want to treat errors as FATAL.
2976 */
2977
2978 free(cmdline);
2979 return err;
2980}
2981
2982static DWORD
2984{
2985 DWORD err = 0;
2986 MIB_IPINTERFACE_ROW ipiface;
2987 InitializeIpInterfaceEntry(&ipiface);
2988 ipiface.Family = mtu->family;
2989 ipiface.InterfaceIndex = mtu->iface.index;
2990 err = GetIpInterfaceEntry(&ipiface);
2991 if (err != NO_ERROR)
2992 {
2993 return err;
2994 }
2995 if (mtu->family == AF_INET)
2996 {
2997 ipiface.SitePrefixLength = 0;
2998 }
2999 ipiface.NlMtu = mtu->mtu;
3000
3001 err = SetIpInterfaceEntry(&ipiface);
3002 return err;
3003}
3004
3012static DWORD
3014{
3015 const WCHAR *hwid;
3016
3017 switch (msg->adapter_type)
3018 {
3019 case ADAPTER_TYPE_DCO:
3020 hwid = L"ovpn-dco";
3021 break;
3022
3023 case ADAPTER_TYPE_TAP:
3024 hwid = L"root\\tap0901";
3025 break;
3026
3027 default:
3028 return ERROR_INVALID_PARAMETER;
3029 }
3030
3031 WCHAR cmd[MAX_PATH];
3032 WCHAR args[MAX_PATH];
3033
3034 if (swprintf_s(cmd, _countof(cmd), L"%s\\tapctl.exe", settings.bin_dir) < 0)
3035 {
3036 return ERROR_BUFFER_OVERFLOW;
3037 }
3038
3039 if (swprintf_s(args, _countof(args), L"tapctl create --hwid %s", hwid) < 0)
3040 {
3041 return ERROR_BUFFER_OVERFLOW;
3042 }
3043
3044 return ExecCommand(cmd, args, 10000);
3045}
3046
3047static VOID
3048HandleMessage(HANDLE pipe, PPROCESS_INFORMATION proc_info, DWORD bytes, DWORD count,
3049 LPHANDLE events, undo_lists_t *lists)
3050{
3052 ack_message_t ack = {
3053 .header = { .type = msg_acknowledgement, .size = sizeof(ack), .message_id = -1 },
3054 .error_number = ERROR_MESSAGE_DATA
3055 };
3056
3057 DWORD read = ReadPipeAsync(pipe, &msg, bytes, count, events);
3058 if (read != bytes || read < sizeof(msg.header) || read != msg.header.size)
3059 {
3060 goto out;
3061 }
3062
3063 ack.header.message_id = msg.header.message_id;
3064
3065 switch (msg.header.type)
3066 {
3067 case msg_add_address:
3068 case msg_del_address:
3069 if (msg.header.size == sizeof(msg.address))
3070 {
3071 ack.error_number = HandleAddressMessage(&msg.address, lists);
3072 }
3073 break;
3074
3075 case msg_add_route:
3076 case msg_del_route:
3077 if (msg.header.size == sizeof(msg.route))
3078 {
3079 ack.error_number = HandleRouteMessage(&msg.route, lists);
3080 }
3081 break;
3082
3084 if (msg.header.size == sizeof(msg.flush_neighbors))
3085 {
3086 ack.error_number = HandleFlushNeighborsMessage(&msg.flush_neighbors);
3087 }
3088 break;
3089
3090 case msg_add_wfp_block:
3091 case msg_del_wfp_block:
3092 if (msg.header.size == sizeof(msg.wfp_block))
3093 {
3094 ack.error_number = HandleWfpBlockMessage(&msg.wfp_block, lists);
3095 }
3096 break;
3097
3098 case msg_register_dns:
3100 break;
3101
3102 case msg_add_dns_cfg:
3103 case msg_del_dns_cfg:
3104 ack.error_number = HandleDNSConfigMessage(&msg.dns, lists);
3105 break;
3106
3107 case msg_add_nrpt_cfg:
3108 case msg_del_nrpt_cfg:
3109 {
3110 DWORD ovpn_pid = proc_info->dwProcessId;
3111 ack.error_number = HandleDNSConfigNrptMessage(&msg.nrpt_dns, ovpn_pid, lists);
3112 }
3113 break;
3114
3115 case msg_add_wins_cfg:
3116 case msg_del_wins_cfg:
3117 ack.error_number = HandleWINSConfigMessage(&msg.wins, lists);
3118 break;
3119
3120 case msg_enable_dhcp:
3121 if (msg.header.size == sizeof(msg.dhcp))
3122 {
3124 }
3125 break;
3126
3127 case msg_set_mtu:
3128 if (msg.header.size == sizeof(msg.mtu))
3129 {
3130 ack.error_number = HandleMTUMessage(&msg.mtu);
3131 }
3132 break;
3133
3134 case msg_create_adapter:
3135 if (msg.header.size == sizeof(msg.create_adapter))
3136 {
3137 ack.error_number = HandleCreateAdapterMessage(&msg.create_adapter);
3138 }
3139 break;
3140
3141 default:
3143 MsgToEventLog(MSG_FLAGS_ERROR, L"Unknown message type %d", msg.header.type);
3144 break;
3145 }
3146
3147out:
3148 WritePipeAsync(pipe, &ack, sizeof(ack), count, events);
3149}
3150
3151
3152static VOID
3154{
3155 undo_type_t type;
3156 wfp_block_data_t *interface_data;
3157 for (type = 0; type < _undo_type_max; type++)
3158 {
3159 list_item_t **pnext = &(*lists)[type];
3160 while (*pnext)
3161 {
3162 list_item_t *item = *pnext;
3163 switch (type)
3164 {
3165 case address:
3166 DeleteAddress(item->data);
3167 break;
3168
3169 case route:
3170 DeleteRoute(item->data);
3171 break;
3172
3173 case undo_dns4:
3174 ResetNameServers(item->data, AF_INET);
3175 break;
3176
3177 case undo_dns6:
3178 ResetNameServers(item->data, AF_INET6);
3179 break;
3180
3181 case undo_nrpt:
3182 UndoNrptRules(*(PDWORD)item->data);
3183 break;
3184
3185 case undo_domains:
3187 break;
3188
3189 case undo_wins:
3190 netsh_wins_cmd(L"delete", *(int *)item->data, NULL);
3191 break;
3192
3193 case wfp_block:
3194 interface_data = (wfp_block_data_t *)(item->data);
3195 delete_wfp_block_filters(interface_data->engine);
3196 if (interface_data->metric_v4 >= 0)
3197 {
3198 set_interface_metric(interface_data->index, AF_INET,
3199 interface_data->metric_v4);
3200 }
3201 if (interface_data->metric_v6 >= 0)
3202 {
3203 set_interface_metric(interface_data->index, AF_INET6,
3204 interface_data->metric_v6);
3205 }
3206 break;
3207
3208 case _undo_type_max:
3209 /* unreachable */
3210 break;
3211 }
3212
3213 /* Remove from the list and free memory */
3214 *pnext = item->next;
3215 free(item->data);
3216 free(item);
3217 }
3218 }
3219}
3220
3221static DWORD WINAPI
3222RunOpenvpn(LPVOID p)
3223{
3224 HANDLE pipe = p;
3225 HANDLE ovpn_pipe = NULL, svc_pipe = NULL;
3226 PTOKEN_USER svc_user = NULL, ovpn_user = NULL;
3227 HANDLE svc_token = NULL, imp_token = NULL, pri_token = NULL;
3228 HANDLE stdin_read = NULL, stdin_write = NULL;
3229 HANDLE stdout_write = NULL;
3230 DWORD pipe_mode, len, exit_code = 0;
3231 STARTUP_DATA sud = { 0, 0, 0 };
3232 STARTUPINFOW startup_info;
3233 PROCESS_INFORMATION proc_info;
3234 LPVOID user_env = NULL;
3235 WCHAR ovpn_pipe_name[256]; /* The entire pipe name string can be up to 256 characters long
3236 according to MSDN. */
3237 LPCWSTR exe_path;
3238 WCHAR *cmdline = NULL;
3239 size_t cmdline_size;
3240 undo_lists_t undo_lists;
3241 WCHAR errmsg[512] = L"";
3242
3243 SECURITY_ATTRIBUTES inheritable = { .nLength = sizeof(inheritable),
3244 .lpSecurityDescriptor = NULL,
3245 .bInheritHandle = TRUE };
3246
3247 PACL ovpn_dacl;
3248 EXPLICIT_ACCESS ea[2];
3249 SECURITY_DESCRIPTOR ovpn_sd;
3250 SECURITY_ATTRIBUTES ovpn_sa = { .nLength = sizeof(ovpn_sa),
3251 .lpSecurityDescriptor = &ovpn_sd,
3252 .bInheritHandle = FALSE };
3253
3254 ZeroMemory(&ea, sizeof(ea));
3255 ZeroMemory(&startup_info, sizeof(startup_info));
3256 ZeroMemory(&undo_lists, sizeof(undo_lists));
3257 ZeroMemory(&proc_info, sizeof(proc_info));
3258
3259 if (!GetStartupData(pipe, &sud))
3260 {
3261 goto out;
3262 }
3263
3264 if (!InitializeSecurityDescriptor(&ovpn_sd, SECURITY_DESCRIPTOR_REVISION))
3265 {
3266 ReturnLastError(pipe, L"InitializeSecurityDescriptor");
3267 goto out;
3268 }
3269
3270 /* Get SID of user the service is running under */
3271 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &svc_token))
3272 {
3273 ReturnLastError(pipe, L"OpenProcessToken");
3274 goto out;
3275 }
3276 len = 0;
3277 while (!GetTokenInformation(svc_token, TokenUser, svc_user, len, &len))
3278 {
3279 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
3280 {
3281 ReturnLastError(pipe, L"GetTokenInformation (service token)");
3282 goto out;
3283 }
3284 free(svc_user);
3285 svc_user = malloc(len);
3286 if (svc_user == NULL)
3287 {
3288 ReturnLastError(pipe, L"malloc (service token user)");
3289 goto out;
3290 }
3291 }
3292 if (!IsValidSid(svc_user->User.Sid))
3293 {
3294 ReturnLastError(pipe, L"IsValidSid (service token user)");
3295 goto out;
3296 }
3297
3298 if (!ImpersonateNamedPipeClient(pipe))
3299 {
3300 ReturnLastError(pipe, L"ImpersonateNamedPipeClient");
3301 goto out;
3302 }
3303 if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &imp_token))
3304 {
3305 ReturnLastError(pipe, L"OpenThreadToken");
3306 goto out;
3307 }
3308 len = 0;
3309 while (!GetTokenInformation(imp_token, TokenUser, ovpn_user, len, &len))
3310 {
3311 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
3312 {
3313 ReturnLastError(pipe, L"GetTokenInformation (impersonation token)");
3314 goto out;
3315 }
3316 free(ovpn_user);
3317 ovpn_user = malloc(len);
3318 if (ovpn_user == NULL)
3319 {
3320 ReturnLastError(pipe, L"malloc (impersonation token user)");
3321 goto out;
3322 }
3323 }
3324 if (!IsValidSid(ovpn_user->User.Sid))
3325 {
3326 ReturnLastError(pipe, L"IsValidSid (impersonation token user)");
3327 goto out;
3328 }
3329
3330 /*
3331 * Only authorized users are allowed to use any command line options or
3332 * have the config file in locations other than the global config directory.
3333 *
3334 * Check options are white-listed and config is in the global directory
3335 * OR user is authorized to run any config.
3336 */
3337 if (!ValidateOptions(pipe, sud.directory, sud.options, errmsg, _countof(errmsg))
3338 && !IsAuthorizedUser(ovpn_user->User.Sid, imp_token, settings.ovpn_admin_group,
3340 {
3341 ReturnError(pipe, ERROR_STARTUP_DATA, errmsg, 1, &exit_event);
3342 goto out;
3343 }
3344
3345 /* OpenVPN process DACL entry for access by service and user */
3346 ea[0].grfAccessPermissions = SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL;
3347 ea[0].grfAccessMode = SET_ACCESS;
3348 ea[0].grfInheritance = NO_INHERITANCE;
3349 ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
3350 ea[0].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
3351 ea[0].Trustee.ptstrName = (LPWSTR)svc_user->User.Sid;
3352 ea[1].grfAccessPermissions = READ_CONTROL | SYNCHRONIZE | PROCESS_VM_READ | SYNCHRONIZE
3353 | PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION;
3354 ea[1].grfAccessMode = SET_ACCESS;
3355 ea[1].grfInheritance = NO_INHERITANCE;
3356 ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
3357 ea[1].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
3358 ea[1].Trustee.ptstrName = (LPWSTR)ovpn_user->User.Sid;
3359
3360 /* Set owner and DACL of OpenVPN security descriptor */
3361 if (!SetSecurityDescriptorOwner(&ovpn_sd, svc_user->User.Sid, FALSE))
3362 {
3363 ReturnLastError(pipe, L"SetSecurityDescriptorOwner");
3364 goto out;
3365 }
3366 if (SetEntriesInAcl(2, ea, NULL, &ovpn_dacl) != ERROR_SUCCESS)
3367 {
3368 ReturnLastError(pipe, L"SetEntriesInAcl");
3369 goto out;
3370 }
3371 if (!SetSecurityDescriptorDacl(&ovpn_sd, TRUE, ovpn_dacl, FALSE))
3372 {
3373 ReturnLastError(pipe, L"SetSecurityDescriptorDacl");
3374 goto out;
3375 }
3376
3377 /* Create primary token from impersonation token */
3378 if (!DuplicateTokenEx(imp_token, TOKEN_ALL_ACCESS, NULL, 0, TokenPrimary, &pri_token))
3379 {
3380 ReturnLastError(pipe, L"DuplicateTokenEx");
3381 goto out;
3382 }
3383
3384 /* use /dev/null for stdout of openvpn (client should use --log for output) */
3385 stdout_write = CreateFile(_L("NUL"), GENERIC_WRITE, FILE_SHARE_WRITE, &inheritable,
3386 OPEN_EXISTING, 0, NULL);
3387 if (stdout_write == INVALID_HANDLE_VALUE)
3388 {
3389 ReturnLastError(pipe, L"CreateFile for stdout");
3390 goto out;
3391 }
3392
3393 if (!CreatePipe(&stdin_read, &stdin_write, &inheritable, 0)
3394 || !SetHandleInformation(stdin_write, HANDLE_FLAG_INHERIT, 0))
3395 {
3396 ReturnLastError(pipe, L"CreatePipe");
3397 goto out;
3398 }
3399
3400 UUID pipe_uuid;
3401 RPC_STATUS rpc_stat = UuidCreate(&pipe_uuid);
3402 if (rpc_stat != RPC_S_OK)
3403 {
3404 ReturnError(pipe, rpc_stat, L"UuidCreate", 1, &exit_event);
3405 goto out;
3406 }
3407
3408 RPC_WSTR pipe_uuid_str = NULL;
3409 rpc_stat = UuidToStringW(&pipe_uuid, &pipe_uuid_str);
3410 if (rpc_stat != RPC_S_OK)
3411 {
3412 ReturnError(pipe, rpc_stat, L"UuidToString", 1, &exit_event);
3413 goto out;
3414 }
3415 swprintf(ovpn_pipe_name, _countof(ovpn_pipe_name),
3416 L"\\\\.\\pipe\\" _L(PACKAGE) L"%ls\\service_%lu_%ls", service_instance,
3417 GetCurrentThreadId(), pipe_uuid_str);
3418 RpcStringFree(&pipe_uuid_str);
3419
3420 /* make a security descriptor for the named pipe with access
3421 * restricted to the user and SYSTEM
3422 */
3423
3424 SECURITY_ATTRIBUTES sa;
3425 PSECURITY_DESCRIPTOR pSD = NULL;
3426 LPCWSTR szSDDL = L"D:(A;;GA;;;SY)(A;;GA;;;OW)";
3427 if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(
3428 szSDDL, SDDL_REVISION_1, &pSD, NULL))
3429 {
3430 ReturnLastError(pipe, L"ConvertSDDL");
3431 goto out;
3432 }
3433 sa.nLength = sizeof(sa);
3434 sa.lpSecurityDescriptor = pSD;
3435 sa.bInheritHandle = FALSE;
3436
3437 ovpn_pipe = CreateNamedPipe(
3438 ovpn_pipe_name, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED,
3439 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS, 1, 128, 128, 0, &sa);
3440 if (ovpn_pipe == INVALID_HANDLE_VALUE)
3441 {
3442 ReturnLastError(pipe, L"CreateNamedPipe");
3443 goto out;
3444 }
3445
3446 svc_pipe = CreateFile(ovpn_pipe_name, GENERIC_READ | GENERIC_WRITE, 0, &inheritable,
3447 OPEN_EXISTING, 0, NULL);
3448 if (svc_pipe == INVALID_HANDLE_VALUE)
3449 {
3450 ReturnLastError(pipe, L"CreateFile");
3451 goto out;
3452 }
3453
3454 pipe_mode = PIPE_READMODE_MESSAGE;
3455 if (!SetNamedPipeHandleState(svc_pipe, &pipe_mode, NULL, NULL))
3456 {
3457 ReturnLastError(pipe, L"SetNamedPipeHandleState");
3458 goto out;
3459 }
3460
3461 cmdline_size = wcslen(sud.options) + 128;
3462 cmdline = malloc(cmdline_size * sizeof(*cmdline));
3463 if (cmdline == NULL)
3464 {
3465 ReturnLastError(pipe, L"malloc");
3466 goto out;
3467 }
3468 /* there seem to be no common printf specifier that works on all
3469 * mingw/msvc platforms without trickery, so convert to void* and use
3470 * PRIuPTR to print that as best compromise */
3471 swprintf(cmdline, cmdline_size, L"openvpn %ls --msg-channel %" PRIuPTR, sud.options,
3472 (uintptr_t)svc_pipe);
3473
3474 if (!CreateEnvironmentBlock(&user_env, imp_token, FALSE))
3475 {
3476 ReturnLastError(pipe, L"CreateEnvironmentBlock");
3477 goto out;
3478 }
3479
3480 startup_info.cb = sizeof(startup_info);
3481 startup_info.dwFlags = STARTF_USESTDHANDLES;
3482 startup_info.hStdInput = stdin_read;
3483 startup_info.hStdOutput = stdout_write;
3484 startup_info.hStdError = stdout_write;
3485
3486 exe_path = settings.exe_path;
3487
3488 /* TODO: make sure HKCU is correct or call LoadUserProfile() */
3489 if (!CreateProcessAsUserW(pri_token, exe_path, cmdline, &ovpn_sa, NULL, TRUE,
3490 settings.priority | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
3491 user_env, sud.directory, &startup_info, &proc_info))
3492 {
3493 ReturnLastError(pipe, L"CreateProcessAsUser");
3494 goto out;
3495 }
3496
3497 if (!RevertToSelf())
3498 {
3499 TerminateProcess(proc_info.hProcess, 1);
3500 ReturnLastError(pipe, L"RevertToSelf");
3501 goto out;
3502 }
3503
3504 ReturnProcessId(pipe, proc_info.dwProcessId, 1, &exit_event);
3505
3506 CloseHandleEx(&stdout_write);
3507 CloseHandleEx(&stdin_read);
3508 CloseHandleEx(&svc_pipe);
3509
3510 DWORD input_size = WideCharToMultiByte(CP_UTF8, 0, sud.std_input, -1, NULL, 0, NULL, NULL);
3511 LPSTR input = NULL;
3512 if (input_size && (input = malloc(input_size)))
3513 {
3514 DWORD written;
3515 WideCharToMultiByte(CP_UTF8, 0, sud.std_input, -1, input, input_size, NULL, NULL);
3516 WriteFile(stdin_write, input, (DWORD)strlen(input), &written, NULL);
3517 free(input);
3518 }
3519
3520 while (TRUE)
3521 {
3522 DWORD bytes = PeekNamedPipeAsync(ovpn_pipe, 1, &exit_event);
3523 if (bytes == 0)
3524 {
3525 break;
3526 }
3527
3528 if (bytes > sizeof(pipe_message_t))
3529 {
3530 /* process at the other side of the pipe is misbehaving, shut it down */
3533 L"OpenVPN process sent too large payload length to the pipe (%lu bytes), it will be terminated",
3534 bytes);
3535 break;
3536 }
3537
3538 HandleMessage(ovpn_pipe, &proc_info, bytes, 1, &exit_event, &undo_lists);
3539 }
3540
3541 WaitForSingleObject(proc_info.hProcess, IO_TIMEOUT);
3542 GetExitCodeProcess(proc_info.hProcess, &exit_code);
3543 if (exit_code == STILL_ACTIVE)
3544 {
3545 TerminateProcess(proc_info.hProcess, 1);
3546 }
3547 else if (exit_code != 0)
3548 {
3549 WCHAR buf[256];
3550 swprintf(buf, _countof(buf), L"OpenVPN exited with error: exit code = %lu", exit_code);
3552 }
3553 Undo(&undo_lists);
3554
3555out:
3556 FlushFileBuffers(pipe);
3557 DisconnectNamedPipe(pipe);
3558
3559 free(ovpn_user);
3560 free(svc_user);
3561 free(cmdline);
3562 DestroyEnvironmentBlock(user_env);
3563 FreeStartupData(&sud);
3564 CloseHandleEx(&proc_info.hProcess);
3565 CloseHandleEx(&proc_info.hThread);
3566 CloseHandleEx(&stdin_read);
3567 CloseHandleEx(&stdin_write);
3568 CloseHandleEx(&stdout_write);
3569 CloseHandleEx(&svc_token);
3570 CloseHandleEx(&imp_token);
3571 CloseHandleEx(&pri_token);
3572 CloseHandleEx(&ovpn_pipe);
3573 CloseHandleEx(&svc_pipe);
3574 CloseHandleEx(&pipe);
3575
3576 return 0;
3577}
3578
3579
3580static DWORD WINAPI
3581ServiceCtrlInteractive(DWORD ctrl_code, DWORD event, LPVOID data, LPVOID ctx)
3582{
3583 SERVICE_STATUS *status = ctx;
3584 switch (ctrl_code)
3585 {
3586 case SERVICE_CONTROL_STOP:
3587 status->dwCurrentState = SERVICE_STOP_PENDING;
3589 if (exit_event)
3590 {
3591 SetEvent(exit_event);
3592 }
3593 return NO_ERROR;
3594
3595 case SERVICE_CONTROL_INTERROGATE:
3596 return NO_ERROR;
3597
3598 default:
3599 return ERROR_CALL_NOT_IMPLEMENTED;
3600 }
3601}
3602
3603
3604static HANDLE
3606{
3607 /*
3608 * allow all access for local system
3609 * deny FILE_CREATE_PIPE_INSTANCE for everyone
3610 * allow read/write for authenticated users
3611 * deny all access to anonymous
3612 */
3613 const WCHAR *sddlString =
3614 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)";
3615
3616 PSECURITY_DESCRIPTOR sd = NULL;
3617 if (!ConvertStringSecurityDescriptorToSecurityDescriptor(sddlString, SDDL_REVISION_1, &sd,
3618 NULL))
3619 {
3620 MsgToEventLog(M_SYSERR, L"ConvertStringSecurityDescriptorToSecurityDescriptor failed.");
3621 return INVALID_HANDLE_VALUE;
3622 }
3623
3624 /* Set up SECURITY_ATTRIBUTES */
3625 SECURITY_ATTRIBUTES sa = { 0 };
3626 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
3627 sa.lpSecurityDescriptor = sd;
3628 sa.bInheritHandle = FALSE;
3629
3630 DWORD flags = PIPE_ACCESS_DUPLEX | WRITE_DAC | FILE_FLAG_OVERLAPPED;
3631
3632 static BOOL first = TRUE;
3633 if (first)
3634 {
3635 flags |= FILE_FLAG_FIRST_PIPE_INSTANCE;
3636 first = FALSE;
3637 }
3638
3639 WCHAR pipe_name[256]; /* The entire pipe name string can be up to 256 characters long according
3640 to MSDN. */
3641 swprintf(pipe_name, _countof(pipe_name), L"\\\\.\\pipe\\" _L(PACKAGE) L"%ls\\service",
3643 HANDLE pipe = CreateNamedPipe(
3644 pipe_name, flags, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
3645 PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0, &sa);
3646
3647 LocalFree(sd);
3648
3649 if (pipe == INVALID_HANDLE_VALUE)
3650 {
3651 MsgToEventLog(M_SYSERR, L"Could not create named pipe");
3652 return INVALID_HANDLE_VALUE;
3653 }
3654
3655 return pipe;
3656}
3657
3658
3659static DWORD
3660UpdateWaitHandles(LPHANDLE *handles_ptr, LPDWORD count, HANDLE io_event, HANDLE exit_event,
3661 list_item_t *threads)
3662{
3663 static DWORD size = 10;
3664 static LPHANDLE handles = NULL;
3665 DWORD pos = 0;
3666
3667 if (handles == NULL)
3668 {
3669 handles = malloc(size * sizeof(HANDLE));
3670 *handles_ptr = handles;
3671 if (handles == NULL)
3672 {
3673 return ERROR_OUTOFMEMORY;
3674 }
3675 }
3676
3677 handles[pos++] = io_event;
3678
3679 if (!threads)
3680 {
3681 handles[pos++] = exit_event;
3682 }
3683
3684 while (threads)
3685 {
3686 if (pos == size)
3687 {
3688 LPHANDLE tmp;
3689 size += 10;
3690 tmp = realloc(handles, size * sizeof(HANDLE));
3691 if (tmp == NULL)
3692 {
3693 size -= 10;
3694 *count = pos;
3695 return ERROR_OUTOFMEMORY;
3696 }
3697 handles = tmp;
3698 *handles_ptr = handles;
3699 }
3700 handles[pos++] = threads->data;
3701 threads = threads->next;
3702 }
3703
3704 *count = pos;
3705 return NO_ERROR;
3706}
3707
3708
3709static VOID
3711{
3712 free(h);
3713}
3714
3715static BOOL
3716CmpHandle(LPVOID item, LPVOID hnd)
3717{
3718 return item == hnd;
3719}
3720
3721
3722VOID WINAPI
3723ServiceStartInteractiveOwn(DWORD dwArgc, LPWSTR *lpszArgv)
3724{
3725 status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
3726 ServiceStartInteractive(dwArgc, lpszArgv);
3727}
3728
3734static void
3736{
3737 BOOL changed = FALSE;
3738
3739 /* Clean up leftover NRPT rules */
3740 BOOL gpol_nrpt;
3741 changed = DeleteNrptRules(0, &gpol_nrpt);
3742
3743 /* Clean up leftover DNS search list fragments */
3744 HKEY key;
3745 BOOL gpol_list;
3746 GetDnsSearchListKey(NULL, &gpol_list, &key);
3747 if (key != INVALID_HANDLE_VALUE)
3748 {
3750 {
3751 changed = TRUE;
3752 }
3753 RegCloseKey(key);
3754 }
3755
3756 if (changed)
3757 {
3758 ApplyDnsSettings(gpol_nrpt || gpol_list);
3759 }
3760}
3761
3762VOID WINAPI
3763ServiceStartInteractive(DWORD dwArgc, LPWSTR *lpszArgv)
3764{
3765 HANDLE pipe, io_event = NULL;
3766 OVERLAPPED overlapped;
3767 DWORD error = NO_ERROR;
3768 list_item_t *threads = NULL;
3769 PHANDLE handles = NULL;
3770 DWORD handle_count;
3771
3772 service =
3773 RegisterServiceCtrlHandlerEx(interactive_service.name, ServiceCtrlInteractive, &status);
3774 if (!service)
3775 {
3776 return;
3777 }
3778
3779 status.dwCurrentState = SERVICE_START_PENDING;
3780 status.dwServiceSpecificExitCode = NO_ERROR;
3781 status.dwWin32ExitCode = NO_ERROR;
3782 status.dwWaitHint = 3000;
3784
3785 /* Clean up potentially left over registry values */
3787
3788 /* Read info from registry in key HKLM\SOFTWARE\OpenVPN */
3789 error = GetOpenvpnSettings(&settings);
3790 if (error != ERROR_SUCCESS)
3791 {
3792 goto out;
3793 }
3794
3795 io_event = InitOverlapped(&overlapped);
3796 exit_event = CreateEvent(NULL, TRUE, FALSE, NULL);
3797 if (!exit_event || !io_event)
3798 {
3799 error = MsgToEventLog(M_SYSERR, L"Could not create event");
3800 goto out;
3801 }
3802
3803 rdns_semaphore = CreateSemaphoreW(NULL, 1, 1, NULL);
3804 if (!rdns_semaphore)
3805 {
3806 error = MsgToEventLog(M_SYSERR, L"Could not create semaphore for register-dns");
3807 goto out;
3808 }
3809
3810 error = UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
3811 if (error != NO_ERROR)
3812 {
3813 goto out;
3814 }
3815
3816 pipe = CreateClientPipeInstance();
3817 if (pipe == INVALID_HANDLE_VALUE)
3818 {
3819 goto out;
3820 }
3821
3822 status.dwCurrentState = SERVICE_RUNNING;
3823 status.dwWaitHint = 0;
3825
3826 while (TRUE)
3827 {
3828 if (ConnectNamedPipe(pipe, &overlapped) == FALSE && GetLastError() != ERROR_PIPE_CONNECTED
3829 && GetLastError() != ERROR_IO_PENDING)
3830 {
3831 MsgToEventLog(M_SYSERR, L"Could not connect pipe");
3832 break;
3833 }
3834
3835 error = WaitForMultipleObjects(handle_count, handles, FALSE, INFINITE);
3836 if (error == WAIT_OBJECT_0)
3837 {
3838 /* Client connected, spawn a worker thread for it */
3839 HANDLE next_pipe = CreateClientPipeInstance();
3840 HANDLE thread = CreateThread(NULL, 0, RunOpenvpn, pipe, CREATE_SUSPENDED, NULL);
3841 if (thread)
3842 {
3843 error = AddListItem(&threads, thread);
3844 if (!error)
3845 {
3846 error =
3847 UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
3848 }
3849 if (error)
3850 {
3851 ReturnError(pipe, error, L"Insufficient resources to service new clients", 1,
3852 &exit_event);
3853 /* Update wait handles again after removing the last worker thread */
3854 RemoveListItem(&threads, CmpHandle, thread);
3855 UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
3856 TerminateThread(thread, 1);
3857 CloseHandleEx(&thread);
3858 CloseHandleEx(&pipe);
3859 }
3860 else
3861 {
3862 ResumeThread(thread);
3863 }
3864 }
3865 else
3866 {
3867 CloseHandleEx(&pipe);
3868 }
3869
3870 ResetOverlapped(&overlapped);
3871 pipe = next_pipe;
3872 }
3873 else
3874 {
3875 CancelIo(pipe);
3876 if (error == WAIT_FAILED)
3877 {
3878 MsgToEventLog(M_SYSERR, L"WaitForMultipleObjects failed");
3879 SetEvent(exit_event);
3880 /* Give some time for worker threads to exit and then terminate */
3881 Sleep(1000);
3882 break;
3883 }
3884 if (!threads)
3885 {
3886 /* exit event signaled */
3887 CloseHandleEx(&pipe);
3888 ResetEvent(exit_event);
3889 error = NO_ERROR;
3890 break;
3891 }
3892
3893 /* Worker thread ended */
3894 HANDLE thread = RemoveListItem(&threads, CmpHandle, handles[error]);
3895 UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
3896 CloseHandleEx(&thread);
3897 }
3898 }
3899
3900out:
3901 FreeWaitHandles(handles);
3902 CloseHandleEx(&io_event);
3905
3906 status.dwCurrentState = SERVICE_STOPPED;
3907 status.dwWin32ExitCode = error;
3909}
static int buf_len(const struct buffer *buf)
Definition buffer.h:253
wchar_t * utf8to16_size(const char *utf8, int size)
Convert a UTF-8 string to UTF-16.
Definition common.c:294
DWORD MsgToEventLog(DWORD flags, LPCWSTR format,...)
Definition common.c:253
LPCWSTR service_instance
Definition common.c:29
DWORD GetOpenvpnSettings(settings_t *s)
Definition common.c:76
#define M_INFO
Definition errlevel.h:54
static LSTATUS GetItfDnsServersV4(HKEY itf_key, PSTR addrs, PDWORD size)
Get DNS server IPv4 addresses of an interface.
static LSTATUS SetNameServerAddresses(PWSTR itf_id, const nrpt_address_t *addresses)
Set name servers from a NRPT address list.
static VOID ReturnLastError(HANDLE pipe, LPCWSTR func)
static BOOL GetInterfacesKey(short family, PHKEY key)
Return the interfaces registry key for the specified address family.
static DWORD ReadPipeAsync(HANDLE pipe, LPVOID buffer, DWORD size, DWORD count, LPHANDLE events)
static void UndoNrptRules(DWORD ovpn_pid)
Delete a process' NRPT rules and apply the reduced set of rules.
static BOOL ApplyGpolSettings(void)
Signal the DNS resolver (and others potentially) to reload the group policy (DNS) settings.
static VOID ReturnProcessId(HANDLE pipe, DWORD pid, DWORD count, LPHANDLE events)
static BOOL GetDnsSearchListKey(PCSTR itf_name, PBOOL gpol, PHKEY key)
Find the registry key for storing the DNS domains for the VPN interface.
static DWORD HandleWINSConfigMessage(const wins_cfg_message_t *msg, undo_lists_t *lists)
static BOOL CmpAddress(LPVOID item, LPVOID address)
static LSTATUS GetItfDnsDomains(HKEY itf, PCWSTR search_domains, PWSTR domains, PDWORD size)
Return interface specific domain suffix(es)
static DWORD 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 netsh_wins_cmd(const wchar_t *action, int if_index, const wchar_t *addr)
Run the command: netsh interface ip $action wins $if_index [static] $addr.
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:46
static DWORD WINAPI RunOpenvpn(LPVOID p)
static settings_t settings
Definition interactive.c:53
VOID WINAPI ServiceStartInteractive(DWORD dwArgc, LPWSTR *lpszArgv)
static DWORD DeleteRoute(PMIB_IPFORWARD_ROW2 fwd_row)
static SERVICE_STATUS status
Definition interactive.c:51
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.
#define ERROR_MESSAGE_TYPE
Definition interactive.c:48
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:55
undo_type_t
Definition interactive.c:83
@ wfp_block
Definition interactive.c:86
@ _undo_type_max
Definition interactive.c:92
@ undo_dns6
Definition interactive.c:88
@ undo_dns4
Definition interactive.c:87
@ undo_wins
Definition interactive.c:91
@ route
Definition interactive.c:85
@ undo_nrpt
Definition interactive.c:89
@ address
Definition interactive.c:84
@ undo_domains
Definition interactive.c:90
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 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:45
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 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:60
VOID WINAPI ServiceStartInteractiveOwn(DWORD dwArgc, LPWSTR *lpszArgv)
static DWORD AsyncPipeOp(async_op_t op, HANDLE pipe, LPVOID buffer, DWORD size, DWORD count, LPHANDLE events)
#define IO_TIMEOUT
Definition interactive.c:43
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:94
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:47
static HANDLE exit_event
Definition interactive.c:52
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:54
static DWORD InterfaceLuid(const char *iface_name, PNET_LUID luid)
static BOOL ValidateOptions(HANDLE pipe, const WCHAR *workdir, const WCHAR *options, WCHAR *errmsg, DWORD capacity)
static DWORD UpdateWaitHandles(LPHANDLE *handles_ptr, LPDWORD count, HANDLE io_event, HANDLE exit_event, list_item_t *threads)
static BOOL CmpRoute(LPVOID item, LPVOID route)
static DWORD HandleDNSConfigMessage(const dns_cfg_message_t *msg, undo_lists_t *lists)
static BOOL CmpAny(LPVOID item, LPVOID any)
async_op_t
@ peek
@ write
@ 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:50
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:76
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:77
struct _list_item * next
Definition interactive.c:76
WCHAR * directory
Definition interactive.c:67
WCHAR * options
Definition interactive.c:68
WCHAR * std_input
Definition interactive.c:69
message_header_t header
Definition argv.h:35
Wrapper structure for dynamically allocated memory.
Definition buffer.h:60
Definition dhcp.h:62
interface_t iface
char name[256]
Definition openvpn-msg.h:70
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
const char * msg2
const char * msg1
struct in6_addr ipv6
Definition openvpn-msg.h:64
struct in_addr ipv4
Definition openvpn-msg.h:63
dns_cfg_message_t dns
address_message_t address
flush_neighbors_message_t flush_neighbors
wfp_block_message_t wfp_block
message_header_t header
wins_cfg_message_t wins
enable_dhcp_message_t dhcp
route_message_t route
nrpt_dns_cfg_message_t nrpt_dns
set_mtu_message_t mtu
create_adapter_message_t create_adapter
BOOL IsAuthorizedUser(PSID sid, const HANDLE token, const WCHAR *ovpn_admin_group, const WCHAR *ovpn_service_user)
Definition validate.c:138
BOOL CheckOption(const WCHAR *workdir, int argc, WCHAR *argv[], const settings_t *s)
Definition validate.c:316
static BOOL IsOption(const WCHAR *o)
Definition validate.h:47
int get_interface_metric(const NET_IFINDEX index, const ADDRESS_FAMILY family, int *is_auto)
Return interface metric value for the specified interface index.
Definition wfp_block.c:369
DWORD set_interface_metric(const NET_IFINDEX index, const ADDRESS_FAMILY family, const ULONG metric)
Sets interface metric value for specified interface index.
Definition wfp_block.c:408
DWORD delete_wfp_block_filters(HANDLE engine_handle)
Definition wfp_block.c:344
DWORD add_wfp_block_filters(HANDLE *engine_handle, int index, const WCHAR *exe_path, wfp_block_msg_handler_t msg_handler, BOOL dns_only)
Definition wfp_block.c:153
#define WFP_BLOCK_IFACE_METRIC
Definition wfp_block.h:33
char * get_win_sys_path(void)
Definition win32.c:1109