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