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 (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM
738 | FORMAT_MESSAGE_ARGUMENT_ARRAY,
739 NULL, err, 0, buf, sizeof(buf), NULL))
740 {
741 err_str = buf;
742 }
743
744 MsgToEventLog(M_ERR, L"%hs (status = %lu): %ls", msg, err, err_str);
745}
746
747/* Use an always-true match_fn to get the head of the list */
748static BOOL
749CmpAny(LPVOID item, LPVOID any)
750{
751 return TRUE;
752}
753
754static DWORD
756{
757 DWORD err = 0;
758 wfp_block_data_t *block_data = RemoveListItem(&(*lists)[wfp_block], CmpAny, NULL);
759
760 if (block_data)
761 {
762 err = delete_wfp_block_filters(block_data->engine);
763 if (block_data->metric_v4 >= 0)
764 {
765 set_interface_metric(msg->iface.index, AF_INET, block_data->metric_v4);
766 }
767 if (block_data->metric_v6 >= 0)
768 {
769 set_interface_metric(msg->iface.index, AF_INET6, block_data->metric_v6);
770 }
771 free(block_data);
772 }
773 else
774 {
775 MsgToEventLog(M_ERR, L"No previous block filters to delete");
776 }
777
778 return err;
779}
780
781static DWORD
783{
784 DWORD err = 0;
785 wfp_block_data_t *block_data = NULL;
786 HANDLE engine = NULL;
787 LPCWSTR exe_path;
788 BOOL dns_only;
789
790 exe_path = settings.exe_path;
791 dns_only = (msg->flags == wfp_block_dns);
792
793 err = add_wfp_block_filters(&engine, msg->iface.index, exe_path, BlockDNSErrHandler, dns_only);
794 if (!err)
795 {
796 block_data = malloc(sizeof(wfp_block_data_t));
797 if (!block_data)
798 {
799 err = ERROR_OUTOFMEMORY;
800 goto out;
801 }
802 block_data->engine = engine;
803 block_data->index = msg->iface.index;
804 int is_auto = 0;
805 block_data->metric_v4 = get_interface_metric(msg->iface.index, AF_INET, &is_auto);
806 if (is_auto)
807 {
808 block_data->metric_v4 = 0;
809 }
810 block_data->metric_v6 = get_interface_metric(msg->iface.index, AF_INET6, &is_auto);
811 if (is_auto)
812 {
813 block_data->metric_v6 = 0;
814 }
815
816 err = AddListItem(&(*lists)[wfp_block], block_data);
817 if (!err)
818 {
819 err = set_interface_metric(msg->iface.index, AF_INET, WFP_BLOCK_IFACE_METRIC);
820 if (!err)
821 {
822 /* for IPv6, we intentionally ignore errors, because
823 * otherwise block-dns activation will fail if a user or
824 * admin has disabled IPv6 on the tun/tap/dco interface
825 * (if OpenVPN wants IPv6 ifconfig, we'll fail there)
826 */
827 set_interface_metric(msg->iface.index, AF_INET6, WFP_BLOCK_IFACE_METRIC);
828 }
829 if (err)
830 {
831 /* delete the filters, remove undo item and free interface data */
832 DeleteWfpBlock(msg, lists);
833 engine = NULL;
834 }
835 }
836 }
837
838out:
839 if (err && engine)
840 {
842 free(block_data);
843 }
844
845 return err;
846}
847
848static DWORD
850{
851 if (msg->header.type == msg_add_wfp_block)
852 {
853 return AddWfpBlock(msg, lists);
854 }
855 else
856 {
857 return DeleteWfpBlock(msg, lists);
858 }
859}
860
861/*
862 * Execute a command and return its exit code. If timeout > 0, terminate
863 * the process if still running after timeout milliseconds. In that case
864 * the return value is the windows error code WAIT_TIMEOUT = 0x102
865 */
866static DWORD
867ExecCommand(const WCHAR *argv0, const WCHAR *cmdline, DWORD timeout)
868{
869 DWORD exit_code;
870 STARTUPINFOW si;
871 PROCESS_INFORMATION pi;
872 DWORD proc_flags = CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT;
873 WCHAR *cmdline_dup = NULL;
874
875 ZeroMemory(&si, sizeof(si));
876 ZeroMemory(&pi, sizeof(pi));
877
878 si.cb = sizeof(si);
879
880 /* CreateProcess needs a modifiable cmdline: make a copy */
881 cmdline_dup = _wcsdup(cmdline);
882 if (cmdline_dup
883 && CreateProcessW(argv0, cmdline_dup, NULL, NULL, FALSE, proc_flags, NULL, NULL, &si, &pi))
884 {
885 WaitForSingleObject(pi.hProcess, timeout ? timeout : INFINITE);
886 if (!GetExitCodeProcess(pi.hProcess, &exit_code))
887 {
888 MsgToEventLog(M_SYSERR, L"ExecCommand: Error getting exit_code:");
889 exit_code = GetLastError();
890 }
891 else if (exit_code == STILL_ACTIVE)
892 {
893 exit_code = WAIT_TIMEOUT; /* Windows error code 0x102 */
894
895 /* kill without impunity */
896 TerminateProcess(pi.hProcess, exit_code);
897 MsgToEventLog(M_ERR, L"ExecCommand: \"%ls %ls\" killed after timeout", argv0, cmdline);
898 }
899 else if (exit_code)
900 {
901 MsgToEventLog(M_ERR, L"ExecCommand: \"%ls %ls\" exited with status = %lu", argv0,
902 cmdline, exit_code);
903 }
904 else
905 {
906 MsgToEventLog(M_INFO, L"ExecCommand: \"%ls %ls\" completed", argv0, cmdline);
907 }
908
909 CloseHandle(pi.hProcess);
910 CloseHandle(pi.hThread);
911 }
912 else
913 {
914 exit_code = GetLastError();
915 MsgToEventLog(M_SYSERR, L"ExecCommand: could not run \"%ls %ls\" :", argv0, cmdline);
916 }
917
918 free(cmdline_dup);
919 return exit_code;
920}
921
922/*
923 * Entry point for register-dns thread.
924 */
925static DWORD WINAPI
926RegisterDNS(LPVOID unused)
927{
928 DWORD err;
929 size_t i;
930 DWORD timeout = RDNS_TIMEOUT * 1000; /* in milliseconds */
931
932 /* path of ipconfig command */
933 WCHAR ipcfg[MAX_PATH];
934
935 struct
936 {
937 WCHAR *argv0;
938 WCHAR *cmdline;
939 DWORD timeout;
940 } cmds[] = {
941 { ipcfg, L"ipconfig /flushdns", timeout },
942 { ipcfg, L"ipconfig /registerdns", timeout },
943 };
944
945 HANDLE wait_handles[2] = { rdns_semaphore, exit_event };
946
947 swprintf(ipcfg, MAX_PATH, L"%ls\\%ls", get_win_sys_path(), L"ipconfig.exe");
948
949 if (WaitForMultipleObjects(2, wait_handles, FALSE, timeout) == WAIT_OBJECT_0)
950 {
951 /* Semaphore locked */
952 for (i = 0; i < _countof(cmds); ++i)
953 {
954 ExecCommand(cmds[i].argv0, cmds[i].cmdline, cmds[i].timeout);
955 }
956 err = 0;
957 if (!ReleaseSemaphore(rdns_semaphore, 1, NULL))
958 {
959 err =
960 MsgToEventLog(M_SYSERR, L"RegisterDNS: Failed to release regsiter-dns semaphore:");
961 }
962 }
963 else
964 {
965 MsgToEventLog(M_ERR, L"RegisterDNS: Failed to lock register-dns semaphore");
966 err = ERROR_SEM_TIMEOUT; /* Windows error code 0x79 */
967 }
968 return err;
969}
970
971static DWORD
973{
974 DWORD err;
975 HANDLE thread = NULL;
976
977 /* Delegate this job to a sub-thread */
978 thread = CreateThread(NULL, 0, RegisterDNS, NULL, 0, NULL);
979
980 /*
981 * We don't add these thread handles to the undo list -- the thread and
982 * processes it spawns are all supposed to terminate or timeout by themselves.
983 */
984 if (thread)
985 {
986 err = 0;
987 CloseHandle(thread);
988 }
989 else
990 {
991 err = GetLastError();
992 }
993
994 return err;
995}
996
1006static DWORD
1007netsh_wins_cmd(const wchar_t *action, int if_index, const wchar_t *addr)
1008{
1009 DWORD err = 0;
1010 int timeout = 30000; /* in msec */
1011 wchar_t argv0[MAX_PATH];
1012 wchar_t *cmdline = NULL;
1013 const wchar_t *addr_static = (wcscmp(action, L"set") == 0) ? L"static" : L"";
1014
1015 if (!addr)
1016 {
1017 if (wcscmp(action, L"delete") == 0)
1018 {
1019 addr = L"all";
1020 }
1021 else /* nothing to do -- return success*/
1022 {
1023 goto out;
1024 }
1025 }
1026
1027 /* Path of netsh */
1028 swprintf(argv0, _countof(argv0), L"%ls\\%ls", get_win_sys_path(), L"netsh.exe");
1029
1030 /* cmd template:
1031 * netsh interface ip $action wins $if_name $static $addr
1032 */
1033 const wchar_t *fmt = L"netsh interface ip %ls wins %d %ls %ls";
1034
1035 /* max cmdline length in wchars -- include room for worst case and some */
1036 size_t ncmdline = wcslen(fmt) + 11 /*if_index*/ + wcslen(action) + wcslen(addr)
1037 + wcslen(addr_static) + 32 + 1;
1038 cmdline = malloc(ncmdline * sizeof(wchar_t));
1039 if (!cmdline)
1040 {
1041 err = ERROR_OUTOFMEMORY;
1042 goto out;
1043 }
1044
1045 swprintf(cmdline, ncmdline, fmt, action, if_index, addr_static, addr);
1046
1047 err = ExecCommand(argv0, cmdline, timeout);
1048
1049out:
1050 free(cmdline);
1051 return err;
1052}
1053
1060static BOOL
1062{
1063 typedef NTSTATUS(__stdcall * publish_fn_t)(DWORD StateNameLo, DWORD StateNameHi, DWORD TypeId,
1064 DWORD Buffer, DWORD Length, DWORD ExplicitScope);
1065 publish_fn_t RtlPublishWnfStateData;
1066 const DWORD WNF_GPOL_SYSTEM_CHANGES_HI = 0x0D891E2A;
1067 const DWORD WNF_GPOL_SYSTEM_CHANGES_LO = 0xA3BC0875;
1068
1069 HMODULE ntdll = LoadLibraryA("ntdll.dll");
1070 if (ntdll == NULL)
1071 {
1072 return FALSE;
1073 }
1074
1075 RtlPublishWnfStateData = (publish_fn_t)GetProcAddress(ntdll, "RtlPublishWnfStateData");
1076 if (RtlPublishWnfStateData == NULL)
1077 {
1078 return FALSE;
1079 }
1080
1081 if (RtlPublishWnfStateData(WNF_GPOL_SYSTEM_CHANGES_LO, WNF_GPOL_SYSTEM_CHANGES_HI, 0, 0, 0, 0)
1082 != ERROR_SUCCESS)
1083 {
1084 return FALSE;
1085 }
1086
1087 return TRUE;
1088}
1089
1096static BOOL
1098{
1099 typedef NTSTATUS (*publish_fn_t)(INT64 StateName, INT64 TypeId, INT64 Buffer,
1100 unsigned int Length, INT64 ExplicitScope);
1101 publish_fn_t RtlPublishWnfStateData;
1102 const INT64 WNF_GPOL_SYSTEM_CHANGES = 0x0D891E2AA3BC0875;
1103
1104 HMODULE ntdll = LoadLibraryA("ntdll.dll");
1105 if (ntdll == NULL)
1106 {
1107 return FALSE;
1108 }
1109
1110 RtlPublishWnfStateData = (publish_fn_t)GetProcAddress(ntdll, "RtlPublishWnfStateData");
1111 if (RtlPublishWnfStateData == NULL)
1112 {
1113 return FALSE;
1114 }
1115
1116 if (RtlPublishWnfStateData(WNF_GPOL_SYSTEM_CHANGES, 0, 0, 0, 0) != ERROR_SUCCESS)
1117 {
1118 return FALSE;
1119 }
1120
1121 return TRUE;
1122}
1123
1129static BOOL
1131{
1132 SYSTEM_INFO si;
1133 GetSystemInfo(&si);
1134 const BOOL win_32bit = si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL;
1135 return win_32bit ? ApplyGpolSettings32() : ApplyGpolSettings64();
1136}
1137
1145static BOOL
1146ApplyDnsSettings(BOOL apply_gpol)
1147{
1148 BOOL res = FALSE;
1149 SC_HANDLE scm = NULL;
1150 SC_HANDLE dnssvc = NULL;
1151
1152 if (apply_gpol && ApplyGpolSettings() == FALSE)
1153 {
1154 MsgToEventLog(M_ERR, L"%S: sending GPOL notification failed", __func__);
1155 }
1156
1157 scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
1158 if (scm == NULL)
1159 {
1160 MsgToEventLog(M_ERR, L"%S: OpenSCManager call failed (%lu)", __func__, GetLastError());
1161 goto out;
1162 }
1163
1164 dnssvc = OpenServiceA(scm, "Dnscache", SERVICE_PAUSE_CONTINUE);
1165 if (dnssvc == NULL)
1166 {
1167 MsgToEventLog(M_ERR, L"%S: OpenService call failed (%lu)", __func__, GetLastError());
1168 goto out;
1169 }
1170
1171 SERVICE_STATUS status;
1172 if (ControlService(dnssvc, SERVICE_CONTROL_PARAMCHANGE, &status) == 0)
1173 {
1174 MsgToEventLog(M_ERR, L"%S: ControlService call failed (%lu)", __func__, GetLastError());
1175 goto out;
1176 }
1177
1178 res = TRUE;
1179
1180out:
1181 if (dnssvc)
1182 {
1183 CloseServiceHandle(dnssvc);
1184 }
1185 if (scm)
1186 {
1187 CloseServiceHandle(scm);
1188 }
1189 return res;
1190}
1191
1201static DWORD
1202InterfaceIdString(PCSTR itf_name, PWSTR str, size_t len)
1203{
1204 DWORD err;
1205 GUID guid;
1206 NET_LUID luid;
1207 PWSTR iid_str = NULL;
1208
1209 err = InterfaceLuid(itf_name, &luid);
1210 if (err)
1211 {
1212 MsgToEventLog(M_ERR, L"%S: failed to convert itf alias '%s'", __func__, itf_name);
1213 goto out;
1214 }
1215 err = ConvertInterfaceLuidToGuid(&luid, &guid);
1216 if (err)
1217 {
1218 MsgToEventLog(M_ERR, L"%S: Failed to convert itf '%s' LUID", __func__, itf_name);
1219 goto out;
1220 }
1221
1222 if (StringFromIID(&guid, &iid_str) != S_OK)
1223 {
1224 MsgToEventLog(M_ERR, L"%S: Failed to convert itf '%s' IID", __func__, itf_name);
1225 err = ERROR_OUTOFMEMORY;
1226 goto out;
1227 }
1228 if (wcslen(iid_str) + 1 > len)
1229 {
1230 err = ERROR_INVALID_PARAMETER;
1231 goto out;
1232 }
1233
1234 wcsncpy(str, iid_str, len);
1235
1236out:
1237 if (iid_str)
1238 {
1239 CoTaskMemFree(iid_str);
1240 }
1241 return err;
1242}
1243
1257static BOOL
1259{
1260 char data[64];
1261 DWORD size = sizeof(data);
1262 LSTATUS err = RegGetValueA(key, NULL, "SearchList", RRF_RT_REG_SZ, NULL, (PBYTE)data, &size);
1263 if (!err || err == ERROR_MORE_DATA)
1264 {
1265 data[sizeof(data) - 1] = '\0';
1266 for (int i = 0; i < strlen(data); ++i)
1267 {
1268 if (isalnum(data[i]) || data[i] == '-' || data[i] == '.')
1269 {
1270 return TRUE;
1271 }
1272 }
1273 }
1274 return FALSE;
1275}
1276
1294static BOOL
1295GetDnsSearchListKey(PCSTR itf_name, PBOOL gpol, PHKEY key)
1296{
1297 LSTATUS err;
1298
1299 *gpol = FALSE;
1300
1301 /* Try the group policy search list */
1302 err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient",
1303 0, KEY_ALL_ACCESS, key);
1304 if (!err)
1305 {
1306 if (HasValidSearchList(*key))
1307 {
1308 *gpol = TRUE;
1309 return TRUE;
1310 }
1311 RegCloseKey(*key);
1312 }
1313
1314 /* Try the system-wide search list */
1315 err =
1316 RegOpenKeyExA(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\TCPIP\\Parameters",
1317 0, KEY_ALL_ACCESS, key);
1318 if (!err)
1319 {
1320 if (HasValidSearchList(*key))
1321 {
1322 return TRUE;
1323 }
1324 RegCloseKey(*key);
1325 }
1326
1327 if (itf_name)
1328 {
1329 /* Always return the VPN interface key (if it exists) */
1330 WCHAR iid[64];
1331 DWORD iid_err = InterfaceIdString(itf_name, iid, _countof(iid));
1332 if (!iid_err)
1333 {
1334 HKEY itfs;
1335 err =
1336 RegOpenKeyExA(HKEY_LOCAL_MACHINE,
1337 "System\\CurrentControlSet\\Services\\TCPIP\\Parameters\\Interfaces",
1338 0, KEY_ALL_ACCESS, &itfs);
1339 if (!err)
1340 {
1341 err = RegOpenKeyExW(itfs, iid, 0, KEY_ALL_ACCESS, key);
1342 RegCloseKey(itfs);
1343 if (!err)
1344 {
1345 return FALSE; /* No need to preserve the VPN itf search list */
1346 }
1347 }
1348 }
1349 }
1350
1351 *key = INVALID_HANDLE_VALUE;
1352 return FALSE;
1353}
1354
1362static BOOL
1364{
1365 LSTATUS err;
1366
1367 err = RegGetValueA(key, NULL, "InitialSearchList", RRF_RT_REG_SZ, NULL, NULL, NULL);
1368 if (err)
1369 {
1370 if (err == ERROR_FILE_NOT_FOUND)
1371 {
1372 return FALSE;
1373 }
1374 MsgToEventLog(M_ERR, L"%S: failed to get InitialSearchList (%lu)", __func__, err);
1375 }
1376
1377 return TRUE;
1378}
1379
1380#if defined(__GNUC__) || defined(__clang__)
1381#pragma GCC diagnostic push
1382#pragma GCC diagnostic ignored "-Wconversion"
1383#endif
1384
1395static BOOL
1397{
1398 if (!list || wcslen(list) == 0)
1399 {
1400 MsgToEventLog(M_ERR, L"%S: empty search list", __func__);
1401 return FALSE;
1402 }
1403
1405 {
1406 /* Initial list had already been stored */
1407 return TRUE;
1408 }
1409
1410 DWORD size = (wcslen(list) + 1) * sizeof(*list);
1411 LSTATUS err = RegSetValueExW(key, L"InitialSearchList", 0, REG_SZ, (PBYTE)list, size);
1412 if (err)
1413 {
1414 MsgToEventLog(M_ERR, L"%S: failed to set InitialSearchList value (%lu)", __func__, err);
1415 return FALSE;
1416 }
1417
1418 return TRUE;
1419}
1420
1430static BOOL
1431AddDnsSearchDomains(HKEY key, BOOL have_list, PCWSTR domains)
1432{
1433 LSTATUS err;
1434 WCHAR list[2048] = { 0 };
1435 DWORD size = sizeof(list);
1436
1437 if (have_list)
1438 {
1439 err = RegGetValueW(key, NULL, L"SearchList", RRF_RT_REG_SZ, NULL, list, &size);
1440 if (err)
1441 {
1442 MsgToEventLog(M_SYSERR, L"%S: could not get SearchList from registry (%lu)", __func__,
1443 err);
1444 return FALSE;
1445 }
1446
1447 if (!StoreInitialDnsSearchList(key, list))
1448 {
1449 return FALSE;
1450 }
1451
1452 size_t listlen = (size / sizeof(list[0])) - 1; /* returned size is in bytes */
1453 size_t domlen = wcslen(domains);
1454 if (listlen + domlen + 2 > _countof(list))
1455 {
1456 MsgToEventLog(M_SYSERR, L"%S: not enough space in list for search domains (len=%lu)",
1457 __func__, domlen);
1458 return FALSE;
1459 }
1460
1461 /* Append to end of the search list */
1462 PWSTR pos = list + listlen;
1463 *pos = ',';
1464 wcsncpy(pos + 1, domains, domlen + 1);
1465 }
1466 else
1467 {
1468 wcsncpy(list, domains, wcslen(domains) + 1);
1469 }
1470
1471 size = (wcslen(list) + 1) * sizeof(list[0]);
1472 err = RegSetValueExW(key, L"SearchList", 0, REG_SZ, (PBYTE)list, size);
1473 if (err)
1474 {
1475 MsgToEventLog(M_SYSERR, L"%S: could not set SearchList to registry (%lu)", __func__, err);
1476 return FALSE;
1477 }
1478
1479 return TRUE;
1480}
1481
1493static BOOL
1495{
1496 LSTATUS err;
1497 BOOL ret = FALSE;
1498 WCHAR list[2048];
1499 DWORD size = sizeof(list);
1500
1501 err = RegGetValueW(key, NULL, L"InitialSearchList", RRF_RT_REG_SZ, NULL, list, &size);
1502 if (err)
1503 {
1504 if (err != ERROR_FILE_NOT_FOUND)
1505 {
1506 MsgToEventLog(M_SYSERR, L"%S: could not get InitialSearchList from registry (%lu)",
1507 __func__, err);
1508 }
1509 goto out;
1510 }
1511
1512 size = (wcslen(list) + 1) * sizeof(list[0]);
1513 err = RegSetValueExW(key, L"SearchList", 0, REG_SZ, (PBYTE)list, size);
1514 if (err)
1515 {
1516 MsgToEventLog(M_SYSERR, L"%S: could not set SearchList in registry (%lu)", __func__, err);
1517 goto out;
1518 }
1519
1520 RegDeleteValueA(key, "InitialSearchList");
1521 ret = TRUE;
1522
1523out:
1524 return ret;
1525}
1526
1533static void
1534RemoveDnsSearchDomains(HKEY key, PCWSTR domains)
1535{
1536 LSTATUS err;
1537 WCHAR list[2048];
1538 DWORD size = sizeof(list);
1539
1540 err = RegGetValueW(key, NULL, L"SearchList", RRF_RT_REG_SZ, NULL, list, &size);
1541 if (err)
1542 {
1543 MsgToEventLog(M_SYSERR, L"%S: could not get SearchList from registry (%lu)", __func__, err);
1544 return;
1545 }
1546
1547 PWSTR dst = wcsstr(list, domains);
1548 if (!dst)
1549 {
1550 MsgToEventLog(M_ERR, L"%S: could not find domains in search list", __func__);
1551 return;
1552 }
1553
1554 /* Cut out domains from list */
1555 size_t domlen = wcslen(domains);
1556 PCWSTR src = dst + domlen;
1557 /* Also remove the leading comma, if there is one */
1558 dst = dst > list ? dst - 1 : dst;
1559 wmemmove(dst, src, domlen);
1560
1561 size_t list_len = wcslen(list);
1562 if (list_len)
1563 {
1564 /* Now check if the shortened list equals the initial search list */
1565 WCHAR initial[2048];
1566 size = sizeof(initial);
1567 err = RegGetValueW(key, NULL, L"InitialSearchList", RRF_RT_REG_SZ, NULL, initial, &size);
1568 if (err)
1569 {
1570 MsgToEventLog(M_SYSERR, L"%S: could not get InitialSearchList from registry (%lu)",
1571 __func__, err);
1572 return;
1573 }
1574
1575 /* If the search list is back to its initial state reset it */
1576 if (wcsncmp(list, initial, wcslen(list)) == 0)
1577 {
1579 return;
1580 }
1581 }
1582
1583 size = (list_len + 1) * sizeof(list[0]);
1584 err = RegSetValueExW(key, L"SearchList", 0, REG_SZ, (PBYTE)list, size);
1585 if (err)
1586 {
1587 MsgToEventLog(M_SYSERR, L"%S: could not set SearchList in registry (%lu)", __func__, err);
1588 }
1589}
1590
1596static void
1598{
1599 BOOL gpol;
1600 HKEY dns_searchlist_key;
1601 GetDnsSearchListKey(undo_data->itf_name, &gpol, &dns_searchlist_key);
1602 if (dns_searchlist_key != INVALID_HANDLE_VALUE)
1603 {
1604 RemoveDnsSearchDomains(dns_searchlist_key, undo_data->domains);
1605 RegCloseKey(dns_searchlist_key);
1606 ApplyDnsSettings(gpol);
1607
1608 free(undo_data->domains);
1609 undo_data->domains = NULL;
1610 }
1611}
1612
1634static DWORD
1635SetDnsSearchDomains(PCSTR itf_name, PCSTR domains, PBOOL gpol, undo_lists_t *lists)
1636{
1637 DWORD err = ERROR_OUTOFMEMORY;
1638
1639 HKEY list_key;
1640 BOOL have_list = GetDnsSearchListKey(itf_name, gpol, &list_key);
1641 if (list_key == INVALID_HANDLE_VALUE)
1642 {
1643 MsgToEventLog(M_SYSERR, L"%S: could not get search list registry key", __func__);
1644 return ERROR_FILE_NOT_FOUND;
1645 }
1646
1647 /* Remove previously installed search domains */
1648 dns_domains_undo_data_t *undo_data = RemoveListItem(&(*lists)[undo_domains], CmpAny, NULL);
1649 if (undo_data)
1650 {
1651 RemoveDnsSearchDomains(list_key, undo_data->domains);
1652 free(undo_data->domains);
1653 free(undo_data);
1654 undo_data = NULL;
1655 }
1656
1657 /* If there are search domains, add them */
1658 if (domains && *domains)
1659 {
1660 wchar_t *wide_domains = utf8to16(domains); /* utf8 to wide-char */
1661 if (!wide_domains)
1662 {
1663 goto out;
1664 }
1665
1666 undo_data = malloc(sizeof(*undo_data));
1667 if (!undo_data)
1668 {
1669 free(wide_domains);
1670 wide_domains = NULL;
1671 goto out;
1672 }
1673 strncpy(undo_data->itf_name, itf_name, sizeof(undo_data->itf_name));
1674 undo_data->domains = wide_domains;
1675
1676 if (AddDnsSearchDomains(list_key, have_list, wide_domains) == FALSE
1677 || AddListItem(&(*lists)[undo_domains], undo_data) != NO_ERROR)
1678 {
1679 RemoveDnsSearchDomains(list_key, wide_domains);
1680 free(wide_domains);
1681 free(undo_data);
1682 undo_data = NULL;
1683 goto out;
1684 }
1685 }
1686
1687 err = NO_ERROR;
1688
1689out:
1690 RegCloseKey(list_key);
1691 return err;
1692}
1693
1701static BOOL
1702GetInterfacesKey(short family, PHKEY key)
1703{
1704 PCSTR itfs_key = family == AF_INET6
1705 ? "SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters\\Interfaces"
1706 : "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces";
1707
1708 LSTATUS err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, itfs_key, 0, KEY_ALL_ACCESS, key);
1709 if (err)
1710 {
1711 *key = INVALID_HANDLE_VALUE;
1712 MsgToEventLog(M_SYSERR, L"%S: could not open interfaces registry key for family %d (%lu)",
1713 __func__, family, err);
1714 }
1715
1716 return err ? FALSE : TRUE;
1717}
1718
1728static DWORD
1729SetNameServersValue(PCWSTR itf_id, short family, PCSTR value)
1730{
1731 DWORD err;
1732
1733 HKEY itfs;
1734 if (!GetInterfacesKey(family, &itfs))
1735 {
1736 return ERROR_FILE_NOT_FOUND;
1737 }
1738
1739 HKEY itf = INVALID_HANDLE_VALUE;
1740 err = RegOpenKeyExW(itfs, itf_id, 0, KEY_ALL_ACCESS, &itf);
1741 if (err)
1742 {
1743 MsgToEventLog(M_SYSERR, L"%S: could not open interface key for %s family %d (%lu)",
1744 __func__, itf_id, family, err);
1745 goto out;
1746 }
1747
1748 err = RegSetValueExA(itf, "NameServer", 0, REG_SZ, (PBYTE)value, strlen(value) + 1);
1749 if (err)
1750 {
1751 MsgToEventLog(M_SYSERR, L"%S: could not set name servers '%S' for %s family %d (%lu)",
1752 __func__, value, itf_id, family, err);
1753 }
1754
1755out:
1756 if (itf != INVALID_HANDLE_VALUE)
1757 {
1758 RegCloseKey(itf);
1759 }
1760 if (itfs != INVALID_HANDLE_VALUE)
1761 {
1762 RegCloseKey(itfs);
1763 }
1764 return err;
1765}
1766
1776static DWORD
1777SetNameServers(PCWSTR itf_id, short family, PCSTR addrs)
1778{
1779 return SetNameServersValue(itf_id, family, addrs);
1780}
1781
1790static DWORD
1791ResetNameServers(PCWSTR itf_id, short family)
1792{
1793 return SetNameServersValue(itf_id, family, "");
1794}
1795
1796static DWORD
1798{
1799 DWORD err = 0;
1800 undo_type_t undo_type = (msg->family == AF_INET6) ? undo_dns6 : undo_dns4;
1801 int addr_len = msg->addr_len;
1802
1803 /* sanity check */
1804 const size_t max_addrs = _countof(msg->addr);
1805 if (addr_len > max_addrs)
1806 {
1807 addr_len = max_addrs;
1808 }
1809
1810 if (!msg->iface.name[0]) /* interface name is required */
1811 {
1812 return ERROR_MESSAGE_DATA;
1813 }
1814
1815 /* use a non-const reference with limited scope to enforce null-termination of strings from
1816 * client */
1817 {
1819 msgptr->iface.name[_countof(msg->iface.name) - 1] = '\0';
1820 msgptr->domains[_countof(msg->domains) - 1] = '\0';
1821 }
1822
1823 WCHAR iid[64];
1824 err = InterfaceIdString(msg->iface.name, iid, _countof(iid));
1825 if (err)
1826 {
1827 return err;
1828 }
1829
1830 /* We delete all current addresses before adding any
1831 * OR if the message type is del_dns_cfg
1832 */
1833 if (addr_len > 0 || msg->header.type == msg_del_dns_cfg)
1834 {
1835 err = ResetNameServers(iid, msg->family);
1836 if (err)
1837 {
1838 return err;
1839 }
1840 free(RemoveListItem(&(*lists)[undo_type], CmpAny, iid));
1841 }
1842
1843 if (msg->header.type == msg_del_dns_cfg)
1844 {
1845 BOOL gpol = FALSE;
1846 if (msg->domains[0])
1847 {
1848 /* setting an empty domain list removes any previous value */
1849 err = SetDnsSearchDomains(msg->iface.name, NULL, &gpol, lists);
1850 }
1851 ApplyDnsSettings(gpol);
1852 return err; /* job done */
1853 }
1854
1855 if (msg->addr_len > 0)
1856 {
1857 /* prepare the comma separated address list */
1858 /* cannot use max_addrs here as that is not considered compile
1859 * time constant by all compilers and constexpr is C23 */
1860 CHAR addrs[_countof(msg->addr) * 64]; /* 64 is enough for one IPv4/6 address */
1861 size_t offset = 0;
1862 for (int i = 0; i < addr_len; ++i)
1863 {
1864 if (i != 0)
1865 {
1866 addrs[offset++] = ',';
1867 }
1868 if (msg->family == AF_INET6)
1869 {
1870 RtlIpv6AddressToStringA(&msg->addr[i].ipv6, addrs + offset);
1871 }
1872 else
1873 {
1874 RtlIpv4AddressToStringA(&msg->addr[i].ipv4, addrs + offset);
1875 }
1876 offset = strlen(addrs);
1877 }
1878
1879 err = SetNameServers(iid, msg->family, addrs);
1880 if (err)
1881 {
1882 return err;
1883 }
1884
1885 wchar_t *tmp_iid = _wcsdup(iid);
1886 if (!tmp_iid || AddListItem(&(*lists)[undo_type], tmp_iid))
1887 {
1888 free(tmp_iid);
1889 ResetNameServers(iid, msg->family);
1890 return ERROR_OUTOFMEMORY;
1891 }
1892 }
1893
1894 BOOL gpol = FALSE;
1895 if (msg->domains[0])
1896 {
1897 err = SetDnsSearchDomains(msg->iface.name, msg->domains, &gpol, lists);
1898 }
1899 ApplyDnsSettings(gpol);
1900
1901 return err;
1902}
1903
1912static BOOL
1914{
1915 DWORD dhcp;
1916 DWORD size = sizeof(dhcp);
1917 LSTATUS err;
1918
1919 err = RegGetValueA(key, NULL, "EnableDHCP", RRF_RT_REG_DWORD, NULL, (PBYTE)&dhcp, &size);
1920 if (err != NO_ERROR)
1921 {
1922 MsgToEventLog(M_SYSERR, L"%S: Could not read DHCP status (%lu)", __func__, err);
1923 return FALSE;
1924 }
1925
1926 return dhcp ? TRUE : FALSE;
1927}
1928
1937static LSTATUS
1938SetNameServerAddresses(PWSTR itf_id, const nrpt_address_t *addresses)
1939{
1940 const short families[] = { AF_INET, AF_INET6 };
1941 for (int i = 0; i < _countof(families); i++)
1942 {
1943 short family = families[i];
1944
1945 /* Create a comma sparated list of addresses of this family */
1946 int offset = 0;
1947 char addr_list[NRPT_ADDR_SIZE * NRPT_ADDR_NUM];
1948 for (int j = 0; j < NRPT_ADDR_NUM && addresses[j][0]; j++)
1949 {
1950 if ((family == AF_INET6 && strchr(addresses[j], ':') == NULL)
1951 || (family == AF_INET && strchr(addresses[j], ':') != NULL))
1952 {
1953 /* Address family doesn't match, skip this one */
1954 continue;
1955 }
1956 if (offset)
1957 {
1958 addr_list[offset++] = ',';
1959 }
1960 strcpy(addr_list + offset, addresses[j]);
1961 offset += strlen(addresses[j]);
1962 }
1963
1964 if (offset == 0)
1965 {
1966 /* No address for this family to set */
1967 continue;
1968 }
1969
1970 /* Set name server addresses */
1971 LSTATUS err = SetNameServers(itf_id, family, addr_list);
1972 if (err)
1973 {
1974 return err;
1975 }
1976 }
1977 return NO_ERROR;
1978}
1979
1990static LSTATUS
1991GetItfDnsServersV4(HKEY itf_key, PSTR addrs, PDWORD size)
1992{
1993 addrs[*size - 1] = '\0';
1994
1995 LSTATUS err;
1996 DWORD s = *size;
1997 err = RegGetValueA(itf_key, NULL, "NameServer", RRF_RT_REG_SZ, NULL, (PBYTE)addrs, &s);
1998 if (err && err != ERROR_FILE_NOT_FOUND)
1999 {
2000 *size = 0;
2001 return err;
2002 }
2003
2004 /* Try DHCP addresses if we don't have some already */
2005 if (!strchr(addrs, '.') && IsDhcpEnabled(itf_key))
2006 {
2007 s = *size;
2008 RegGetValueA(itf_key, NULL, "DhcpNameServer", RRF_RT_REG_SZ, NULL, (PBYTE)addrs, &s);
2009 if (err)
2010 {
2011 *size = 0;
2012 return err;
2013 }
2014 }
2015
2016 if (strchr(addrs, '.'))
2017 {
2018 *size = s;
2019 return NO_ERROR;
2020 }
2021
2022 *size = 0;
2023 return ERROR_FILE_NOT_FOUND;
2024}
2025
2035static LSTATUS
2036GetItfDnsServersV6(HKEY itf_key, PSTR addrs, PDWORD size)
2037{
2038 addrs[*size - 1] = '\0';
2039
2040 LSTATUS err;
2041 DWORD s = *size;
2042 err = RegGetValueA(itf_key, NULL, "NameServer", RRF_RT_REG_SZ, NULL, (PBYTE)addrs, &s);
2043 if (err && err != ERROR_FILE_NOT_FOUND)
2044 {
2045 *size = 0;
2046 return err;
2047 }
2048
2049 /* Try DHCP addresses if we don't have some already */
2050 if (!strchr(addrs, ':') && IsDhcpEnabled(itf_key))
2051 {
2052 IN6_ADDR in_addrs[8];
2053 DWORD in_addrs_size = sizeof(in_addrs);
2054 err = RegGetValueA(itf_key, NULL, "Dhcpv6DNSServers", RRF_RT_REG_BINARY, NULL,
2055 (PBYTE)in_addrs, &in_addrs_size);
2056 if (err)
2057 {
2058 *size = 0;
2059 return err;
2060 }
2061
2062 s = *size;
2063 PSTR pos = addrs;
2064 size_t in_addrs_read = in_addrs_size / sizeof(IN6_ADDR);
2065 for (size_t i = 0; i < in_addrs_read; ++i)
2066 {
2067 if (i != 0)
2068 {
2069 /* Add separator */
2070 *pos++ = ',';
2071 s--;
2072 }
2073
2074 if (inet_ntop(AF_INET6, &in_addrs[i], pos, s) != NULL)
2075 {
2076 *size = 0;
2077 return ERROR_MORE_DATA;
2078 }
2079
2080 size_t addr_len = strlen(pos);
2081 pos += addr_len;
2082 s -= addr_len;
2083 }
2084 s = strlen(addrs) + 1;
2085 }
2086
2087 if (strchr(addrs, ':'))
2088 {
2089 *size = s;
2090 return NO_ERROR;
2091 }
2092
2093 *size = 0;
2094 return ERROR_FILE_NOT_FOUND;
2095}
2096
2106static BOOL
2107ListContainsDomain(PCWSTR list, PCWSTR domain, size_t len)
2108{
2109 PCWSTR match = list;
2110 while (TRUE)
2111 {
2112 match = wcsstr(match, domain);
2113 if (!match)
2114 {
2115 /* Domain has not matched */
2116 break;
2117 }
2118 if ((match == list || *(match - 1) == ',')
2119 && (*(match + len) == ',' || *(match + len) == '\0'))
2120 {
2121 /* Domain has matched fully */
2122 return TRUE;
2123 }
2124 match += len;
2125 }
2126 return FALSE;
2127}
2128
2150static LSTATUS
2151GetItfDnsDomains(HKEY itf, PCWSTR search_domains, PWSTR domains, PDWORD size)
2152{
2153 if (domains == NULL || size == 0)
2154 {
2155 return ERROR_INVALID_PARAMETER;
2156 }
2157
2158 LSTATUS err = ERROR_FILE_NOT_FOUND;
2159 const DWORD buf_size = *size;
2160 const size_t one_glyph = sizeof(*domains);
2161 PWSTR values[] = { L"SearchList", L"Domain", L"DhcpDomainSearchList", L"DhcpDomain", NULL };
2162
2163 for (int i = 0; values[i]; i++)
2164 {
2165 *size = buf_size;
2166 err = RegGetValueW(itf, NULL, values[i], RRF_RT_REG_SZ, NULL, (PBYTE)domains, size);
2167 if (!err && *size > one_glyph && wcschr(domains, '.'))
2168 {
2169 /*
2170 * Found domain(s), now convert them:
2171 * - prefix each domain with a dot
2172 * - convert comma separated list to MULTI_SZ
2173 */
2174 PWCHAR pos = domains;
2175 const DWORD buf_len = buf_size / one_glyph;
2176 while (TRUE)
2177 {
2178 /* Terminate the domain at the next comma */
2179 PWCHAR comma = wcschr(pos, ',');
2180 if (comma)
2181 {
2182 *comma = '\0';
2183 }
2184
2185 /* Ignore itf domains which match a pushed search domain */
2186 size_t domain_len = wcslen(pos);
2187 if (ListContainsDomain(search_domains, pos, domain_len))
2188 {
2189 if (comma)
2190 {
2191 pos = comma + 1;
2192 continue;
2193 }
2194 else
2195 {
2196 /* This was the last domain */
2197 *pos = '\0';
2198 *size += one_glyph;
2199 return wcslen(domains) ? NO_ERROR : ERROR_FILE_NOT_FOUND;
2200 }
2201 }
2202
2203 /* Check for enough space to convert this domain */
2204 domain_len += 1; /* leading dot */
2205 size_t converted_size = pos - domains;
2206 size_t domain_size = domain_len * one_glyph;
2207 size_t extra_size = 2 * one_glyph;
2208 if (converted_size + domain_size + extra_size > buf_size)
2209 {
2210 /* Domain doesn't fit, bad luck if it's the first one */
2211 *pos = '\0';
2212 *size = converted_size == 0 ? 0 : *size + 1;
2213 return ERROR_MORE_DATA;
2214 }
2215
2216 /* Prefix domain at pos with the dot */
2217 memmove(pos + 1, pos, buf_size - converted_size - one_glyph);
2218 domains[buf_len - 1] = '\0';
2219 *pos = '.';
2220 *size += one_glyph;
2221
2222 if (!comma)
2223 {
2224 /* Conversion is done */
2225 *(pos + domain_len) = '\0';
2226 *size += one_glyph;
2227 return NO_ERROR;
2228 }
2229
2230 pos = comma + 1;
2231 }
2232 }
2233 }
2234
2235 *size = 0;
2236 return err;
2237}
2238
2247static BOOL
2249{
2250 GUID iid;
2251 BOOL res = FALSE;
2252 MIB_IF_ROW2 itf_row;
2253
2254 /* Get GUID from string */
2255 if (IIDFromString(iid_str, &iid) != S_OK)
2256 {
2257 MsgToEventLog(M_SYSERR, L"%S: could not convert interface %s GUID string", __func__,
2258 iid_str);
2259 goto out;
2260 }
2261
2262 /* Get LUID from GUID */
2263 if (ConvertInterfaceGuidToLuid(&iid, &itf_row.InterfaceLuid) != NO_ERROR)
2264 {
2265 goto out;
2266 }
2267
2268 /* Look up interface status */
2269 if (GetIfEntry2(&itf_row) != NO_ERROR)
2270 {
2271 MsgToEventLog(M_SYSERR, L"%S: could not get interface %s status", __func__, iid_str);
2272 goto out;
2273 }
2274
2275 if (itf_row.MediaConnectState == MediaConnectStateConnected
2276 && itf_row.OperStatus == IfOperStatusUp)
2277 {
2278 res = TRUE;
2279 }
2280
2281out:
2282 return res;
2283}
2284
2294static void
2295GetNrptExcludeData(PCWSTR search_domains, nrpt_exclude_data_t *data, size_t data_size)
2296{
2297 HKEY v4_itfs = INVALID_HANDLE_VALUE;
2298 HKEY v6_itfs = INVALID_HANDLE_VALUE;
2299
2300 if (!GetInterfacesKey(AF_INET, &v4_itfs) || !GetInterfacesKey(AF_INET6, &v6_itfs))
2301 {
2302 goto out;
2303 }
2304
2305 size_t i = 0;
2306 DWORD enum_index = 0;
2307 while (i < data_size)
2308 {
2309 WCHAR itf_guid[MAX_PATH];
2310 DWORD itf_guid_len = _countof(itf_guid);
2311 LSTATUS err =
2312 RegEnumKeyExW(v4_itfs, enum_index++, itf_guid, &itf_guid_len, NULL, NULL, NULL, NULL);
2313 if (err)
2314 {
2315 if (err != ERROR_NO_MORE_ITEMS)
2316 {
2317 MsgToEventLog(M_SYSERR, L"%S: could not enumerate interfaces (%lu)", __func__, err);
2318 }
2319 goto out;
2320 }
2321
2322 /* Ignore interfaces that are not connected or disabled */
2323 if (!IsInterfaceConnected(itf_guid))
2324 {
2325 continue;
2326 }
2327
2328 HKEY v4_itf;
2329 if (RegOpenKeyExW(v4_itfs, itf_guid, 0, KEY_READ, &v4_itf) != NO_ERROR)
2330 {
2331 MsgToEventLog(M_SYSERR, L"%S: could not open interface %s v4 registry key", __func__,
2332 itf_guid);
2333 goto out;
2334 }
2335
2336 /* Get the DNS domain(s) for exclude routing */
2337 data[i].domains_size = sizeof(data[0].domains);
2338 memset(data[i].domains, 0, data[i].domains_size);
2339 err = GetItfDnsDomains(v4_itf, search_domains, data[i].domains, &data[i].domains_size);
2340 if (err)
2341 {
2342 if (err != ERROR_FILE_NOT_FOUND)
2343 {
2344 MsgToEventLog(M_SYSERR, L"%S: could not read interface %s domain suffix", __func__,
2345 itf_guid);
2346 }
2347 goto next_itf;
2348 }
2349
2350 /* Get the IPv4 DNS servers */
2351 DWORD v4_addrs_size = sizeof(data[0].addresses);
2352 err = GetItfDnsServersV4(v4_itf, data[i].addresses, &v4_addrs_size);
2353 if (err && err != ERROR_FILE_NOT_FOUND)
2354 {
2355 MsgToEventLog(M_SYSERR, L"%S: could not read interface %s v4 name servers (%ld)",
2356 __func__, itf_guid, err);
2357 goto next_itf;
2358 }
2359
2360 /* Get the IPv6 DNS servers, if there's space left */
2361 PSTR v6_addrs = data[i].addresses + v4_addrs_size;
2362 DWORD v6_addrs_size = sizeof(data[0].addresses) - v4_addrs_size;
2363 if (v6_addrs_size > NRPT_ADDR_SIZE)
2364 {
2365 HKEY v6_itf;
2366 if (RegOpenKeyExW(v6_itfs, itf_guid, 0, KEY_READ, &v6_itf) != NO_ERROR)
2367 {
2368 MsgToEventLog(M_SYSERR, L"%S: could not open interface %s v6 registry key",
2369 __func__, itf_guid);
2370 goto next_itf;
2371 }
2372 err = GetItfDnsServersV6(v6_itf, v6_addrs, &v6_addrs_size);
2373 RegCloseKey(v6_itf);
2374 if (err && err != ERROR_FILE_NOT_FOUND)
2375 {
2376 MsgToEventLog(M_SYSERR, L"%S: could not read interface %s v6 name servers (%ld)",
2377 __func__, itf_guid, err);
2378 goto next_itf;
2379 }
2380 }
2381
2382 if (v4_addrs_size || v6_addrs_size)
2383 {
2384 /* Replace delimiters with semicolons, as required by NRPT */
2385 for (int j = 0; j < sizeof(data[0].addresses) && data[i].addresses[j]; j++)
2386 {
2387 if (data[i].addresses[j] == ',' || data[i].addresses[j] == ' ')
2388 {
2389 data[i].addresses[j] = ';';
2390 }
2391 }
2392 ++i;
2393 }
2394
2395next_itf:
2396 RegCloseKey(v4_itf);
2397 }
2398
2399out:
2400 RegCloseKey(v6_itfs);
2401 RegCloseKey(v4_itfs);
2402}
2403
2416static DWORD
2417SetNrptRule(HKEY nrpt_key, PCWSTR subkey, PCSTR address, PCWSTR domains, DWORD dom_size,
2418 BOOL dnssec)
2419{
2420 /* Create rule subkey */
2421 DWORD err = NO_ERROR;
2422 HKEY rule_key;
2423 err = RegCreateKeyExW(nrpt_key, subkey, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &rule_key, NULL);
2424 if (err)
2425 {
2426 return err;
2427 }
2428
2429 /* Set name(s) for DNS routing */
2430 err = RegSetValueExW(rule_key, L"Name", 0, REG_MULTI_SZ, (PBYTE)domains, dom_size);
2431 if (err)
2432 {
2433 goto out;
2434 }
2435
2436 /* Set DNS Server address */
2437 err = RegSetValueExA(rule_key, "GenericDNSServers", 0, REG_SZ, (PBYTE)address,
2438 strlen(address) + 1);
2439 if (err)
2440 {
2441 goto out;
2442 }
2443
2444 DWORD reg_val;
2445 /* Set DNSSEC if required */
2446 if (dnssec)
2447 {
2448 reg_val = 1;
2449 err = RegSetValueExA(rule_key, "DNSSECValidationRequired", 0, REG_DWORD, (PBYTE)&reg_val,
2450 sizeof(reg_val));
2451 if (err)
2452 {
2453 goto out;
2454 }
2455
2456 reg_val = 0;
2457 err = RegSetValueExA(rule_key, "DNSSECQueryIPSECRequired", 0, REG_DWORD, (PBYTE)&reg_val,
2458 sizeof(reg_val));
2459 if (err)
2460 {
2461 goto out;
2462 }
2463
2464 reg_val = 0;
2465 err = RegSetValueExA(rule_key, "DNSSECQueryIPSECEncryption", 0, REG_DWORD, (PBYTE)&reg_val,
2466 sizeof(reg_val));
2467 if (err)
2468 {
2469 goto out;
2470 }
2471 }
2472
2473 /* Set NRPT config options */
2474 reg_val = dnssec ? 0x0000000A : 0x00000008;
2475 err = RegSetValueExA(rule_key, "ConfigOptions", 0, REG_DWORD, (const PBYTE)&reg_val,
2476 sizeof(reg_val));
2477 if (err)
2478 {
2479 goto out;
2480 }
2481
2482 /* Mandatory NRPT version */
2483 reg_val = 2;
2484 err = RegSetValueExA(rule_key, "Version", 0, REG_DWORD, (const PBYTE)&reg_val, sizeof(reg_val));
2485 if (err)
2486 {
2487 goto out;
2488 }
2489
2490out:
2491 if (err)
2492 {
2493 RegDeleteKeyW(nrpt_key, subkey);
2494 }
2495 RegCloseKey(rule_key);
2496 return err;
2497}
2498
2508static void
2509SetNrptExcludeRules(HKEY nrpt_key, DWORD ovpn_pid, PCWSTR search_domains)
2510{
2511 nrpt_exclude_data_t data[8]; /* data from up to 8 interfaces */
2512 memset(data, 0, sizeof(data));
2513 GetNrptExcludeData(search_domains, data, _countof(data));
2514
2515 unsigned n = 0;
2516 for (int i = 0; i < _countof(data); ++i)
2517 {
2518 nrpt_exclude_data_t *d = &data[i];
2519 if (d->domains_size == 0)
2520 {
2521 break;
2522 }
2523
2524 DWORD err;
2525 WCHAR subkey[48];
2526 swprintf(subkey, _countof(subkey), L"OpenVPNDNSRoutingX-%02x-%lu", ++n, ovpn_pid);
2527 err = SetNrptRule(nrpt_key, subkey, d->addresses, d->domains, d->domains_size, FALSE);
2528 if (err)
2529 {
2530 MsgToEventLog(M_ERR, L"%S: failed to set rule %s (%lu)", __func__, subkey, err);
2531 }
2532 }
2533}
2534
2547static DWORD
2548SetNrptRules(HKEY nrpt_key, const nrpt_address_t *addresses, const char *domains,
2549 const char *search_domains, BOOL dnssec, DWORD ovpn_pid)
2550{
2551 DWORD err = NO_ERROR;
2552 PWSTR wide_domains = L".\0"; /* DNS route everything by default */
2553 DWORD dom_size = 6;
2554
2555 /* Prepare DNS routing domains / split DNS */
2556 if (domains[0])
2557 {
2558 size_t domains_len = strlen(domains);
2559 dom_size = domains_len + 2; /* len + the trailing NULs */
2560
2561 wide_domains = utf8to16_size(domains, dom_size);
2562 dom_size *= sizeof(*wide_domains);
2563 if (!wide_domains)
2564 {
2565 return ERROR_OUTOFMEMORY;
2566 }
2567 /* Make a MULTI_SZ from a comma separated list */
2568 for (size_t i = 0; i < domains_len; ++i)
2569 {
2570 if (wide_domains[i] == ',')
2571 {
2572 wide_domains[i] = 0;
2573 }
2574 }
2575 }
2576 else
2577 {
2578 PWSTR wide_search_domains;
2579 wide_search_domains = utf8to16(search_domains);
2580 if (!wide_search_domains)
2581 {
2582 return ERROR_OUTOFMEMORY;
2583 }
2584 SetNrptExcludeRules(nrpt_key, ovpn_pid, wide_search_domains);
2585 free(wide_search_domains);
2586 }
2587
2588 /* Create address string list */
2589 CHAR addr_list[NRPT_ADDR_NUM * NRPT_ADDR_SIZE];
2590 PSTR pos = addr_list;
2591 for (int i = 0; i < NRPT_ADDR_NUM && addresses[i][0]; ++i)
2592 {
2593 if (i != 0)
2594 {
2595 *pos++ = ';';
2596 }
2597 strcpy(pos, addresses[i]);
2598 pos += strlen(pos);
2599 }
2600
2601 WCHAR subkey[MAX_PATH];
2602 swprintf(subkey, _countof(subkey), L"OpenVPNDNSRouting-%lu", ovpn_pid);
2603 err = SetNrptRule(nrpt_key, subkey, addr_list, wide_domains, dom_size, dnssec);
2604 if (err)
2605 {
2606 MsgToEventLog(M_ERR, L"%S: failed to set rule %s (%lu)", __func__, subkey, err);
2607 }
2608
2609 if (domains[0])
2610 {
2611 free(wide_domains);
2612 }
2613 return err;
2614}
2615
2616#if defined(__GNUC__) || defined(__clang__)
2617#pragma GCC diagnostic pop
2618#endif
2619
2628static LSTATUS
2629OpenNrptBaseKey(PHKEY key, PBOOL gpol)
2630{
2631 /*
2632 * Registry keys Name Service Policy Table (NRPT) rules can be stored at.
2633 * When the group policy key exists, NRPT rules must be placed there.
2634 * It is created when NRPT rules are pushed via group policy and it
2635 * remains in the registry even if the last GP-NRPT rule is deleted.
2636 */
2637 static PCSTR gpol_key = "SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient\\DnsPolicyConfig";
2638 static PCSTR sys_key =
2639 "SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters\\DnsPolicyConfig";
2640
2641 HKEY nrpt;
2642 *gpol = TRUE;
2643 LSTATUS err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, gpol_key, 0, KEY_ALL_ACCESS, &nrpt);
2644 if (err == ERROR_FILE_NOT_FOUND)
2645 {
2646 *gpol = FALSE;
2647 err = RegCreateKeyExA(HKEY_LOCAL_MACHINE, sys_key, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &nrpt,
2648 NULL);
2649 if (err)
2650 {
2651 nrpt = INVALID_HANDLE_VALUE;
2652 }
2653 }
2654 *key = nrpt;
2655 return err;
2656}
2657
2669static BOOL
2670DeleteNrptRules(DWORD pid, PBOOL gpol)
2671{
2672 HKEY key;
2673 LSTATUS err = OpenNrptBaseKey(&key, gpol);
2674 if (err)
2675 {
2676 MsgToEventLog(M_SYSERR, L"%S: could not open NRPT base key (%lu)", __func__, err);
2677 return FALSE;
2678 }
2679
2680 /* PID suffix string to compare against later */
2681 WCHAR pid_str[16];
2682 size_t pidlen = 0;
2683 if (pid)
2684 {
2685 swprintf(pid_str, _countof(pid_str), L"-%lu", pid);
2686 pidlen = wcslen(pid_str);
2687 }
2688
2689 int deleted = 0;
2690 DWORD enum_index = 0;
2691 while (TRUE)
2692 {
2693 WCHAR name[MAX_PATH];
2694 DWORD namelen = _countof(name);
2695 err = RegEnumKeyExW(key, enum_index++, name, &namelen, NULL, NULL, NULL, NULL);
2696 if (err)
2697 {
2698 if (err != ERROR_NO_MORE_ITEMS)
2699 {
2700 MsgToEventLog(M_SYSERR, L"%S: could not enumerate NRPT rules (%lu)", __func__, err);
2701 }
2702 break;
2703 }
2704
2705 /* Keep rule if name doesn't match */
2706 if (wcsncmp(name, L"OpenVPNDNSRouting", 17) != 0
2707 || (pid && wcsncmp(name + namelen - pidlen, pid_str, pidlen) != 0))
2708 {
2709 continue;
2710 }
2711
2712 if (RegDeleteKeyW(key, name) == NO_ERROR)
2713 {
2714 enum_index--;
2715 deleted++;
2716 }
2717 }
2718
2719 RegCloseKey(key);
2720 return deleted ? TRUE : FALSE;
2721}
2722
2728static void
2729UndoNrptRules(DWORD ovpn_pid)
2730{
2731 BOOL gpol;
2732 if (DeleteNrptRules(ovpn_pid, &gpol))
2733 {
2734 ApplyDnsSettings(gpol);
2735 }
2736}
2737
2749static DWORD
2751{
2752 /*
2753 * Use a non-const reference with limited scope to
2754 * enforce null-termination of strings from client
2755 */
2756 {
2758 msgptr->iface.name[_countof(msg->iface.name) - 1] = '\0';
2759 msgptr->search_domains[_countof(msg->search_domains) - 1] = '\0';
2760 msgptr->resolve_domains[_countof(msg->resolve_domains) - 1] = '\0';
2761 for (size_t i = 0; i < NRPT_ADDR_NUM; ++i)
2762 {
2763 msgptr->addresses[i][_countof(msg->addresses[0]) - 1] = '\0';
2764 }
2765 }
2766
2767 /* Make sure we have the VPN interface name */
2768 if (msg->iface.name[0] == 0)
2769 {
2770 return ERROR_MESSAGE_DATA;
2771 }
2772
2773 /* Some sanity checks on the add message data */
2774 if (msg->header.type == msg_add_nrpt_cfg)
2775 {
2776 /* At least one name server address is set */
2777 if (msg->addresses[0][0] == 0)
2778 {
2779 return ERROR_MESSAGE_DATA;
2780 }
2781 /* Resolve domains are double zero terminated (MULTI_SZ) */
2782 const char *rdom = msg->resolve_domains;
2783 size_t rdom_size = sizeof(msg->resolve_domains);
2784 size_t rdom_len = strlen(rdom);
2785 if (rdom_len && (rdom_len + 1 >= rdom_size || rdom[rdom_len + 2] != 0))
2786 {
2787 return ERROR_MESSAGE_DATA;
2788 }
2789 }
2790
2791 BOOL gpol_nrpt = FALSE;
2792 BOOL gpol_list = FALSE;
2793
2794 WCHAR iid[64];
2795 DWORD iid_err = InterfaceIdString(msg->iface.name, iid, _countof(iid));
2796 if (iid_err)
2797 {
2798 return iid_err;
2799 }
2800
2801 /* Delete previously set values for this instance first, if any */
2802 PDWORD undo_pid = RemoveListItem(&(*lists)[undo_nrpt], CmpAny, NULL);
2803 if (undo_pid)
2804 {
2805 if (*undo_pid != ovpn_pid)
2806 {
2808 L"%S: PID stored for undo doesn't match: %lu vs %lu. "
2809 "This is likely an error. Cleaning up anyway.",
2810 __func__, *undo_pid, ovpn_pid);
2811 }
2812 DeleteNrptRules(*undo_pid, &gpol_nrpt);
2813 free(undo_pid);
2814
2815 ResetNameServers(iid, AF_INET);
2816 ResetNameServers(iid, AF_INET6);
2817 }
2818 SetDnsSearchDomains(msg->iface.name, NULL, &gpol_list, lists);
2819
2820 if (msg->header.type == msg_del_nrpt_cfg)
2821 {
2822 ApplyDnsSettings(gpol_nrpt || gpol_list);
2823 return NO_ERROR; /* Done dealing with del message */
2824 }
2825
2826 HKEY key;
2827 LSTATUS err = OpenNrptBaseKey(&key, &gpol_nrpt);
2828 if (err)
2829 {
2830 goto out;
2831 }
2832
2833 /* Add undo information first in case there's no heap left */
2834 PDWORD pid = malloc(sizeof(ovpn_pid));
2835 if (!pid)
2836 {
2837 err = ERROR_OUTOFMEMORY;
2838 goto out;
2839 }
2840 *pid = ovpn_pid;
2841 if (AddListItem(&(*lists)[undo_nrpt], pid))
2842 {
2843 err = ERROR_OUTOFMEMORY;
2844 free(pid);
2845 goto out;
2846 }
2847
2848 /* Set NRPT rules */
2849 BOOL dnssec = (msg->flags & nrpt_dnssec) != 0;
2850 err = SetNrptRules(key, msg->addresses, msg->resolve_domains, msg->search_domains, dnssec,
2851 ovpn_pid);
2852 if (err)
2853 {
2854 goto out;
2855 }
2856
2857 /* Set name servers */
2858 err = SetNameServerAddresses(iid, msg->addresses);
2859 if (err)
2860 {
2861 goto out;
2862 }
2863
2864 /* Set search domains, if any */
2865 if (msg->search_domains[0])
2866 {
2867 err = SetDnsSearchDomains(msg->iface.name, msg->search_domains, &gpol_list, lists);
2868 }
2869
2870 ApplyDnsSettings(gpol_nrpt || gpol_list);
2871
2872out:
2873 return err;
2874}
2875
2876static DWORD
2878{
2879 DWORD err = NO_ERROR;
2880 wchar_t addr[16]; /* large enough to hold string representation of an ipv4 */
2881 int addr_len = msg->addr_len;
2882
2883 /* sanity check */
2884 if (addr_len > _countof(msg->addr))
2885 {
2886 addr_len = _countof(msg->addr);
2887 }
2888
2889 if (!msg->iface.index) /* interface index is required */
2890 {
2891 return ERROR_MESSAGE_DATA;
2892 }
2893
2894 /* We delete all current addresses before adding any
2895 * OR if the message type is del_wins_cfg
2896 */
2897 if (addr_len > 0 || msg->header.type == msg_del_wins_cfg)
2898 {
2899 err = netsh_wins_cmd(L"delete", msg->iface.index, NULL);
2900 if (err)
2901 {
2902 goto out;
2903 }
2904 free(RemoveListItem(&(*lists)[undo_wins], CmpAny, NULL));
2905 }
2906
2907 if (addr_len == 0 || msg->header.type == msg_del_wins_cfg)
2908 {
2909 goto out; /* job done */
2910 }
2911
2912 for (int i = 0; i < addr_len; ++i)
2913 {
2914 RtlIpv4AddressToStringW(&msg->addr[i].ipv4, addr);
2915 err = netsh_wins_cmd(i == 0 ? L"set" : L"add", msg->iface.index, addr);
2916 if (i == 0 && err)
2917 {
2918 goto out;
2919 }
2920 /* We do not check for duplicate addresses, so any error in adding
2921 * additional addresses is ignored.
2922 */
2923 }
2924
2925 int *if_index = malloc(sizeof(msg->iface.index));
2926 if (if_index)
2927 {
2928 *if_index = msg->iface.index;
2929 }
2930
2931 if (!if_index || AddListItem(&(*lists)[undo_wins], if_index))
2932 {
2933 free(if_index);
2934 netsh_wins_cmd(L"delete", msg->iface.index, NULL);
2935 err = ERROR_OUTOFMEMORY;
2936 goto out;
2937 }
2938
2939 err = 0;
2940
2941out:
2942 return err;
2943}
2944
2945static DWORD
2947{
2948 DWORD err = 0;
2949 DWORD timeout = 5000; /* in milli seconds */
2950 wchar_t argv0[MAX_PATH];
2951
2952 /* Path of netsh */
2953 swprintf(argv0, _countof(argv0), L"%ls\\%ls", get_win_sys_path(), L"netsh.exe");
2954
2955 /* cmd template:
2956 * netsh interface ipv4 set address name=$if_index source=dhcp
2957 */
2958 const wchar_t *fmt = L"netsh interface ipv4 set address name=\"%d\" source=dhcp";
2959
2960 /* max cmdline length in wchars -- include room for if index:
2961 * 10 chars for 32 bit int in decimal and +1 for NUL
2962 */
2963 size_t ncmdline = wcslen(fmt) + 10 + 1;
2964 wchar_t *cmdline = malloc(ncmdline * sizeof(wchar_t));
2965 if (!cmdline)
2966 {
2967 err = ERROR_OUTOFMEMORY;
2968 return err;
2969 }
2970
2971 swprintf(cmdline, ncmdline, fmt, dhcp->iface.index);
2972
2973 err = ExecCommand(argv0, cmdline, timeout);
2974
2975 /* Note: This could fail if dhcp is already enabled, so the caller
2976 * may not want to treat errors as FATAL.
2977 */
2978
2979 free(cmdline);
2980 return err;
2981}
2982
2983static DWORD
2985{
2986 DWORD err = 0;
2987 MIB_IPINTERFACE_ROW ipiface;
2988 InitializeIpInterfaceEntry(&ipiface);
2989 ipiface.Family = mtu->family;
2990 ipiface.InterfaceIndex = mtu->iface.index;
2991 err = GetIpInterfaceEntry(&ipiface);
2992 if (err != NO_ERROR)
2993 {
2994 return err;
2995 }
2996 if (mtu->family == AF_INET)
2997 {
2998 ipiface.SitePrefixLength = 0;
2999 }
3000 ipiface.NlMtu = mtu->mtu;
3001
3002 err = SetIpInterfaceEntry(&ipiface);
3003 return err;
3004}
3005
3013static DWORD
3015{
3016 const WCHAR *hwid;
3017
3018 switch (msg->adapter_type)
3019 {
3020 case ADAPTER_TYPE_DCO:
3021 hwid = L"ovpn-dco";
3022 break;
3023
3024 case ADAPTER_TYPE_TAP:
3025 hwid = L"root\\tap0901";
3026 break;
3027
3028 default:
3029 return ERROR_INVALID_PARAMETER;
3030 }
3031
3032 WCHAR cmd[MAX_PATH];
3033 WCHAR args[MAX_PATH];
3034
3035 if (swprintf_s(cmd, _countof(cmd), L"%s\\tapctl.exe", settings.bin_dir) < 0)
3036 {
3037 return ERROR_BUFFER_OVERFLOW;
3038 }
3039
3040 if (swprintf_s(args, _countof(args), L"tapctl create --hwid %s", hwid) < 0)
3041 {
3042 return ERROR_BUFFER_OVERFLOW;
3043 }
3044
3045 return ExecCommand(cmd, args, 10000);
3046}
3047
3048static VOID
3049HandleMessage(HANDLE pipe, PPROCESS_INFORMATION proc_info, DWORD bytes, DWORD count,
3050 LPHANDLE events, undo_lists_t *lists)
3051{
3053 ack_message_t ack = {
3054 .header = { .type = msg_acknowledgement, .size = sizeof(ack), .message_id = -1 },
3055 .error_number = ERROR_MESSAGE_DATA
3056 };
3057
3058 DWORD read = ReadPipeAsync(pipe, &msg, bytes, count, events);
3059 if (read != bytes || read < sizeof(msg.header) || read != msg.header.size)
3060 {
3061 goto out;
3062 }
3063
3064 ack.header.message_id = msg.header.message_id;
3065
3066 switch (msg.header.type)
3067 {
3068 case msg_add_address:
3069 case msg_del_address:
3070 if (msg.header.size == sizeof(msg.address))
3071 {
3072 ack.error_number = HandleAddressMessage(&msg.address, lists);
3073 }
3074 break;
3075
3076 case msg_add_route:
3077 case msg_del_route:
3078 if (msg.header.size == sizeof(msg.route))
3079 {
3080 ack.error_number = HandleRouteMessage(&msg.route, lists);
3081 }
3082 break;
3083
3085 if (msg.header.size == sizeof(msg.flush_neighbors))
3086 {
3087 ack.error_number = HandleFlushNeighborsMessage(&msg.flush_neighbors);
3088 }
3089 break;
3090
3091 case msg_add_wfp_block:
3092 case msg_del_wfp_block:
3093 if (msg.header.size == sizeof(msg.wfp_block))
3094 {
3095 ack.error_number = HandleWfpBlockMessage(&msg.wfp_block, lists);
3096 }
3097 break;
3098
3099 case msg_register_dns:
3101 break;
3102
3103 case msg_add_dns_cfg:
3104 case msg_del_dns_cfg:
3105 ack.error_number = HandleDNSConfigMessage(&msg.dns, lists);
3106 break;
3107
3108 case msg_add_nrpt_cfg:
3109 case msg_del_nrpt_cfg:
3110 {
3111 DWORD ovpn_pid = proc_info->dwProcessId;
3112 ack.error_number = HandleDNSConfigNrptMessage(&msg.nrpt_dns, ovpn_pid, lists);
3113 }
3114 break;
3115
3116 case msg_add_wins_cfg:
3117 case msg_del_wins_cfg:
3118 ack.error_number = HandleWINSConfigMessage(&msg.wins, lists);
3119 break;
3120
3121 case msg_enable_dhcp:
3122 if (msg.header.size == sizeof(msg.dhcp))
3123 {
3125 }
3126 break;
3127
3128 case msg_set_mtu:
3129 if (msg.header.size == sizeof(msg.mtu))
3130 {
3131 ack.error_number = HandleMTUMessage(&msg.mtu);
3132 }
3133 break;
3134
3135 case msg_create_adapter:
3136 if (msg.header.size == sizeof(msg.create_adapter))
3137 {
3138 ack.error_number = HandleCreateAdapterMessage(&msg.create_adapter);
3139 }
3140 break;
3141
3142 default:
3144 MsgToEventLog(MSG_FLAGS_ERROR, L"Unknown message type %d", msg.header.type);
3145 break;
3146 }
3147
3148out:
3149 WritePipeAsync(pipe, &ack, sizeof(ack), count, events);
3150}
3151
3152
3153static VOID
3155{
3156 undo_type_t type;
3157 wfp_block_data_t *interface_data;
3158 for (type = 0; type < _undo_type_max; type++)
3159 {
3160 list_item_t **pnext = &(*lists)[type];
3161 while (*pnext)
3162 {
3163 list_item_t *item = *pnext;
3164 switch (type)
3165 {
3166 case address:
3167 DeleteAddress(item->data);
3168 break;
3169
3170 case route:
3171 DeleteRoute(item->data);
3172 break;
3173
3174 case undo_dns4:
3175 ResetNameServers(item->data, AF_INET);
3176 break;
3177
3178 case undo_dns6:
3179 ResetNameServers(item->data, AF_INET6);
3180 break;
3181
3182 case undo_nrpt:
3183 UndoNrptRules(*(PDWORD)item->data);
3184 break;
3185
3186 case undo_domains:
3188 break;
3189
3190 case undo_wins:
3191 netsh_wins_cmd(L"delete", *(int *)item->data, NULL);
3192 break;
3193
3194 case wfp_block:
3195 interface_data = (wfp_block_data_t *)(item->data);
3196 delete_wfp_block_filters(interface_data->engine);
3197 if (interface_data->metric_v4 >= 0)
3198 {
3199 set_interface_metric(interface_data->index, AF_INET,
3200 interface_data->metric_v4);
3201 }
3202 if (interface_data->metric_v6 >= 0)
3203 {
3204 set_interface_metric(interface_data->index, AF_INET6,
3205 interface_data->metric_v6);
3206 }
3207 break;
3208
3209 case _undo_type_max:
3210 /* unreachable */
3211 break;
3212 }
3213
3214 /* Remove from the list and free memory */
3215 *pnext = item->next;
3216 free(item->data);
3217 free(item);
3218 }
3219 }
3220}
3221
3222static DWORD WINAPI
3223RunOpenvpn(LPVOID p)
3224{
3225 HANDLE pipe = p;
3226 HANDLE ovpn_pipe = NULL, svc_pipe = NULL;
3227 PTOKEN_USER svc_user = NULL, ovpn_user = NULL;
3228 HANDLE svc_token = NULL, imp_token = NULL, pri_token = NULL;
3229 HANDLE stdin_read = NULL, stdin_write = NULL;
3230 HANDLE stdout_write = NULL;
3231 DWORD pipe_mode, len, exit_code = 0;
3232 STARTUP_DATA sud = { 0, 0, 0 };
3233 STARTUPINFOW startup_info;
3234 PROCESS_INFORMATION proc_info;
3235 LPVOID user_env = NULL;
3236 WCHAR ovpn_pipe_name[256]; /* The entire pipe name string can be up to 256 characters long
3237 according to MSDN. */
3238 LPCWSTR exe_path;
3239 WCHAR *cmdline = NULL;
3240 size_t cmdline_size;
3241 undo_lists_t undo_lists;
3242 WCHAR errmsg[512] = L"";
3243
3244 SECURITY_ATTRIBUTES inheritable = { .nLength = sizeof(inheritable),
3245 .lpSecurityDescriptor = NULL,
3246 .bInheritHandle = TRUE };
3247
3248 PACL ovpn_dacl;
3249 EXPLICIT_ACCESS ea[2];
3250 SECURITY_DESCRIPTOR ovpn_sd;
3251 SECURITY_ATTRIBUTES ovpn_sa = { .nLength = sizeof(ovpn_sa),
3252 .lpSecurityDescriptor = &ovpn_sd,
3253 .bInheritHandle = FALSE };
3254
3255 ZeroMemory(&ea, sizeof(ea));
3256 ZeroMemory(&startup_info, sizeof(startup_info));
3257 ZeroMemory(&undo_lists, sizeof(undo_lists));
3258 ZeroMemory(&proc_info, sizeof(proc_info));
3259
3260 if (!GetStartupData(pipe, &sud))
3261 {
3262 goto out;
3263 }
3264
3265 if (!InitializeSecurityDescriptor(&ovpn_sd, SECURITY_DESCRIPTOR_REVISION))
3266 {
3267 ReturnLastError(pipe, L"InitializeSecurityDescriptor");
3268 goto out;
3269 }
3270
3271 /* Get SID of user the service is running under */
3272 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &svc_token))
3273 {
3274 ReturnLastError(pipe, L"OpenProcessToken");
3275 goto out;
3276 }
3277 len = 0;
3278 while (!GetTokenInformation(svc_token, TokenUser, svc_user, len, &len))
3279 {
3280 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
3281 {
3282 ReturnLastError(pipe, L"GetTokenInformation (service token)");
3283 goto out;
3284 }
3285 free(svc_user);
3286 svc_user = malloc(len);
3287 if (svc_user == NULL)
3288 {
3289 ReturnLastError(pipe, L"malloc (service token user)");
3290 goto out;
3291 }
3292 }
3293 if (!IsValidSid(svc_user->User.Sid))
3294 {
3295 ReturnLastError(pipe, L"IsValidSid (service token user)");
3296 goto out;
3297 }
3298
3299 if (!ImpersonateNamedPipeClient(pipe))
3300 {
3301 ReturnLastError(pipe, L"ImpersonateNamedPipeClient");
3302 goto out;
3303 }
3304 if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &imp_token))
3305 {
3306 ReturnLastError(pipe, L"OpenThreadToken");
3307 goto out;
3308 }
3309 len = 0;
3310 while (!GetTokenInformation(imp_token, TokenUser, ovpn_user, len, &len))
3311 {
3312 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
3313 {
3314 ReturnLastError(pipe, L"GetTokenInformation (impersonation token)");
3315 goto out;
3316 }
3317 free(ovpn_user);
3318 ovpn_user = malloc(len);
3319 if (ovpn_user == NULL)
3320 {
3321 ReturnLastError(pipe, L"malloc (impersonation token user)");
3322 goto out;
3323 }
3324 }
3325 if (!IsValidSid(ovpn_user->User.Sid))
3326 {
3327 ReturnLastError(pipe, L"IsValidSid (impersonation token user)");
3328 goto out;
3329 }
3330
3331 /*
3332 * Only authorized users are allowed to use any command line options or
3333 * have the config file in locations other than the global config directory.
3334 *
3335 * Check options are white-listed and config is in the global directory
3336 * OR user is authorized to run any config.
3337 */
3338 if (!ValidateOptions(pipe, sud.directory, sud.options, errmsg, _countof(errmsg))
3339 && !IsAuthorizedUser(ovpn_user->User.Sid, imp_token, settings.ovpn_admin_group,
3341 {
3342 ReturnError(pipe, ERROR_STARTUP_DATA, errmsg, 1, &exit_event);
3343 goto out;
3344 }
3345
3346 /* OpenVPN process DACL entry for access by service and user */
3347 ea[0].grfAccessPermissions = SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL;
3348 ea[0].grfAccessMode = SET_ACCESS;
3349 ea[0].grfInheritance = NO_INHERITANCE;
3350 ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
3351 ea[0].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
3352 ea[0].Trustee.ptstrName = (LPWSTR)svc_user->User.Sid;
3353 ea[1].grfAccessPermissions = READ_CONTROL | SYNCHRONIZE | PROCESS_VM_READ | SYNCHRONIZE
3354 | PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION;
3355 ea[1].grfAccessMode = SET_ACCESS;
3356 ea[1].grfInheritance = NO_INHERITANCE;
3357 ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
3358 ea[1].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
3359 ea[1].Trustee.ptstrName = (LPWSTR)ovpn_user->User.Sid;
3360
3361 /* Set owner and DACL of OpenVPN security descriptor */
3362 if (!SetSecurityDescriptorOwner(&ovpn_sd, svc_user->User.Sid, FALSE))
3363 {
3364 ReturnLastError(pipe, L"SetSecurityDescriptorOwner");
3365 goto out;
3366 }
3367 if (SetEntriesInAcl(2, ea, NULL, &ovpn_dacl) != ERROR_SUCCESS)
3368 {
3369 ReturnLastError(pipe, L"SetEntriesInAcl");
3370 goto out;
3371 }
3372 if (!SetSecurityDescriptorDacl(&ovpn_sd, TRUE, ovpn_dacl, FALSE))
3373 {
3374 ReturnLastError(pipe, L"SetSecurityDescriptorDacl");
3375 goto out;
3376 }
3377
3378 /* Create primary token from impersonation token */
3379 if (!DuplicateTokenEx(imp_token, TOKEN_ALL_ACCESS, NULL, 0, TokenPrimary, &pri_token))
3380 {
3381 ReturnLastError(pipe, L"DuplicateTokenEx");
3382 goto out;
3383 }
3384
3385 /* use /dev/null for stdout of openvpn (client should use --log for output) */
3386 stdout_write = CreateFile(_L("NUL"), GENERIC_WRITE, FILE_SHARE_WRITE, &inheritable,
3387 OPEN_EXISTING, 0, NULL);
3388 if (stdout_write == INVALID_HANDLE_VALUE)
3389 {
3390 ReturnLastError(pipe, L"CreateFile for stdout");
3391 goto out;
3392 }
3393
3394 if (!CreatePipe(&stdin_read, &stdin_write, &inheritable, 0)
3395 || !SetHandleInformation(stdin_write, HANDLE_FLAG_INHERIT, 0))
3396 {
3397 ReturnLastError(pipe, L"CreatePipe");
3398 goto out;
3399 }
3400
3401 swprintf(ovpn_pipe_name, _countof(ovpn_pipe_name),
3402 L"\\\\.\\pipe\\" _L(PACKAGE) L"%ls\\service_%lu", service_instance,
3403 GetCurrentThreadId());
3404 ovpn_pipe = CreateNamedPipe(
3405 ovpn_pipe_name, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED,
3406 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 1, 128, 128, 0, NULL);
3407 if (ovpn_pipe == INVALID_HANDLE_VALUE)
3408 {
3409 ReturnLastError(pipe, L"CreateNamedPipe");
3410 goto out;
3411 }
3412
3413 svc_pipe = CreateFile(ovpn_pipe_name, GENERIC_READ | GENERIC_WRITE, 0, &inheritable,
3414 OPEN_EXISTING, 0, NULL);
3415 if (svc_pipe == INVALID_HANDLE_VALUE)
3416 {
3417 ReturnLastError(pipe, L"CreateFile");
3418 goto out;
3419 }
3420
3421 pipe_mode = PIPE_READMODE_MESSAGE;
3422 if (!SetNamedPipeHandleState(svc_pipe, &pipe_mode, NULL, NULL))
3423 {
3424 ReturnLastError(pipe, L"SetNamedPipeHandleState");
3425 goto out;
3426 }
3427
3428 cmdline_size = wcslen(sud.options) + 128;
3429 cmdline = malloc(cmdline_size * sizeof(*cmdline));
3430 if (cmdline == NULL)
3431 {
3432 ReturnLastError(pipe, L"malloc");
3433 goto out;
3434 }
3435 /* there seem to be no common printf specifier that works on all
3436 * mingw/msvc platforms without trickery, so convert to void* and use
3437 * PRIuPTR to print that as best compromise */
3438 swprintf(cmdline, cmdline_size, L"openvpn %ls --msg-channel %" PRIuPTR, sud.options,
3439 (uintptr_t)svc_pipe);
3440
3441 if (!CreateEnvironmentBlock(&user_env, imp_token, FALSE))
3442 {
3443 ReturnLastError(pipe, L"CreateEnvironmentBlock");
3444 goto out;
3445 }
3446
3447 startup_info.cb = sizeof(startup_info);
3448 startup_info.dwFlags = STARTF_USESTDHANDLES;
3449 startup_info.hStdInput = stdin_read;
3450 startup_info.hStdOutput = stdout_write;
3451 startup_info.hStdError = stdout_write;
3452
3453 exe_path = settings.exe_path;
3454
3455 /* TODO: make sure HKCU is correct or call LoadUserProfile() */
3456 if (!CreateProcessAsUserW(pri_token, exe_path, cmdline, &ovpn_sa, NULL, TRUE,
3457 settings.priority | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
3458 user_env, sud.directory, &startup_info, &proc_info))
3459 {
3460 ReturnLastError(pipe, L"CreateProcessAsUser");
3461 goto out;
3462 }
3463
3464 if (!RevertToSelf())
3465 {
3466 TerminateProcess(proc_info.hProcess, 1);
3467 ReturnLastError(pipe, L"RevertToSelf");
3468 goto out;
3469 }
3470
3471 ReturnProcessId(pipe, proc_info.dwProcessId, 1, &exit_event);
3472
3473 CloseHandleEx(&stdout_write);
3474 CloseHandleEx(&stdin_read);
3475 CloseHandleEx(&svc_pipe);
3476
3477 DWORD input_size = WideCharToMultiByte(CP_UTF8, 0, sud.std_input, -1, NULL, 0, NULL, NULL);
3478 LPSTR input = NULL;
3479 if (input_size && (input = malloc(input_size)))
3480 {
3481 DWORD written;
3482 WideCharToMultiByte(CP_UTF8, 0, sud.std_input, -1, input, input_size, NULL, NULL);
3483 WriteFile(stdin_write, input, (DWORD)strlen(input), &written, NULL);
3484 free(input);
3485 }
3486
3487 while (TRUE)
3488 {
3489 DWORD bytes = PeekNamedPipeAsync(ovpn_pipe, 1, &exit_event);
3490 if (bytes == 0)
3491 {
3492 break;
3493 }
3494
3495 if (bytes > sizeof(pipe_message_t))
3496 {
3497 /* process at the other side of the pipe is misbehaving, shut it down */
3500 L"OpenVPN process sent too large payload length to the pipe (%lu bytes), it will be terminated",
3501 bytes);
3502 break;
3503 }
3504
3505 HandleMessage(ovpn_pipe, &proc_info, bytes, 1, &exit_event, &undo_lists);
3506 }
3507
3508 WaitForSingleObject(proc_info.hProcess, IO_TIMEOUT);
3509 GetExitCodeProcess(proc_info.hProcess, &exit_code);
3510 if (exit_code == STILL_ACTIVE)
3511 {
3512 TerminateProcess(proc_info.hProcess, 1);
3513 }
3514 else if (exit_code != 0)
3515 {
3516 WCHAR buf[256];
3517 swprintf(buf, _countof(buf), L"OpenVPN exited with error: exit code = %lu", exit_code);
3519 }
3520 Undo(&undo_lists);
3521
3522out:
3523 FlushFileBuffers(pipe);
3524 DisconnectNamedPipe(pipe);
3525
3526 free(ovpn_user);
3527 free(svc_user);
3528 free(cmdline);
3529 DestroyEnvironmentBlock(user_env);
3530 FreeStartupData(&sud);
3531 CloseHandleEx(&proc_info.hProcess);
3532 CloseHandleEx(&proc_info.hThread);
3533 CloseHandleEx(&stdin_read);
3534 CloseHandleEx(&stdin_write);
3535 CloseHandleEx(&stdout_write);
3536 CloseHandleEx(&svc_token);
3537 CloseHandleEx(&imp_token);
3538 CloseHandleEx(&pri_token);
3539 CloseHandleEx(&ovpn_pipe);
3540 CloseHandleEx(&svc_pipe);
3541 CloseHandleEx(&pipe);
3542
3543 return 0;
3544}
3545
3546
3547static DWORD WINAPI
3548ServiceCtrlInteractive(DWORD ctrl_code, DWORD event, LPVOID data, LPVOID ctx)
3549{
3550 SERVICE_STATUS *status = ctx;
3551 switch (ctrl_code)
3552 {
3553 case SERVICE_CONTROL_STOP:
3554 status->dwCurrentState = SERVICE_STOP_PENDING;
3556 if (exit_event)
3557 {
3558 SetEvent(exit_event);
3559 }
3560 return NO_ERROR;
3561
3562 case SERVICE_CONTROL_INTERROGATE:
3563 return NO_ERROR;
3564
3565 default:
3566 return ERROR_CALL_NOT_IMPLEMENTED;
3567 }
3568}
3569
3570
3571static HANDLE
3573{
3574 /*
3575 * allow all access for local system
3576 * deny FILE_CREATE_PIPE_INSTANCE for everyone
3577 * allow read/write for authenticated users
3578 * deny all access to anonymous
3579 */
3580 const WCHAR *sddlString =
3581 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)";
3582
3583 PSECURITY_DESCRIPTOR sd = NULL;
3584 if (!ConvertStringSecurityDescriptorToSecurityDescriptor(sddlString, SDDL_REVISION_1, &sd,
3585 NULL))
3586 {
3587 MsgToEventLog(M_SYSERR, L"ConvertStringSecurityDescriptorToSecurityDescriptor failed.");
3588 return INVALID_HANDLE_VALUE;
3589 }
3590
3591 /* Set up SECURITY_ATTRIBUTES */
3592 SECURITY_ATTRIBUTES sa = { 0 };
3593 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
3594 sa.lpSecurityDescriptor = sd;
3595 sa.bInheritHandle = FALSE;
3596
3597 DWORD flags = PIPE_ACCESS_DUPLEX | WRITE_DAC | FILE_FLAG_OVERLAPPED;
3598
3599 static BOOL first = TRUE;
3600 if (first)
3601 {
3602 flags |= FILE_FLAG_FIRST_PIPE_INSTANCE;
3603 first = FALSE;
3604 }
3605
3606 WCHAR pipe_name[256]; /* The entire pipe name string can be up to 256 characters long according
3607 to MSDN. */
3608 swprintf(pipe_name, _countof(pipe_name), L"\\\\.\\pipe\\" _L(PACKAGE) L"%ls\\service",
3610 HANDLE pipe = CreateNamedPipe(
3611 pipe_name, flags, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
3612 PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0, &sa);
3613
3614 LocalFree(sd);
3615
3616 if (pipe == INVALID_HANDLE_VALUE)
3617 {
3618 MsgToEventLog(M_SYSERR, L"Could not create named pipe");
3619 return INVALID_HANDLE_VALUE;
3620 }
3621
3622 return pipe;
3623}
3624
3625
3626static DWORD
3627UpdateWaitHandles(LPHANDLE *handles_ptr, LPDWORD count, HANDLE io_event, HANDLE exit_event,
3628 list_item_t *threads)
3629{
3630 static DWORD size = 10;
3631 static LPHANDLE handles = NULL;
3632 DWORD pos = 0;
3633
3634 if (handles == NULL)
3635 {
3636 handles = malloc(size * sizeof(HANDLE));
3637 *handles_ptr = handles;
3638 if (handles == NULL)
3639 {
3640 return ERROR_OUTOFMEMORY;
3641 }
3642 }
3643
3644 handles[pos++] = io_event;
3645
3646 if (!threads)
3647 {
3648 handles[pos++] = exit_event;
3649 }
3650
3651 while (threads)
3652 {
3653 if (pos == size)
3654 {
3655 LPHANDLE tmp;
3656 size += 10;
3657 tmp = realloc(handles, size * sizeof(HANDLE));
3658 if (tmp == NULL)
3659 {
3660 size -= 10;
3661 *count = pos;
3662 return ERROR_OUTOFMEMORY;
3663 }
3664 handles = tmp;
3665 *handles_ptr = handles;
3666 }
3667 handles[pos++] = threads->data;
3668 threads = threads->next;
3669 }
3670
3671 *count = pos;
3672 return NO_ERROR;
3673}
3674
3675
3676static VOID
3678{
3679 free(h);
3680}
3681
3682static BOOL
3683CmpHandle(LPVOID item, LPVOID hnd)
3684{
3685 return item == hnd;
3686}
3687
3688
3689VOID WINAPI
3690ServiceStartInteractiveOwn(DWORD dwArgc, LPWSTR *lpszArgv)
3691{
3692 status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
3693 ServiceStartInteractive(dwArgc, lpszArgv);
3694}
3695
3701static void
3703{
3704 BOOL changed = FALSE;
3705
3706 /* Clean up leftover NRPT rules */
3707 BOOL gpol_nrpt;
3708 changed = DeleteNrptRules(0, &gpol_nrpt);
3709
3710 /* Clean up leftover DNS search list fragments */
3711 HKEY key;
3712 BOOL gpol_list;
3713 GetDnsSearchListKey(NULL, &gpol_list, &key);
3714 if (key != INVALID_HANDLE_VALUE)
3715 {
3717 {
3718 changed = TRUE;
3719 }
3720 RegCloseKey(key);
3721 }
3722
3723 if (changed)
3724 {
3725 ApplyDnsSettings(gpol_nrpt || gpol_list);
3726 }
3727}
3728
3729VOID WINAPI
3730ServiceStartInteractive(DWORD dwArgc, LPWSTR *lpszArgv)
3731{
3732 HANDLE pipe, io_event = NULL;
3733 OVERLAPPED overlapped;
3734 DWORD error = NO_ERROR;
3735 list_item_t *threads = NULL;
3736 PHANDLE handles = NULL;
3737 DWORD handle_count;
3738
3739 service =
3740 RegisterServiceCtrlHandlerEx(interactive_service.name, ServiceCtrlInteractive, &status);
3741 if (!service)
3742 {
3743 return;
3744 }
3745
3746 status.dwCurrentState = SERVICE_START_PENDING;
3747 status.dwServiceSpecificExitCode = NO_ERROR;
3748 status.dwWin32ExitCode = NO_ERROR;
3749 status.dwWaitHint = 3000;
3751
3752 /* Clean up potentially left over registry values */
3754
3755 /* Read info from registry in key HKLM\SOFTWARE\OpenVPN */
3756 error = GetOpenvpnSettings(&settings);
3757 if (error != ERROR_SUCCESS)
3758 {
3759 goto out;
3760 }
3761
3762 io_event = InitOverlapped(&overlapped);
3763 exit_event = CreateEvent(NULL, TRUE, FALSE, NULL);
3764 if (!exit_event || !io_event)
3765 {
3766 error = MsgToEventLog(M_SYSERR, L"Could not create event");
3767 goto out;
3768 }
3769
3770 rdns_semaphore = CreateSemaphoreW(NULL, 1, 1, NULL);
3771 if (!rdns_semaphore)
3772 {
3773 error = MsgToEventLog(M_SYSERR, L"Could not create semaphore for register-dns");
3774 goto out;
3775 }
3776
3777 error = UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
3778 if (error != NO_ERROR)
3779 {
3780 goto out;
3781 }
3782
3783 pipe = CreateClientPipeInstance();
3784 if (pipe == INVALID_HANDLE_VALUE)
3785 {
3786 goto out;
3787 }
3788
3789 status.dwCurrentState = SERVICE_RUNNING;
3790 status.dwWaitHint = 0;
3792
3793 while (TRUE)
3794 {
3795 if (ConnectNamedPipe(pipe, &overlapped) == FALSE && GetLastError() != ERROR_PIPE_CONNECTED
3796 && GetLastError() != ERROR_IO_PENDING)
3797 {
3798 MsgToEventLog(M_SYSERR, L"Could not connect pipe");
3799 break;
3800 }
3801
3802 error = WaitForMultipleObjects(handle_count, handles, FALSE, INFINITE);
3803 if (error == WAIT_OBJECT_0)
3804 {
3805 /* Client connected, spawn a worker thread for it */
3806 HANDLE next_pipe = CreateClientPipeInstance();
3807 HANDLE thread = CreateThread(NULL, 0, RunOpenvpn, pipe, CREATE_SUSPENDED, NULL);
3808 if (thread)
3809 {
3810 error = AddListItem(&threads, thread);
3811 if (!error)
3812 {
3813 error =
3814 UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
3815 }
3816 if (error)
3817 {
3818 ReturnError(pipe, error, L"Insufficient resources to service new clients", 1,
3819 &exit_event);
3820 /* Update wait handles again after removing the last worker thread */
3821 RemoveListItem(&threads, CmpHandle, thread);
3822 UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
3823 TerminateThread(thread, 1);
3824 CloseHandleEx(&thread);
3825 CloseHandleEx(&pipe);
3826 }
3827 else
3828 {
3829 ResumeThread(thread);
3830 }
3831 }
3832 else
3833 {
3834 CloseHandleEx(&pipe);
3835 }
3836
3837 ResetOverlapped(&overlapped);
3838 pipe = next_pipe;
3839 }
3840 else
3841 {
3842 CancelIo(pipe);
3843 if (error == WAIT_FAILED)
3844 {
3845 MsgToEventLog(M_SYSERR, L"WaitForMultipleObjects failed");
3846 SetEvent(exit_event);
3847 /* Give some time for worker threads to exit and then terminate */
3848 Sleep(1000);
3849 break;
3850 }
3851 if (!threads)
3852 {
3853 /* exit event signaled */
3854 CloseHandleEx(&pipe);
3855 ResetEvent(exit_event);
3856 error = NO_ERROR;
3857 break;
3858 }
3859
3860 /* Worker thread ended */
3861 HANDLE thread = RemoveListItem(&threads, CmpHandle, handles[error]);
3862 UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
3863 CloseHandleEx(&thread);
3864 }
3865 }
3866
3867out:
3868 FreeWaitHandles(handles);
3869 CloseHandleEx(&io_event);
3872
3873 status.dwCurrentState = SERVICE_STOPPED;
3874 status.dwWin32ExitCode = error;
3876}
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:276
DWORD MsgToEventLog(DWORD flags, LPCWSTR format,...)
Definition common.c:235
LPCWSTR service_instance
Definition common.c:27
DWORD GetOpenvpnSettings(settings_t *s)
Definition common.c:58
#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 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 DWORD DeleteWfpBlock(const wfp_block_message_t *msg, undo_lists_t *lists)
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:155
BOOL CheckOption(const WCHAR *workdir, int argc, WCHAR *argv[], const settings_t *s)
Definition validate.c:333
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