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
61 _L(PACKAGE_NAME) L" Interactive Service",
62 SERVICE_DEPENDENCIES, SERVICE_AUTO_START };
63
64
65typedef struct
66{
67 WCHAR *directory;
68 WCHAR *options;
69 WCHAR *std_input;
71
72
73/* Datatype for linked lists */
74typedef struct _list_item
75{
77 LPVOID data;
79
80
81/* Datatypes for undo information */
95
96typedef struct
97{
98 HANDLE engine;
99 int index;
103
104typedef struct
105{
106 char itf_name[256];
107 PWSTR domains;
109
124
125typedef struct
126{
127 CHAR addresses[NRPT_ADDR_NUM * NRPT_ADDR_SIZE];
128 WCHAR domains[512]; /* MULTI_SZ string */
129 DWORD domains_size; /* bytes in domains */
131
132
133static DWORD
134AddListItem(list_item_t **pfirst, LPVOID data)
135{
136 list_item_t *new_item = malloc(sizeof(list_item_t));
137 if (new_item == NULL)
138 {
139 return ERROR_OUTOFMEMORY;
140 }
141
142 new_item->next = *pfirst;
143 new_item->data = data;
144
145 *pfirst = new_item;
146 return NO_ERROR;
147}
148
149typedef BOOL (*match_fn_t)(LPVOID item, LPVOID ctx);
150
151static LPVOID
152RemoveListItem(list_item_t **pfirst, match_fn_t match, LPVOID ctx)
153{
154 LPVOID data = NULL;
155 list_item_t **pnext;
156
157 for (pnext = pfirst; *pnext; pnext = &(*pnext)->next)
158 {
159 list_item_t *item = *pnext;
160 if (!match(item->data, ctx))
161 {
162 continue;
163 }
164
165 /* Found item, remove from the list and free memory */
166 *pnext = item->next;
167 data = item->data;
168 free(item);
169 break;
170 }
171 return data;
172}
173
174
175static HANDLE
176CloseHandleEx(LPHANDLE handle)
177{
178 if (handle && *handle && *handle != INVALID_HANDLE_VALUE)
179 {
180 CloseHandle(*handle);
181 *handle = INVALID_HANDLE_VALUE;
182 }
183 return INVALID_HANDLE_VALUE;
184}
185
186static HANDLE
187InitOverlapped(LPOVERLAPPED overlapped)
188{
189 ZeroMemory(overlapped, sizeof(OVERLAPPED));
190 overlapped->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
191 return overlapped->hEvent;
192}
193
194static BOOL
195ResetOverlapped(LPOVERLAPPED overlapped)
196{
197 HANDLE io_event = overlapped->hEvent;
198 if (!ResetEvent(io_event))
199 {
200 return FALSE;
201 }
202 ZeroMemory(overlapped, sizeof(OVERLAPPED));
203 overlapped->hEvent = io_event;
204 return TRUE;
205}
206
207
208typedef enum
209{
212 write
214
215static DWORD
216AsyncPipeOp(async_op_t op, HANDLE pipe, LPVOID buffer, DWORD size, DWORD count, LPHANDLE events)
217{
218 DWORD i;
219 BOOL success;
220 HANDLE io_event;
221 DWORD res, bytes = 0;
222 OVERLAPPED overlapped;
223 LPHANDLE handles = NULL;
224
225 io_event = InitOverlapped(&overlapped);
226 if (!io_event)
227 {
228 goto out;
229 }
230
231 handles = malloc((count + 1) * sizeof(HANDLE));
232 if (!handles)
233 {
234 goto out;
235 }
236
237 if (op == write)
238 {
239 success = WriteFile(pipe, buffer, size, NULL, &overlapped);
240 }
241 else
242 {
243 success = ReadFile(pipe, buffer, size, NULL, &overlapped);
244 }
245 if (!success && GetLastError() != ERROR_IO_PENDING && GetLastError() != ERROR_MORE_DATA)
246 {
247 goto out;
248 }
249
250 handles[0] = io_event;
251 for (i = 0; i < count; i++)
252 {
253 handles[i + 1] = events[i];
254 }
255
256 res = WaitForMultipleObjects(count + 1, handles, FALSE, op == peek ? INFINITE : IO_TIMEOUT);
257 if (res != WAIT_OBJECT_0)
258 {
259 CancelIo(pipe);
260 goto out;
261 }
262
263 if (op == peek)
264 {
265 PeekNamedPipe(pipe, NULL, 0, NULL, &bytes, NULL);
266 }
267 else
268 {
269 GetOverlappedResult(pipe, &overlapped, &bytes, TRUE);
270 }
271
272out:
273 CloseHandleEx(&io_event);
274 free(handles);
275 return bytes;
276}
277
278static DWORD
279PeekNamedPipeAsync(HANDLE pipe, DWORD count, LPHANDLE events)
280{
281 return AsyncPipeOp(peek, pipe, NULL, 0, count, events);
282}
283
284static DWORD
285ReadPipeAsync(HANDLE pipe, LPVOID buffer, DWORD size, DWORD count, LPHANDLE events)
286{
287 return AsyncPipeOp(read, pipe, buffer, size, count, events);
288}
289
290static DWORD
291WritePipeAsync(HANDLE pipe, LPVOID data, DWORD size, DWORD count, LPHANDLE events)
292{
293 return AsyncPipeOp(write, pipe, data, size, count, events);
294}
295
296static VOID
297ReturnProcessId(HANDLE pipe, DWORD pid, DWORD count, LPHANDLE events)
298{
299 const WCHAR msg[] = L"Process ID";
300 WCHAR buf[22 + _countof(msg)]; /* 10 chars each for error and PID and 2 for line breaks */
301
302 /*
303 * Same format as error messages (3 line string) with error = 0 in
304 * 0x%08x format, PID on line 2 and a description "Process ID" on line 3
305 */
306 swprintf(buf, _countof(buf), L"0x%08x\n0x%08x\n%ls", 0, pid, msg);
307
308 WritePipeAsync(pipe, buf, (DWORD)(wcslen(buf) * 2), count, events);
309}
310
311static VOID
312ReturnError(HANDLE pipe, DWORD error, LPCWSTR func, DWORD count, LPHANDLE events)
313{
314 DWORD result_len;
315 LPWSTR result = L"0xffffffff\nFormatMessage failed\nCould not return result";
316 DWORD_PTR args[] = { (DWORD_PTR)error, (DWORD_PTR)func, (DWORD_PTR) "" };
317
318 if (error != ERROR_OPENVPN_STARTUP)
319 {
320 FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER
321 | FORMAT_MESSAGE_IGNORE_INSERTS,
322 0, error, 0, (LPWSTR)&args[2], 0, NULL);
323 }
324
325 result_len = FormatMessageW(
326 FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY,
327 L"0x%1!08x!\n%2!s!\n%3!s!", 0, 0, (LPWSTR)&result, 0, (va_list *)args);
328
329 WritePipeAsync(pipe, result, (DWORD)(wcslen(result) * 2), count, events);
331
332 if (error != ERROR_OPENVPN_STARTUP)
333 {
334 LocalFree((LPVOID)args[2]);
335 }
336 if (result_len)
337 {
338 LocalFree(result);
339 }
340}
341
342
343static VOID
344ReturnLastError(HANDLE pipe, LPCWSTR func)
345{
346 ReturnError(pipe, GetLastError(), func, 1, &exit_event);
347}
348
349/*
350 * Validate options against a white list. Also check the config_file is
351 * inside the config_dir. The white list is defined in validate.c
352 * Returns true on success, false on error with reason set in errmsg.
353 */
354static BOOL
355ValidateOptions(HANDLE pipe, const WCHAR *workdir, const WCHAR *options, WCHAR *errmsg,
356 DWORD capacity)
357{
358 WCHAR **argv;
359 int argc;
360 BOOL ret = FALSE;
361 int i;
362 const WCHAR *msg1 = L"You have specified a config file location (%ls relative to %ls)"
363 L" that requires admin approval. This error may be avoided"
364 L" by adding your account to the \"%ls\" group";
365
366 const WCHAR *msg2 = L"You have specified an option (%ls) that may be used"
367 L" only with admin approval. This error may be avoided"
368 L" by adding your account to the \"%ls\" group";
369
370 argv = CommandLineToArgvW(options, &argc);
371
372 if (!argv)
373 {
374 swprintf(errmsg, capacity,
375 L"Cannot validate options: CommandLineToArgvW failed with error = 0x%08x",
376 GetLastError());
377 goto out;
378 }
379
380 /* Note: argv[0] is the first option */
381 if (argc < 1) /* no options */
382 {
383 ret = TRUE;
384 goto out;
385 }
386
387 /*
388 * If only one argument, it is the config file
389 */
390 if (argc == 1)
391 {
392 WCHAR *argv_tmp[2] = { L"--config", argv[0] };
393
394 if (!CheckOption(workdir, 2, argv_tmp, &settings))
395 {
396 swprintf(errmsg, capacity, msg1, argv[0], workdir, settings.ovpn_admin_group);
397 }
398 goto out;
399 }
400
401 for (i = 0; i < argc; ++i)
402 {
403 if (!IsOption(argv[i]))
404 {
405 continue;
406 }
407
408 if (!CheckOption(workdir, argc - i, &argv[i], &settings))
409 {
410 if (wcscmp(L"--config", argv[i]) == 0 && argc - i > 1)
411 {
412 swprintf(errmsg, capacity, msg1, argv[i + 1], workdir, settings.ovpn_admin_group);
413 }
414 else
415 {
416 swprintf(errmsg, capacity, msg2, argv[i], settings.ovpn_admin_group);
417 }
418 goto out;
419 }
420 }
421
422 /* all options passed */
423 ret = TRUE;
424
425out:
426 if (argv)
427 {
428 LocalFree(argv);
429 }
430 return ret;
431}
432
433static BOOL
434GetStartupData(HANDLE pipe, STARTUP_DATA *sud)
435{
436 size_t size, len;
437 WCHAR *data = NULL;
438 DWORD bytes, read;
439
440 bytes = PeekNamedPipeAsync(pipe, 1, &exit_event);
441 if (bytes == 0)
442 {
443 MsgToEventLog(M_SYSERR, L"PeekNamedPipeAsync failed");
444 ReturnLastError(pipe, L"PeekNamedPipeAsync");
445 goto err;
446 }
447
448 size = bytes / sizeof(*data);
449 if (size == 0)
450 {
451 MsgToEventLog(M_SYSERR, L"malformed startup data: 1 byte received");
452 ReturnError(pipe, ERROR_STARTUP_DATA, L"GetStartupData", 1, &exit_event);
453 goto err;
454 }
455
456 data = malloc(bytes);
457 if (data == NULL)
458 {
459 MsgToEventLog(M_SYSERR, L"malloc failed");
460 ReturnLastError(pipe, L"malloc");
461 goto err;
462 }
463
464 read = ReadPipeAsync(pipe, data, bytes, 1, &exit_event);
465 if (bytes != read)
466 {
467 MsgToEventLog(M_SYSERR, L"ReadPipeAsync failed");
468 ReturnLastError(pipe, L"ReadPipeAsync");
469 goto err;
470 }
471
472 if (data[size - 1] != 0)
473 {
474 MsgToEventLog(M_ERR, L"Startup data is not NULL terminated");
475 ReturnError(pipe, ERROR_STARTUP_DATA, L"GetStartupData", 1, &exit_event);
476 goto err;
477 }
478
479 sud->directory = data;
480 len = wcslen(sud->directory) + 1;
481 size -= len;
482 if (size <= 0)
483 {
484 MsgToEventLog(M_ERR, L"Startup data ends at working directory");
485 ReturnError(pipe, ERROR_STARTUP_DATA, L"GetStartupData", 1, &exit_event);
486 goto err;
487 }
488
489 sud->options = sud->directory + len;
490 len = wcslen(sud->options) + 1;
491 size -= len;
492 if (size <= 0)
493 {
494 MsgToEventLog(M_ERR, L"Startup data ends at command line options");
495 ReturnError(pipe, ERROR_STARTUP_DATA, L"GetStartupData", 1, &exit_event);
496 goto err;
497 }
498
499 sud->std_input = sud->options + len;
500 return TRUE;
501
502err:
503 sud->directory = NULL; /* caller must not free() */
504 free(data);
505 return FALSE;
506}
507
508
509static VOID
511{
512 free(sud->directory);
513}
514
515
516static SOCKADDR_INET
517sockaddr_inet(short family, inet_address_t *addr)
518{
519 SOCKADDR_INET sa_inet;
520 ZeroMemory(&sa_inet, sizeof(sa_inet));
521 sa_inet.si_family = family;
522 if (family == AF_INET)
523 {
524 sa_inet.Ipv4.sin_addr = addr->ipv4;
525 }
526 else if (family == AF_INET6)
527 {
528 sa_inet.Ipv6.sin6_addr = addr->ipv6;
529 }
530 return sa_inet;
531}
532
533static DWORD
534InterfaceLuid(const char *iface_name, PNET_LUID luid)
535{
536 NETIO_STATUS status;
537 LPWSTR wide_name = utf8to16(iface_name);
538
539 if (wide_name)
540 {
541 status = ConvertInterfaceAliasToLuid(wide_name, luid);
542 free(wide_name);
543 }
544 else
545 {
546 status = ERROR_OUTOFMEMORY;
547 }
548 return status;
549}
550
551static BOOL
552CmpAddress(LPVOID item, LPVOID address)
553{
554 return memcmp(item, address, sizeof(MIB_UNICASTIPADDRESS_ROW)) == 0 ? TRUE : FALSE;
555}
556
557static DWORD
558DeleteAddress(PMIB_UNICASTIPADDRESS_ROW addr_row)
559{
560 return DeleteUnicastIpAddressEntry(addr_row);
561}
562
563static DWORD
565{
566 DWORD err;
567 PMIB_UNICASTIPADDRESS_ROW addr_row;
568 BOOL add = msg->header.type == msg_add_address;
569
570 addr_row = malloc(sizeof(*addr_row));
571 if (addr_row == NULL)
572 {
573 return ERROR_OUTOFMEMORY;
574 }
575
576 InitializeUnicastIpAddressEntry(addr_row);
577 addr_row->Address = sockaddr_inet(msg->family, &msg->address);
578 addr_row->OnLinkPrefixLength = (UINT8)msg->prefix_len;
579
580 if (msg->iface.index != -1)
581 {
582 addr_row->InterfaceIndex = msg->iface.index;
583 }
584 else
585 {
586 NET_LUID luid;
587 err = InterfaceLuid(msg->iface.name, &luid);
588 if (err)
589 {
590 goto out;
591 }
592 addr_row->InterfaceLuid = luid;
593 }
594
595 if (add)
596 {
597 err = CreateUnicastIpAddressEntry(addr_row);
598 if (err)
599 {
600 goto out;
601 }
602
603 err = AddListItem(&(*lists)[address], addr_row);
604 if (err)
605 {
606 DeleteAddress(addr_row);
607 }
608 }
609 else
610 {
611 err = DeleteAddress(addr_row);
612 if (err)
613 {
614 goto out;
615 }
616
617 free(RemoveListItem(&(*lists)[address], CmpAddress, addr_row));
618 }
619
620out:
621 if (!add || err)
622 {
623 free(addr_row);
624 }
625
626 return err;
627}
628
629static BOOL
630CmpRoute(LPVOID item, LPVOID route)
631{
632 return memcmp(item, route, sizeof(MIB_IPFORWARD_ROW2)) == 0 ? TRUE : FALSE;
633}
634
635static DWORD
636DeleteRoute(PMIB_IPFORWARD_ROW2 fwd_row)
637{
638 return DeleteIpForwardEntry2(fwd_row);
639}
640
641static DWORD
643{
644 DWORD err;
645 PMIB_IPFORWARD_ROW2 fwd_row;
646 BOOL add = msg->header.type == msg_add_route;
647
648 fwd_row = malloc(sizeof(*fwd_row));
649 if (fwd_row == NULL)
650 {
651 return ERROR_OUTOFMEMORY;
652 }
653
654 ZeroMemory(fwd_row, sizeof(*fwd_row));
655 fwd_row->ValidLifetime = 0xffffffff;
656 fwd_row->PreferredLifetime = 0xffffffff;
657 fwd_row->Protocol = MIB_IPPROTO_NETMGMT;
658 fwd_row->Metric = msg->metric;
659 fwd_row->DestinationPrefix.Prefix = sockaddr_inet(msg->family, &msg->prefix);
660 fwd_row->DestinationPrefix.PrefixLength = (UINT8)msg->prefix_len;
661 fwd_row->NextHop = sockaddr_inet(msg->family, &msg->gateway);
662
663 if (msg->iface.index != -1)
664 {
665 fwd_row->InterfaceIndex = msg->iface.index;
666 }
667 else if (strlen(msg->iface.name))
668 {
669 NET_LUID luid;
670 err = InterfaceLuid(msg->iface.name, &luid);
671 if (err)
672 {
673 goto out;
674 }
675 fwd_row->InterfaceLuid = luid;
676 }
677
678 if (add)
679 {
680 err = CreateIpForwardEntry2(fwd_row);
681 if (err)
682 {
683 goto out;
684 }
685
686 err = AddListItem(&(*lists)[route], fwd_row);
687 if (err)
688 {
689 DeleteRoute(fwd_row);
690 }
691 }
692 else
693 {
694 err = DeleteRoute(fwd_row);
695 if (err)
696 {
697 goto out;
698 }
699
700 free(RemoveListItem(&(*lists)[route], CmpRoute, fwd_row));
701 }
702
703out:
704 if (!add || err)
705 {
706 free(fwd_row);
707 }
708
709 return err;
710}
711
712
713static DWORD
715{
716 if (msg->family == AF_INET)
717 {
718 return FlushIpNetTable(msg->iface.index);
719 }
720
721 return FlushIpNetTable2(msg->family, msg->iface.index);
722}
723
724static void
725BlockDNSErrHandler(DWORD err, const char *msg)
726{
727 WCHAR buf[256];
728 LPCWSTR err_str;
729
730 if (!err)
731 {
732 return;
733 }
734
735 err_str = L"Unknown Win32 Error";
736
737 if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM
738 | FORMAT_MESSAGE_ARGUMENT_ARRAY,
739 NULL, err, 0, buf, sizeof(buf), NULL))
740 {
741 err_str = buf;
742 }
743
744 MsgToEventLog(M_ERR, L"%hs (status = %lu): %ls", msg, err, err_str);
745}
746
747/* Use an always-true match_fn to get the head of the list */
748static BOOL
749CmpAny(LPVOID item, LPVOID any)
750{
751 return TRUE;
752}
753
754static DWORD
756{
757 DWORD err = 0;
758 wfp_block_data_t *block_data = RemoveListItem(&(*lists)[wfp_block], CmpAny, NULL);
759
760 if (block_data)
761 {
762 err = delete_wfp_block_filters(block_data->engine);
763 if (block_data->metric_v4 >= 0)
764 {
765 set_interface_metric(msg->iface.index, AF_INET, block_data->metric_v4);
766 }
767 if (block_data->metric_v6 >= 0)
768 {
769 set_interface_metric(msg->iface.index, AF_INET6, block_data->metric_v6);
770 }
771 free(block_data);
772 }
773 else
774 {
775 MsgToEventLog(M_ERR, L"No previous block filters to delete");
776 }
777
778 return err;
779}
780
781static DWORD
783{
784 DWORD err = 0;
785 wfp_block_data_t *block_data = NULL;
786 HANDLE engine = NULL;
787 LPCWSTR exe_path;
788 BOOL dns_only;
789
790 exe_path = settings.exe_path;
791 dns_only = (msg->flags == wfp_block_dns);
792
793 err = add_wfp_block_filters(&engine, msg->iface.index, exe_path, BlockDNSErrHandler, dns_only);
794 if (!err)
795 {
796 block_data = malloc(sizeof(wfp_block_data_t));
797 if (!block_data)
798 {
799 err = ERROR_OUTOFMEMORY;
800 goto out;
801 }
802 block_data->engine = engine;
803 block_data->index = msg->iface.index;
804 int is_auto = 0;
805 block_data->metric_v4 = get_interface_metric(msg->iface.index, AF_INET, &is_auto);
806 if (is_auto)
807 {
808 block_data->metric_v4 = 0;
809 }
810 block_data->metric_v6 = get_interface_metric(msg->iface.index, AF_INET6, &is_auto);
811 if (is_auto)
812 {
813 block_data->metric_v6 = 0;
814 }
815
816 err = AddListItem(&(*lists)[wfp_block], block_data);
817 if (!err)
818 {
819 err = set_interface_metric(msg->iface.index, AF_INET, WFP_BLOCK_IFACE_METRIC);
820 if (!err)
821 {
822 /* for IPv6, we intentionally ignore errors, because
823 * otherwise block-dns activation will fail if a user or
824 * admin has disabled IPv6 on the tun/tap/dco interface
825 * (if OpenVPN wants IPv6 ifconfig, we'll fail there)
826 */
827 set_interface_metric(msg->iface.index, AF_INET6, WFP_BLOCK_IFACE_METRIC);
828 }
829 if (err)
830 {
831 /* delete the filters, remove undo item and free interface data */
832 DeleteWfpBlock(msg, lists);
833 engine = NULL;
834 }
835 }
836 }
837
838out:
839 if (err && engine)
840 {
842 free(block_data);
843 }
844
845 return err;
846}
847
848static DWORD
850{
851 if (msg->header.type == msg_add_wfp_block)
852 {
853 return AddWfpBlock(msg, lists);
854 }
855 else
856 {
857 return DeleteWfpBlock(msg, lists);
858 }
859}
860
861/*
862 * Execute a command and return its exit code. If timeout > 0, terminate
863 * the process if still running after timeout milliseconds. In that case
864 * the return value is the windows error code WAIT_TIMEOUT = 0x102
865 */
866static DWORD
867ExecCommand(const WCHAR *argv0, const WCHAR *cmdline, DWORD timeout)
868{
869 DWORD exit_code;
870 STARTUPINFOW si;
871 PROCESS_INFORMATION pi;
872 DWORD proc_flags = CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT;
873 WCHAR *cmdline_dup = NULL;
874
875 ZeroMemory(&si, sizeof(si));
876 ZeroMemory(&pi, sizeof(pi));
877
878 si.cb = sizeof(si);
879
880 /* CreateProcess needs a modifiable cmdline: make a copy */
881 cmdline_dup = _wcsdup(cmdline);
882 if (cmdline_dup
883 && CreateProcessW(argv0, cmdline_dup, NULL, NULL, FALSE, proc_flags, NULL, NULL, &si, &pi))
884 {
885 WaitForSingleObject(pi.hProcess, timeout ? timeout : INFINITE);
886 if (!GetExitCodeProcess(pi.hProcess, &exit_code))
887 {
888 MsgToEventLog(M_SYSERR, L"ExecCommand: Error getting exit_code:");
889 exit_code = GetLastError();
890 }
891 else if (exit_code == STILL_ACTIVE)
892 {
893 exit_code = WAIT_TIMEOUT; /* Windows error code 0x102 */
894
895 /* kill without impunity */
896 TerminateProcess(pi.hProcess, exit_code);
897 MsgToEventLog(M_ERR, L"ExecCommand: \"%ls %ls\" killed after timeout", argv0, cmdline);
898 }
899 else if (exit_code)
900 {
901 MsgToEventLog(M_ERR, L"ExecCommand: \"%ls %ls\" exited with status = %lu", argv0,
902 cmdline, exit_code);
903 }
904 else
905 {
906 MsgToEventLog(M_INFO, L"ExecCommand: \"%ls %ls\" completed", argv0, cmdline);
907 }
908
909 CloseHandle(pi.hProcess);
910 CloseHandle(pi.hThread);
911 }
912 else
913 {
914 exit_code = GetLastError();
915 MsgToEventLog(M_SYSERR, L"ExecCommand: could not run \"%ls %ls\" :", argv0, cmdline);
916 }
917
918 free(cmdline_dup);
919 return exit_code;
920}
921
922/*
923 * Entry point for register-dns thread.
924 */
925static DWORD WINAPI
926RegisterDNS(LPVOID unused)
927{
928 DWORD err;
929 size_t i;
930 DWORD timeout = RDNS_TIMEOUT * 1000; /* in milliseconds */
931
932 /* path of ipconfig command */
933 WCHAR ipcfg[MAX_PATH];
934
935 struct
936 {
937 WCHAR *argv0;
938 WCHAR *cmdline;
939 DWORD timeout;
940 } cmds[] = {
941 { ipcfg, L"ipconfig /flushdns", timeout },
942 { ipcfg, L"ipconfig /registerdns", timeout },
943 };
944
945 HANDLE wait_handles[2] = { rdns_semaphore, exit_event };
946
947 swprintf(ipcfg, MAX_PATH, L"%ls\\%ls", get_win_sys_path(), L"ipconfig.exe");
948
949 if (WaitForMultipleObjects(2, wait_handles, FALSE, timeout) == WAIT_OBJECT_0)
950 {
951 /* Semaphore locked */
952 for (i = 0; i < _countof(cmds); ++i)
953 {
954 ExecCommand(cmds[i].argv0, cmds[i].cmdline, cmds[i].timeout);
955 }
956 err = 0;
957 if (!ReleaseSemaphore(rdns_semaphore, 1, NULL))
958 {
959 err =
960 MsgToEventLog(M_SYSERR, L"RegisterDNS: Failed to release regsiter-dns semaphore:");
961 }
962 }
963 else
964 {
965 MsgToEventLog(M_ERR, L"RegisterDNS: Failed to lock register-dns semaphore");
966 err = ERROR_SEM_TIMEOUT; /* Windows error code 0x79 */
967 }
968 return err;
969}
970
971static DWORD
973{
974 DWORD err;
975 HANDLE thread = NULL;
976
977 /* Delegate this job to a sub-thread */
978 thread = CreateThread(NULL, 0, RegisterDNS, NULL, 0, NULL);
979
980 /*
981 * We don't add these thread handles to the undo list -- the thread and
982 * processes it spawns are all supposed to terminate or timeout by themselves.
983 */
984 if (thread)
985 {
986 err = 0;
987 CloseHandle(thread);
988 }
989 else
990 {
991 err = GetLastError();
992 }
993
994 return err;
995}
996
1006static DWORD
1007netsh_wins_cmd(const wchar_t *action, const wchar_t *if_name, const wchar_t *addr)
1008{
1009 DWORD err = 0;
1010 int timeout = 30000; /* in msec */
1011 wchar_t argv0[MAX_PATH];
1012 wchar_t *cmdline = NULL;
1013 const wchar_t *addr_static = (wcscmp(action, L"set") == 0) ? L"static" : L"";
1014
1015 if (!addr)
1016 {
1017 if (wcscmp(action, L"delete") == 0)
1018 {
1019 addr = L"all";
1020 }
1021 else /* nothing to do -- return success*/
1022 {
1023 goto out;
1024 }
1025 }
1026
1027 /* Path of netsh */
1028 swprintf(argv0, _countof(argv0), L"%ls\\%ls", get_win_sys_path(), L"netsh.exe");
1029
1030 /* cmd template:
1031 * netsh interface ip $action wins $if_name $static $addr
1032 */
1033 const wchar_t *fmt = L"netsh interface ip %ls wins \"%ls\" %ls %ls";
1034
1035 /* max cmdline length in wchars -- include room for worst case and some */
1036 size_t ncmdline = wcslen(fmt) + wcslen(if_name) + wcslen(action) + wcslen(addr)
1037 + wcslen(addr_static) + 32 + 1;
1038 cmdline = malloc(ncmdline * sizeof(wchar_t));
1039 if (!cmdline)
1040 {
1041 err = ERROR_OUTOFMEMORY;
1042 goto out;
1043 }
1044
1045 swprintf(cmdline, ncmdline, fmt, action, if_name, addr_static, addr);
1046
1047 err = ExecCommand(argv0, cmdline, timeout);
1048
1049out:
1050 free(cmdline);
1051 return err;
1052}
1053
1054static BOOL
1055CmpWString(LPVOID item, LPVOID str)
1056{
1057 return (wcscmp(item, str) == 0) ? TRUE : FALSE;
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_dns4 : undo_dns6;
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 (TRUE)
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 == 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 one_glyph = 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 > one_glyph && 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 / one_glyph;
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 /* Ignore itf domains which match a pushed search domain */
2192 size_t domain_len = wcslen(pos);
2193 if (ListContainsDomain(search_domains, pos, domain_len))
2194 {
2195 if (comma)
2196 {
2197 pos = comma + 1;
2198 continue;
2199 }
2200 else
2201 {
2202 /* This was the last domain */
2203 *pos = '\0';
2204 *size += one_glyph;
2205 return wcslen(domains) ? NO_ERROR : ERROR_FILE_NOT_FOUND;
2206 }
2207 }
2208
2209 /* Check for enough space to convert this domain */
2210 domain_len += 1; /* leading dot */
2211 size_t converted_size = pos - domains;
2212 size_t domain_size = domain_len * one_glyph;
2213 size_t extra_size = 2 * one_glyph;
2214 if (converted_size + domain_size + extra_size > buf_size)
2215 {
2216 /* Domain doesn't fit, bad luck if it's the first one */
2217 *pos = '\0';
2218 *size = converted_size == 0 ? 0 : *size + 1;
2219 return ERROR_MORE_DATA;
2220 }
2221
2222 /* Prefix domain at pos with the dot */
2223 memmove(pos + 1, pos, buf_size - converted_size - one_glyph);
2224 domains[buf_len - 1] = '\0';
2225 *pos = '.';
2226 *size += one_glyph;
2227
2228 if (!comma)
2229 {
2230 /* Conversion is done */
2231 *(pos + domain_len) = '\0';
2232 *size += one_glyph;
2233 return NO_ERROR;
2234 }
2235
2236 pos = comma + 1;
2237 }
2238 }
2239 }
2240
2241 *size = 0;
2242 return err;
2243}
2244
2253static BOOL
2255{
2256 GUID iid;
2257 BOOL res = FALSE;
2258 MIB_IF_ROW2 itf_row;
2259
2260 /* Get GUID from string */
2261 if (IIDFromString(iid_str, &iid) != S_OK)
2262 {
2263 MsgToEventLog(M_SYSERR, L"%S: could not convert interface %s GUID string", __func__,
2264 iid_str);
2265 goto out;
2266 }
2267
2268 /* Get LUID from GUID */
2269 if (ConvertInterfaceGuidToLuid(&iid, &itf_row.InterfaceLuid) != NO_ERROR)
2270 {
2271 goto out;
2272 }
2273
2274 /* Look up interface status */
2275 if (GetIfEntry2(&itf_row) != NO_ERROR)
2276 {
2277 MsgToEventLog(M_SYSERR, L"%S: could not get interface %s status", __func__, iid_str);
2278 goto out;
2279 }
2280
2281 if (itf_row.MediaConnectState == MediaConnectStateConnected
2282 && itf_row.OperStatus == IfOperStatusUp)
2283 {
2284 res = TRUE;
2285 }
2286
2287out:
2288 return res;
2289}
2290
2300static void
2301GetNrptExcludeData(PCWSTR search_domains, nrpt_exclude_data_t *data, size_t data_size)
2302{
2303 HKEY v4_itfs = INVALID_HANDLE_VALUE;
2304 HKEY v6_itfs = INVALID_HANDLE_VALUE;
2305
2306 if (!GetInterfacesKey(AF_INET, &v4_itfs) || !GetInterfacesKey(AF_INET6, &v6_itfs))
2307 {
2308 goto out;
2309 }
2310
2311 size_t i = 0;
2312 DWORD enum_index = 0;
2313 while (i < data_size)
2314 {
2315 WCHAR itf_guid[MAX_PATH];
2316 DWORD itf_guid_len = _countof(itf_guid);
2317 LSTATUS err =
2318 RegEnumKeyExW(v4_itfs, enum_index++, itf_guid, &itf_guid_len, NULL, NULL, NULL, NULL);
2319 if (err)
2320 {
2321 if (err != ERROR_NO_MORE_ITEMS)
2322 {
2323 MsgToEventLog(M_SYSERR, L"%S: could not enumerate interfaces (%lu)", __func__, err);
2324 }
2325 goto out;
2326 }
2327
2328 /* Ignore interfaces that are not connected or disabled */
2329 if (!IsInterfaceConnected(itf_guid))
2330 {
2331 continue;
2332 }
2333
2334 HKEY v4_itf;
2335 if (RegOpenKeyExW(v4_itfs, itf_guid, 0, KEY_READ, &v4_itf) != NO_ERROR)
2336 {
2337 MsgToEventLog(M_SYSERR, L"%S: could not open interface %s v4 registry key", __func__,
2338 itf_guid);
2339 goto out;
2340 }
2341
2342 /* Get the DNS domain(s) for exclude routing */
2343 data[i].domains_size = sizeof(data[0].domains);
2344 memset(data[i].domains, 0, data[i].domains_size);
2345 err = GetItfDnsDomains(v4_itf, search_domains, data[i].domains, &data[i].domains_size);
2346 if (err)
2347 {
2348 if (err != ERROR_FILE_NOT_FOUND)
2349 {
2350 MsgToEventLog(M_SYSERR, L"%S: could not read interface %s domain suffix", __func__,
2351 itf_guid);
2352 }
2353 goto next_itf;
2354 }
2355
2356 /* Get the IPv4 DNS servers */
2357 DWORD v4_addrs_size = sizeof(data[0].addresses);
2358 err = GetItfDnsServersV4(v4_itf, data[i].addresses, &v4_addrs_size);
2359 if (err && err != ERROR_FILE_NOT_FOUND)
2360 {
2361 MsgToEventLog(M_SYSERR, L"%S: could not read interface %s v4 name servers (%ld)",
2362 __func__, itf_guid, err);
2363 goto next_itf;
2364 }
2365
2366 /* Get the IPv6 DNS servers, if there's space left */
2367 PSTR v6_addrs = data[i].addresses + v4_addrs_size;
2368 DWORD v6_addrs_size = sizeof(data[0].addresses) - v4_addrs_size;
2369 if (v6_addrs_size > NRPT_ADDR_SIZE)
2370 {
2371 HKEY v6_itf;
2372 if (RegOpenKeyExW(v6_itfs, itf_guid, 0, KEY_READ, &v6_itf) != NO_ERROR)
2373 {
2374 MsgToEventLog(M_SYSERR, L"%S: could not open interface %s v6 registry key",
2375 __func__, itf_guid);
2376 goto next_itf;
2377 }
2378 err = GetItfDnsServersV6(v6_itf, v6_addrs, &v6_addrs_size);
2379 RegCloseKey(v6_itf);
2380 if (err && err != ERROR_FILE_NOT_FOUND)
2381 {
2382 MsgToEventLog(M_SYSERR, L"%S: could not read interface %s v6 name servers (%ld)",
2383 __func__, itf_guid, err);
2384 goto next_itf;
2385 }
2386 }
2387
2388 if (v4_addrs_size || v6_addrs_size)
2389 {
2390 /* Replace delimiters with semicolons, as required by NRPT */
2391 for (int j = 0; j < sizeof(data[0].addresses) && data[i].addresses[j]; j++)
2392 {
2393 if (data[i].addresses[j] == ',' || data[i].addresses[j] == ' ')
2394 {
2395 data[i].addresses[j] = ';';
2396 }
2397 }
2398 ++i;
2399 }
2400
2401next_itf:
2402 RegCloseKey(v4_itf);
2403 }
2404
2405out:
2406 RegCloseKey(v6_itfs);
2407 RegCloseKey(v4_itfs);
2408}
2409
2422static DWORD
2423SetNrptRule(HKEY nrpt_key, PCWSTR subkey, PCSTR address, PCWSTR domains, DWORD dom_size,
2424 BOOL dnssec)
2425{
2426 /* Create rule subkey */
2427 DWORD err = NO_ERROR;
2428 HKEY rule_key;
2429 err = RegCreateKeyExW(nrpt_key, subkey, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &rule_key, NULL);
2430 if (err)
2431 {
2432 return err;
2433 }
2434
2435 /* Set name(s) for DNS routing */
2436 err = RegSetValueExW(rule_key, L"Name", 0, REG_MULTI_SZ, (PBYTE)domains, dom_size);
2437 if (err)
2438 {
2439 goto out;
2440 }
2441
2442 /* Set DNS Server address */
2443 err = RegSetValueExA(rule_key, "GenericDNSServers", 0, REG_SZ, (PBYTE)address,
2444 strlen(address) + 1);
2445 if (err)
2446 {
2447 goto out;
2448 }
2449
2450 DWORD reg_val;
2451 /* Set DNSSEC if required */
2452 if (dnssec)
2453 {
2454 reg_val = 1;
2455 err = RegSetValueExA(rule_key, "DNSSECValidationRequired", 0, REG_DWORD, (PBYTE)&reg_val,
2456 sizeof(reg_val));
2457 if (err)
2458 {
2459 goto out;
2460 }
2461
2462 reg_val = 0;
2463 err = RegSetValueExA(rule_key, "DNSSECQueryIPSECRequired", 0, REG_DWORD, (PBYTE)&reg_val,
2464 sizeof(reg_val));
2465 if (err)
2466 {
2467 goto out;
2468 }
2469
2470 reg_val = 0;
2471 err = RegSetValueExA(rule_key, "DNSSECQueryIPSECEncryption", 0, REG_DWORD, (PBYTE)&reg_val,
2472 sizeof(reg_val));
2473 if (err)
2474 {
2475 goto out;
2476 }
2477 }
2478
2479 /* Set NRPT config options */
2480 reg_val = dnssec ? 0x0000000A : 0x00000008;
2481 err = RegSetValueExA(rule_key, "ConfigOptions", 0, REG_DWORD, (const PBYTE)&reg_val,
2482 sizeof(reg_val));
2483 if (err)
2484 {
2485 goto out;
2486 }
2487
2488 /* Mandatory NRPT version */
2489 reg_val = 2;
2490 err = RegSetValueExA(rule_key, "Version", 0, REG_DWORD, (const PBYTE)&reg_val, sizeof(reg_val));
2491 if (err)
2492 {
2493 goto out;
2494 }
2495
2496out:
2497 if (err)
2498 {
2499 RegDeleteKeyW(nrpt_key, subkey);
2500 }
2501 RegCloseKey(rule_key);
2502 return err;
2503}
2504
2514static void
2515SetNrptExcludeRules(HKEY nrpt_key, DWORD ovpn_pid, PCWSTR search_domains)
2516{
2517 nrpt_exclude_data_t data[8]; /* data from up to 8 interfaces */
2518 memset(data, 0, sizeof(data));
2519 GetNrptExcludeData(search_domains, data, _countof(data));
2520
2521 unsigned n = 0;
2522 for (int i = 0; i < _countof(data); ++i)
2523 {
2524 nrpt_exclude_data_t *d = &data[i];
2525 if (d->domains_size == 0)
2526 {
2527 break;
2528 }
2529
2530 DWORD err;
2531 WCHAR subkey[48];
2532 swprintf(subkey, _countof(subkey), L"OpenVPNDNSRoutingX-%02x-%lu", ++n, ovpn_pid);
2533 err = SetNrptRule(nrpt_key, subkey, d->addresses, d->domains, d->domains_size, FALSE);
2534 if (err)
2535 {
2536 MsgToEventLog(M_ERR, L"%S: failed to set rule %s (%lu)", __func__, subkey, err);
2537 }
2538 }
2539}
2540
2553static DWORD
2554SetNrptRules(HKEY nrpt_key, const nrpt_address_t *addresses, const char *domains,
2555 const char *search_domains, BOOL dnssec, DWORD ovpn_pid)
2556{
2557 DWORD err = NO_ERROR;
2558 PWSTR wide_domains = L".\0"; /* DNS route everything by default */
2559 DWORD dom_size = 6;
2560
2561 /* Prepare DNS routing domains / split DNS */
2562 if (domains[0])
2563 {
2564 size_t domains_len = strlen(domains);
2565 dom_size = domains_len + 2; /* len + the trailing NULs */
2566
2567 wide_domains = utf8to16_size(domains, dom_size);
2568 dom_size *= sizeof(*wide_domains);
2569 if (!wide_domains)
2570 {
2571 return ERROR_OUTOFMEMORY;
2572 }
2573 /* Make a MULTI_SZ from a comma separated list */
2574 for (size_t i = 0; i < domains_len; ++i)
2575 {
2576 if (wide_domains[i] == ',')
2577 {
2578 wide_domains[i] = 0;
2579 }
2580 }
2581 }
2582 else
2583 {
2584 PWSTR wide_search_domains;
2585 wide_search_domains = utf8to16(search_domains);
2586 if (!wide_search_domains)
2587 {
2588 return ERROR_OUTOFMEMORY;
2589 }
2590 SetNrptExcludeRules(nrpt_key, ovpn_pid, wide_search_domains);
2591 free(wide_search_domains);
2592 }
2593
2594 /* Create address string list */
2595 CHAR addr_list[NRPT_ADDR_NUM * NRPT_ADDR_SIZE];
2596 PSTR pos = addr_list;
2597 for (int i = 0; i < NRPT_ADDR_NUM && addresses[i][0]; ++i)
2598 {
2599 if (i != 0)
2600 {
2601 *pos++ = ';';
2602 }
2603 strcpy(pos, addresses[i]);
2604 pos += strlen(pos);
2605 }
2606
2607 WCHAR subkey[MAX_PATH];
2608 swprintf(subkey, _countof(subkey), L"OpenVPNDNSRouting-%lu", ovpn_pid);
2609 err = SetNrptRule(nrpt_key, subkey, addr_list, wide_domains, dom_size, dnssec);
2610 if (err)
2611 {
2612 MsgToEventLog(M_ERR, L"%S: failed to set rule %s (%lu)", __func__, subkey, err);
2613 }
2614
2615 if (domains[0])
2616 {
2617 free(wide_domains);
2618 }
2619 return err;
2620}
2621
2622#if defined(__GNUC__) || defined(__clang__)
2623#pragma GCC diagnostic pop
2624#endif
2625
2634static LSTATUS
2635OpenNrptBaseKey(PHKEY key, PBOOL gpol)
2636{
2637 /*
2638 * Registry keys Name Service Policy Table (NRPT) rules can be stored at.
2639 * When the group policy key exists, NRPT rules must be placed there.
2640 * It is created when NRPT rules are pushed via group policy and it
2641 * remains in the registry even if the last GP-NRPT rule is deleted.
2642 */
2643 static PCSTR gpol_key = "SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient\\DnsPolicyConfig";
2644 static PCSTR sys_key =
2645 "SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters\\DnsPolicyConfig";
2646
2647 HKEY nrpt;
2648 *gpol = TRUE;
2649 LSTATUS err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, gpol_key, 0, KEY_ALL_ACCESS, &nrpt);
2650 if (err == ERROR_FILE_NOT_FOUND)
2651 {
2652 *gpol = FALSE;
2653 err = RegCreateKeyExA(HKEY_LOCAL_MACHINE, sys_key, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &nrpt,
2654 NULL);
2655 if (err)
2656 {
2657 nrpt = INVALID_HANDLE_VALUE;
2658 }
2659 }
2660 *key = nrpt;
2661 return err;
2662}
2663
2675static BOOL
2676DeleteNrptRules(DWORD pid, PBOOL gpol)
2677{
2678 HKEY key;
2679 LSTATUS err = OpenNrptBaseKey(&key, gpol);
2680 if (err)
2681 {
2682 MsgToEventLog(M_SYSERR, L"%S: could not open NRPT base key (%lu)", __func__, err);
2683 return FALSE;
2684 }
2685
2686 /* PID suffix string to compare against later */
2687 WCHAR pid_str[16];
2688 size_t pidlen = 0;
2689 if (pid)
2690 {
2691 swprintf(pid_str, _countof(pid_str), L"-%lu", pid);
2692 pidlen = wcslen(pid_str);
2693 }
2694
2695 int deleted = 0;
2696 DWORD enum_index = 0;
2697 while (TRUE)
2698 {
2699 WCHAR name[MAX_PATH];
2700 DWORD namelen = _countof(name);
2701 err = RegEnumKeyExW(key, enum_index++, name, &namelen, NULL, NULL, NULL, NULL);
2702 if (err)
2703 {
2704 if (err != ERROR_NO_MORE_ITEMS)
2705 {
2706 MsgToEventLog(M_SYSERR, L"%S: could not enumerate NRPT rules (%lu)", __func__, err);
2707 }
2708 break;
2709 }
2710
2711 /* Keep rule if name doesn't match */
2712 if (wcsncmp(name, L"OpenVPNDNSRouting", 17) != 0
2713 || (pid && wcsncmp(name + namelen - pidlen, pid_str, pidlen) != 0))
2714 {
2715 continue;
2716 }
2717
2718 if (RegDeleteKeyW(key, name) == NO_ERROR)
2719 {
2720 enum_index--;
2721 deleted++;
2722 }
2723 }
2724
2725 RegCloseKey(key);
2726 return deleted ? TRUE : FALSE;
2727}
2728
2734static void
2735UndoNrptRules(DWORD ovpn_pid)
2736{
2737 BOOL gpol;
2738 if (DeleteNrptRules(ovpn_pid, &gpol))
2739 {
2740 ApplyDnsSettings(gpol);
2741 }
2742}
2743
2755static DWORD
2757{
2758 /*
2759 * Use a non-const reference with limited scope to
2760 * enforce null-termination of strings from client
2761 */
2762 {
2764 msgptr->iface.name[_countof(msg->iface.name) - 1] = '\0';
2765 msgptr->search_domains[_countof(msg->search_domains) - 1] = '\0';
2766 msgptr->resolve_domains[_countof(msg->resolve_domains) - 1] = '\0';
2767 for (size_t i = 0; i < NRPT_ADDR_NUM; ++i)
2768 {
2769 msgptr->addresses[i][_countof(msg->addresses[0]) - 1] = '\0';
2770 }
2771 }
2772
2773 /* Make sure we have the VPN interface name */
2774 if (msg->iface.name[0] == 0)
2775 {
2776 return ERROR_MESSAGE_DATA;
2777 }
2778
2779 /* Some sanity checks on the add message data */
2780 if (msg->header.type == msg_add_nrpt_cfg)
2781 {
2782 /* At least one name server address is set */
2783 if (msg->addresses[0][0] == 0)
2784 {
2785 return ERROR_MESSAGE_DATA;
2786 }
2787 /* Resolve domains are double zero terminated (MULTI_SZ) */
2788 const char *rdom = msg->resolve_domains;
2789 size_t rdom_size = sizeof(msg->resolve_domains);
2790 size_t rdom_len = strlen(rdom);
2791 if (rdom_len && (rdom_len + 1 >= rdom_size || rdom[rdom_len + 2] != 0))
2792 {
2793 return ERROR_MESSAGE_DATA;
2794 }
2795 }
2796
2797 BOOL gpol_nrpt = FALSE;
2798 BOOL gpol_list = FALSE;
2799
2800 WCHAR iid[64];
2801 DWORD iid_err = InterfaceIdString(msg->iface.name, iid, _countof(iid));
2802 if (iid_err)
2803 {
2804 return iid_err;
2805 }
2806
2807 /* Delete previously set values for this instance first, if any */
2808 PDWORD undo_pid = RemoveListItem(&(*lists)[undo_nrpt], CmpAny, NULL);
2809 if (undo_pid)
2810 {
2811 if (*undo_pid != ovpn_pid)
2812 {
2814 L"%S: PID stored for undo doesn't match: %lu vs %lu. "
2815 "This is likely an error. Cleaning up anyway.",
2816 __func__, *undo_pid, ovpn_pid);
2817 }
2818 DeleteNrptRules(*undo_pid, &gpol_nrpt);
2819 free(undo_pid);
2820
2821 ResetNameServers(iid, AF_INET);
2822 ResetNameServers(iid, AF_INET6);
2823 }
2824 SetDnsSearchDomains(msg->iface.name, NULL, &gpol_list, lists);
2825
2826 if (msg->header.type == msg_del_nrpt_cfg)
2827 {
2828 ApplyDnsSettings(gpol_nrpt || gpol_list);
2829 return NO_ERROR; /* Done dealing with del message */
2830 }
2831
2832 HKEY key;
2833 LSTATUS err = OpenNrptBaseKey(&key, &gpol_nrpt);
2834 if (err)
2835 {
2836 goto out;
2837 }
2838
2839 /* Add undo information first in case there's no heap left */
2840 PDWORD pid = malloc(sizeof(ovpn_pid));
2841 if (!pid)
2842 {
2843 err = ERROR_OUTOFMEMORY;
2844 goto out;
2845 }
2846 *pid = ovpn_pid;
2847 if (AddListItem(&(*lists)[undo_nrpt], pid))
2848 {
2849 err = ERROR_OUTOFMEMORY;
2850 free(pid);
2851 goto out;
2852 }
2853
2854 /* Set NRPT rules */
2855 BOOL dnssec = (msg->flags & nrpt_dnssec) != 0;
2856 err = SetNrptRules(key, msg->addresses, msg->resolve_domains, msg->search_domains, dnssec,
2857 ovpn_pid);
2858 if (err)
2859 {
2860 goto out;
2861 }
2862
2863 /* Set name servers */
2864 err = SetNameServerAddresses(iid, msg->addresses);
2865 if (err)
2866 {
2867 goto out;
2868 }
2869
2870 /* Set search domains, if any */
2871 if (msg->search_domains[0])
2872 {
2873 err = SetDnsSearchDomains(msg->iface.name, msg->search_domains, &gpol_list, lists);
2874 }
2875
2876 ApplyDnsSettings(gpol_nrpt || gpol_list);
2877
2878out:
2879 return err;
2880}
2881
2882static DWORD
2884{
2885 DWORD err = 0;
2886 wchar_t addr[16]; /* large enough to hold string representation of an ipv4 */
2887 int addr_len = msg->addr_len;
2888
2889 /* sanity check */
2890 if (addr_len > _countof(msg->addr))
2891 {
2892 addr_len = _countof(msg->addr);
2893 }
2894
2895 if (!msg->iface.name[0]) /* interface name is required */
2896 {
2897 return ERROR_MESSAGE_DATA;
2898 }
2899
2900 /* use a non-const reference with limited scope to enforce null-termination of strings from
2901 * client */
2902 {
2904 msgptr->iface.name[_countof(msg->iface.name) - 1] = '\0';
2905 }
2906
2907 wchar_t *wide_name = utf8to16(msg->iface.name); /* utf8 to wide-char */
2908 if (!wide_name)
2909 {
2910 return ERROR_OUTOFMEMORY;
2911 }
2912
2913 /* We delete all current addresses before adding any
2914 * OR if the message type is del_wins_cfg
2915 */
2916 if (addr_len > 0 || msg->header.type == msg_del_wins_cfg)
2917 {
2918 err = netsh_wins_cmd(L"delete", wide_name, NULL);
2919 if (err)
2920 {
2921 goto out;
2922 }
2923 free(RemoveListItem(&(*lists)[undo_wins], CmpWString, wide_name));
2924 }
2925
2926 if (msg->header.type == msg_del_wins_cfg)
2927 {
2928 goto out; /* job done */
2929 }
2930
2931 for (int i = 0; i < addr_len; ++i)
2932 {
2933 RtlIpv4AddressToStringW(&msg->addr[i].ipv4, addr);
2934 err = netsh_wins_cmd(i == 0 ? L"set" : L"add", wide_name, addr);
2935 if (i == 0 && err)
2936 {
2937 goto out;
2938 }
2939 /* We do not check for duplicate addresses, so any error in adding
2940 * additional addresses is ignored.
2941 */
2942 }
2943
2944 err = 0;
2945
2946 if (addr_len > 0)
2947 {
2948 wchar_t *tmp_name = _wcsdup(wide_name);
2949 if (!tmp_name || AddListItem(&(*lists)[undo_wins], tmp_name))
2950 {
2951 free(tmp_name);
2952 netsh_wins_cmd(L"delete", wide_name, NULL);
2953 err = ERROR_OUTOFMEMORY;
2954 goto out;
2955 }
2956 }
2957
2958out:
2959 free(wide_name);
2960 return err;
2961}
2962
2963static DWORD
2965{
2966 DWORD err = 0;
2967 DWORD timeout = 5000; /* in milli seconds */
2968 wchar_t argv0[MAX_PATH];
2969
2970 /* Path of netsh */
2971 swprintf(argv0, _countof(argv0), L"%ls\\%ls", get_win_sys_path(), L"netsh.exe");
2972
2973 /* cmd template:
2974 * netsh interface ipv4 set address name=$if_index source=dhcp
2975 */
2976 const wchar_t *fmt = L"netsh interface ipv4 set address name=\"%d\" source=dhcp";
2977
2978 /* max cmdline length in wchars -- include room for if index:
2979 * 10 chars for 32 bit int in decimal and +1 for NUL
2980 */
2981 size_t ncmdline = wcslen(fmt) + 10 + 1;
2982 wchar_t *cmdline = malloc(ncmdline * sizeof(wchar_t));
2983 if (!cmdline)
2984 {
2985 err = ERROR_OUTOFMEMORY;
2986 return err;
2987 }
2988
2989 swprintf(cmdline, ncmdline, fmt, dhcp->iface.index);
2990
2991 err = ExecCommand(argv0, cmdline, timeout);
2992
2993 /* Note: This could fail if dhcp is already enabled, so the caller
2994 * may not want to treat errors as FATAL.
2995 */
2996
2997 free(cmdline);
2998 return err;
2999}
3000
3001static DWORD
3003{
3004 DWORD err = 0;
3005 MIB_IPINTERFACE_ROW ipiface;
3006 InitializeIpInterfaceEntry(&ipiface);
3007 ipiface.Family = mtu->family;
3008 ipiface.InterfaceIndex = mtu->iface.index;
3009 err = GetIpInterfaceEntry(&ipiface);
3010 if (err != NO_ERROR)
3011 {
3012 return err;
3013 }
3014 if (mtu->family == AF_INET)
3015 {
3016 ipiface.SitePrefixLength = 0;
3017 }
3018 ipiface.NlMtu = mtu->mtu;
3019
3020 err = SetIpInterfaceEntry(&ipiface);
3021 return err;
3022}
3023
3031static DWORD
3033{
3034 const WCHAR *hwid;
3035
3036 switch (msg->adapter_type)
3037 {
3038 case ADAPTER_TYPE_DCO:
3039 hwid = L"ovpn-dco";
3040 break;
3041
3042 case ADAPTER_TYPE_TAP:
3043 hwid = L"root\\tap0901";
3044 break;
3045
3046 default:
3047 return ERROR_INVALID_PARAMETER;
3048 }
3049
3050 WCHAR cmd[MAX_PATH];
3051 WCHAR args[MAX_PATH];
3052
3053 if (swprintf_s(cmd, _countof(cmd), L"%s\\tapctl.exe", settings.bin_dir) < 0)
3054 {
3055 return ERROR_BUFFER_OVERFLOW;
3056 }
3057
3058 if (swprintf_s(args, _countof(args), L"tapctl create --hwid %s", hwid) < 0)
3059 {
3060 return ERROR_BUFFER_OVERFLOW;
3061 }
3062
3063 return ExecCommand(cmd, args, 10000);
3064}
3065
3066static VOID
3067HandleMessage(HANDLE pipe, PPROCESS_INFORMATION proc_info, DWORD bytes, DWORD count,
3068 LPHANDLE events, undo_lists_t *lists)
3069{
3071 ack_message_t ack = {
3072 .header = { .type = msg_acknowledgement, .size = sizeof(ack), .message_id = -1 },
3073 .error_number = ERROR_MESSAGE_DATA
3074 };
3075
3076 DWORD read = ReadPipeAsync(pipe, &msg, bytes, count, events);
3077 if (read != bytes || read < sizeof(msg.header) || read != msg.header.size)
3078 {
3079 goto out;
3080 }
3081
3082 ack.header.message_id = msg.header.message_id;
3083
3084 switch (msg.header.type)
3085 {
3086 case msg_add_address:
3087 case msg_del_address:
3088 if (msg.header.size == sizeof(msg.address))
3089 {
3090 ack.error_number = HandleAddressMessage(&msg.address, lists);
3091 }
3092 break;
3093
3094 case msg_add_route:
3095 case msg_del_route:
3096 if (msg.header.size == sizeof(msg.route))
3097 {
3098 ack.error_number = HandleRouteMessage(&msg.route, lists);
3099 }
3100 break;
3101
3103 if (msg.header.size == sizeof(msg.flush_neighbors))
3104 {
3105 ack.error_number = HandleFlushNeighborsMessage(&msg.flush_neighbors);
3106 }
3107 break;
3108
3109 case msg_add_wfp_block:
3110 case msg_del_wfp_block:
3111 if (msg.header.size == sizeof(msg.wfp_block))
3112 {
3113 ack.error_number = HandleWfpBlockMessage(&msg.wfp_block, lists);
3114 }
3115 break;
3116
3117 case msg_register_dns:
3119 break;
3120
3121 case msg_add_dns_cfg:
3122 case msg_del_dns_cfg:
3123 ack.error_number = HandleDNSConfigMessage(&msg.dns, lists);
3124 break;
3125
3126 case msg_add_nrpt_cfg:
3127 case msg_del_nrpt_cfg:
3128 {
3129 DWORD ovpn_pid = proc_info->dwProcessId;
3130 ack.error_number = HandleDNSConfigNrptMessage(&msg.nrpt_dns, ovpn_pid, lists);
3131 }
3132 break;
3133
3134 case msg_add_wins_cfg:
3135 case msg_del_wins_cfg:
3136 ack.error_number = HandleWINSConfigMessage(&msg.wins, lists);
3137 break;
3138
3139 case msg_enable_dhcp:
3140 if (msg.header.size == sizeof(msg.dhcp))
3141 {
3143 }
3144 break;
3145
3146 case msg_set_mtu:
3147 if (msg.header.size == sizeof(msg.mtu))
3148 {
3149 ack.error_number = HandleMTUMessage(&msg.mtu);
3150 }
3151 break;
3152
3153 case msg_create_adapter:
3154 if (msg.header.size == sizeof(msg.create_adapter))
3155 {
3156 ack.error_number = HandleCreateAdapterMessage(&msg.create_adapter);
3157 }
3158 break;
3159
3160 default:
3162 MsgToEventLog(MSG_FLAGS_ERROR, L"Unknown message type %d", msg.header.type);
3163 break;
3164 }
3165
3166out:
3167 WritePipeAsync(pipe, &ack, sizeof(ack), count, events);
3168}
3169
3170
3171static VOID
3173{
3174 undo_type_t type;
3175 wfp_block_data_t *interface_data;
3176 for (type = 0; type < _undo_type_max; type++)
3177 {
3178 list_item_t **pnext = &(*lists)[type];
3179 while (*pnext)
3180 {
3181 list_item_t *item = *pnext;
3182 switch (type)
3183 {
3184 case address:
3185 DeleteAddress(item->data);
3186 break;
3187
3188 case route:
3189 DeleteRoute(item->data);
3190 break;
3191
3192 case undo_dns4:
3193 ResetNameServers(item->data, AF_INET);
3194 break;
3195
3196 case undo_dns6:
3197 ResetNameServers(item->data, AF_INET6);
3198 break;
3199
3200 case undo_nrpt:
3201 UndoNrptRules(*(PDWORD)item->data);
3202 break;
3203
3204 case undo_domains:
3206 break;
3207
3208 case undo_wins:
3209 netsh_wins_cmd(L"delete", item->data, NULL);
3210 break;
3211
3212 case wfp_block:
3213 interface_data = (wfp_block_data_t *)(item->data);
3214 delete_wfp_block_filters(interface_data->engine);
3215 if (interface_data->metric_v4 >= 0)
3216 {
3217 set_interface_metric(interface_data->index, AF_INET,
3218 interface_data->metric_v4);
3219 }
3220 if (interface_data->metric_v6 >= 0)
3221 {
3222 set_interface_metric(interface_data->index, AF_INET6,
3223 interface_data->metric_v6);
3224 }
3225 break;
3226
3227 case _undo_type_max:
3228 /* unreachable */
3229 break;
3230 }
3231
3232 /* Remove from the list and free memory */
3233 *pnext = item->next;
3234 free(item->data);
3235 free(item);
3236 }
3237 }
3238}
3239
3240static DWORD WINAPI
3241RunOpenvpn(LPVOID p)
3242{
3243 HANDLE pipe = p;
3244 HANDLE ovpn_pipe = NULL, svc_pipe = NULL;
3245 PTOKEN_USER svc_user = NULL, ovpn_user = NULL;
3246 HANDLE svc_token = NULL, imp_token = NULL, pri_token = NULL;
3247 HANDLE stdin_read = NULL, stdin_write = NULL;
3248 HANDLE stdout_write = NULL;
3249 DWORD pipe_mode, len, exit_code = 0;
3250 STARTUP_DATA sud = { 0, 0, 0 };
3251 STARTUPINFOW startup_info;
3252 PROCESS_INFORMATION proc_info;
3253 LPVOID user_env = NULL;
3254 WCHAR ovpn_pipe_name[256]; /* The entire pipe name string can be up to 256 characters long
3255 according to MSDN. */
3256 LPCWSTR exe_path;
3257 WCHAR *cmdline = NULL;
3258 size_t cmdline_size;
3259 undo_lists_t undo_lists;
3260 WCHAR errmsg[512] = L"";
3261
3262 SECURITY_ATTRIBUTES inheritable = { .nLength = sizeof(inheritable),
3263 .lpSecurityDescriptor = NULL,
3264 .bInheritHandle = TRUE };
3265
3266 PACL ovpn_dacl;
3267 EXPLICIT_ACCESS ea[2];
3268 SECURITY_DESCRIPTOR ovpn_sd;
3269 SECURITY_ATTRIBUTES ovpn_sa = { .nLength = sizeof(ovpn_sa),
3270 .lpSecurityDescriptor = &ovpn_sd,
3271 .bInheritHandle = FALSE };
3272
3273 ZeroMemory(&ea, sizeof(ea));
3274 ZeroMemory(&startup_info, sizeof(startup_info));
3275 ZeroMemory(&undo_lists, sizeof(undo_lists));
3276 ZeroMemory(&proc_info, sizeof(proc_info));
3277
3278 if (!GetStartupData(pipe, &sud))
3279 {
3280 goto out;
3281 }
3282
3283 if (!InitializeSecurityDescriptor(&ovpn_sd, SECURITY_DESCRIPTOR_REVISION))
3284 {
3285 ReturnLastError(pipe, L"InitializeSecurityDescriptor");
3286 goto out;
3287 }
3288
3289 /* Get SID of user the service is running under */
3290 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &svc_token))
3291 {
3292 ReturnLastError(pipe, L"OpenProcessToken");
3293 goto out;
3294 }
3295 len = 0;
3296 while (!GetTokenInformation(svc_token, TokenUser, svc_user, len, &len))
3297 {
3298 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
3299 {
3300 ReturnLastError(pipe, L"GetTokenInformation (service token)");
3301 goto out;
3302 }
3303 free(svc_user);
3304 svc_user = malloc(len);
3305 if (svc_user == NULL)
3306 {
3307 ReturnLastError(pipe, L"malloc (service token user)");
3308 goto out;
3309 }
3310 }
3311 if (!IsValidSid(svc_user->User.Sid))
3312 {
3313 ReturnLastError(pipe, L"IsValidSid (service token user)");
3314 goto out;
3315 }
3316
3317 if (!ImpersonateNamedPipeClient(pipe))
3318 {
3319 ReturnLastError(pipe, L"ImpersonateNamedPipeClient");
3320 goto out;
3321 }
3322 if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &imp_token))
3323 {
3324 ReturnLastError(pipe, L"OpenThreadToken");
3325 goto out;
3326 }
3327 len = 0;
3328 while (!GetTokenInformation(imp_token, TokenUser, ovpn_user, len, &len))
3329 {
3330 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
3331 {
3332 ReturnLastError(pipe, L"GetTokenInformation (impersonation token)");
3333 goto out;
3334 }
3335 free(ovpn_user);
3336 ovpn_user = malloc(len);
3337 if (ovpn_user == NULL)
3338 {
3339 ReturnLastError(pipe, L"malloc (impersonation token user)");
3340 goto out;
3341 }
3342 }
3343 if (!IsValidSid(ovpn_user->User.Sid))
3344 {
3345 ReturnLastError(pipe, L"IsValidSid (impersonation token user)");
3346 goto out;
3347 }
3348
3349 /*
3350 * Only authorized users are allowed to use any command line options or
3351 * have the config file in locations other than the global config directory.
3352 *
3353 * Check options are white-listed and config is in the global directory
3354 * OR user is authorized to run any config.
3355 */
3356 if (!ValidateOptions(pipe, sud.directory, sud.options, errmsg, _countof(errmsg))
3357 && !IsAuthorizedUser(ovpn_user->User.Sid, imp_token, settings.ovpn_admin_group,
3359 {
3360 ReturnError(pipe, ERROR_STARTUP_DATA, errmsg, 1, &exit_event);
3361 goto out;
3362 }
3363
3364 /* OpenVPN process DACL entry for access by service and user */
3365 ea[0].grfAccessPermissions = SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL;
3366 ea[0].grfAccessMode = SET_ACCESS;
3367 ea[0].grfInheritance = NO_INHERITANCE;
3368 ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
3369 ea[0].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
3370 ea[0].Trustee.ptstrName = (LPWSTR)svc_user->User.Sid;
3371 ea[1].grfAccessPermissions = READ_CONTROL | SYNCHRONIZE | PROCESS_VM_READ | SYNCHRONIZE
3372 | PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION;
3373 ea[1].grfAccessMode = SET_ACCESS;
3374 ea[1].grfInheritance = NO_INHERITANCE;
3375 ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
3376 ea[1].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
3377 ea[1].Trustee.ptstrName = (LPWSTR)ovpn_user->User.Sid;
3378
3379 /* Set owner and DACL of OpenVPN security descriptor */
3380 if (!SetSecurityDescriptorOwner(&ovpn_sd, svc_user->User.Sid, FALSE))
3381 {
3382 ReturnLastError(pipe, L"SetSecurityDescriptorOwner");
3383 goto out;
3384 }
3385 if (SetEntriesInAcl(2, ea, NULL, &ovpn_dacl) != ERROR_SUCCESS)
3386 {
3387 ReturnLastError(pipe, L"SetEntriesInAcl");
3388 goto out;
3389 }
3390 if (!SetSecurityDescriptorDacl(&ovpn_sd, TRUE, ovpn_dacl, FALSE))
3391 {
3392 ReturnLastError(pipe, L"SetSecurityDescriptorDacl");
3393 goto out;
3394 }
3395
3396 /* Create primary token from impersonation token */
3397 if (!DuplicateTokenEx(imp_token, TOKEN_ALL_ACCESS, NULL, 0, TokenPrimary, &pri_token))
3398 {
3399 ReturnLastError(pipe, L"DuplicateTokenEx");
3400 goto out;
3401 }
3402
3403 /* use /dev/null for stdout of openvpn (client should use --log for output) */
3404 stdout_write = CreateFile(_L("NUL"), GENERIC_WRITE, FILE_SHARE_WRITE, &inheritable,
3405 OPEN_EXISTING, 0, NULL);
3406 if (stdout_write == INVALID_HANDLE_VALUE)
3407 {
3408 ReturnLastError(pipe, L"CreateFile for stdout");
3409 goto out;
3410 }
3411
3412 if (!CreatePipe(&stdin_read, &stdin_write, &inheritable, 0)
3413 || !SetHandleInformation(stdin_write, HANDLE_FLAG_INHERIT, 0))
3414 {
3415 ReturnLastError(pipe, L"CreatePipe");
3416 goto out;
3417 }
3418
3419 swprintf(ovpn_pipe_name, _countof(ovpn_pipe_name),
3420 L"\\\\.\\pipe\\" _L(PACKAGE) L"%ls\\service_%lu", service_instance,
3421 GetCurrentThreadId());
3422 ovpn_pipe = CreateNamedPipe(
3423 ovpn_pipe_name, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED,
3424 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 1, 128, 128, 0, NULL);
3425 if (ovpn_pipe == INVALID_HANDLE_VALUE)
3426 {
3427 ReturnLastError(pipe, L"CreateNamedPipe");
3428 goto out;
3429 }
3430
3431 svc_pipe = CreateFile(ovpn_pipe_name, GENERIC_READ | GENERIC_WRITE, 0, &inheritable,
3432 OPEN_EXISTING, 0, NULL);
3433 if (svc_pipe == INVALID_HANDLE_VALUE)
3434 {
3435 ReturnLastError(pipe, L"CreateFile");
3436 goto out;
3437 }
3438
3439 pipe_mode = PIPE_READMODE_MESSAGE;
3440 if (!SetNamedPipeHandleState(svc_pipe, &pipe_mode, NULL, NULL))
3441 {
3442 ReturnLastError(pipe, L"SetNamedPipeHandleState");
3443 goto out;
3444 }
3445
3446 cmdline_size = wcslen(sud.options) + 128;
3447 cmdline = malloc(cmdline_size * sizeof(*cmdline));
3448 if (cmdline == NULL)
3449 {
3450 ReturnLastError(pipe, L"malloc");
3451 goto out;
3452 }
3453 /* there seem to be no common printf specifier that works on all
3454 * mingw/msvc platforms without trickery, so convert to void* and use
3455 * PRIuPTR to print that as best compromise */
3456 swprintf(cmdline, cmdline_size, L"openvpn %ls --msg-channel %" PRIuPTR, sud.options,
3457 (uintptr_t)svc_pipe);
3458
3459 if (!CreateEnvironmentBlock(&user_env, imp_token, FALSE))
3460 {
3461 ReturnLastError(pipe, L"CreateEnvironmentBlock");
3462 goto out;
3463 }
3464
3465 startup_info.cb = sizeof(startup_info);
3466 startup_info.dwFlags = STARTF_USESTDHANDLES;
3467 startup_info.hStdInput = stdin_read;
3468 startup_info.hStdOutput = stdout_write;
3469 startup_info.hStdError = stdout_write;
3470
3471 exe_path = settings.exe_path;
3472
3473 /* TODO: make sure HKCU is correct or call LoadUserProfile() */
3474 if (!CreateProcessAsUserW(pri_token, exe_path, cmdline, &ovpn_sa, NULL, TRUE,
3475 settings.priority | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
3476 user_env, sud.directory, &startup_info, &proc_info))
3477 {
3478 ReturnLastError(pipe, L"CreateProcessAsUser");
3479 goto out;
3480 }
3481
3482 if (!RevertToSelf())
3483 {
3484 TerminateProcess(proc_info.hProcess, 1);
3485 ReturnLastError(pipe, L"RevertToSelf");
3486 goto out;
3487 }
3488
3489 ReturnProcessId(pipe, proc_info.dwProcessId, 1, &exit_event);
3490
3491 CloseHandleEx(&stdout_write);
3492 CloseHandleEx(&stdin_read);
3493 CloseHandleEx(&svc_pipe);
3494
3495 DWORD input_size = WideCharToMultiByte(CP_UTF8, 0, sud.std_input, -1, NULL, 0, NULL, NULL);
3496 LPSTR input = NULL;
3497 if (input_size && (input = malloc(input_size)))
3498 {
3499 DWORD written;
3500 WideCharToMultiByte(CP_UTF8, 0, sud.std_input, -1, input, input_size, NULL, NULL);
3501 WriteFile(stdin_write, input, (DWORD)strlen(input), &written, NULL);
3502 free(input);
3503 }
3504
3505 while (TRUE)
3506 {
3507 DWORD bytes = PeekNamedPipeAsync(ovpn_pipe, 1, &exit_event);
3508 if (bytes == 0)
3509 {
3510 break;
3511 }
3512
3513 if (bytes > sizeof(pipe_message_t))
3514 {
3515 /* process at the other side of the pipe is misbehaving, shut it down */
3518 L"OpenVPN process sent too large payload length to the pipe (%lu bytes), it will be terminated",
3519 bytes);
3520 break;
3521 }
3522
3523 HandleMessage(ovpn_pipe, &proc_info, bytes, 1, &exit_event, &undo_lists);
3524 }
3525
3526 WaitForSingleObject(proc_info.hProcess, IO_TIMEOUT);
3527 GetExitCodeProcess(proc_info.hProcess, &exit_code);
3528 if (exit_code == STILL_ACTIVE)
3529 {
3530 TerminateProcess(proc_info.hProcess, 1);
3531 }
3532 else if (exit_code != 0)
3533 {
3534 WCHAR buf[256];
3535 swprintf(buf, _countof(buf), L"OpenVPN exited with error: exit code = %lu", exit_code);
3537 }
3538 Undo(&undo_lists);
3539
3540out:
3541 FlushFileBuffers(pipe);
3542 DisconnectNamedPipe(pipe);
3543
3544 free(ovpn_user);
3545 free(svc_user);
3546 free(cmdline);
3547 DestroyEnvironmentBlock(user_env);
3548 FreeStartupData(&sud);
3549 CloseHandleEx(&proc_info.hProcess);
3550 CloseHandleEx(&proc_info.hThread);
3551 CloseHandleEx(&stdin_read);
3552 CloseHandleEx(&stdin_write);
3553 CloseHandleEx(&stdout_write);
3554 CloseHandleEx(&svc_token);
3555 CloseHandleEx(&imp_token);
3556 CloseHandleEx(&pri_token);
3557 CloseHandleEx(&ovpn_pipe);
3558 CloseHandleEx(&svc_pipe);
3559 CloseHandleEx(&pipe);
3560
3561 return 0;
3562}
3563
3564
3565static DWORD WINAPI
3566ServiceCtrlInteractive(DWORD ctrl_code, DWORD event, LPVOID data, LPVOID ctx)
3567{
3568 SERVICE_STATUS *status = ctx;
3569 switch (ctrl_code)
3570 {
3571 case SERVICE_CONTROL_STOP:
3572 status->dwCurrentState = SERVICE_STOP_PENDING;
3574 if (exit_event)
3575 {
3576 SetEvent(exit_event);
3577 }
3578 return NO_ERROR;
3579
3580 case SERVICE_CONTROL_INTERROGATE:
3581 return NO_ERROR;
3582
3583 default:
3584 return ERROR_CALL_NOT_IMPLEMENTED;
3585 }
3586}
3587
3588
3589static HANDLE
3591{
3592 /*
3593 * allow all access for local system
3594 * deny FILE_CREATE_PIPE_INSTANCE for everyone
3595 * allow read/write for authenticated users
3596 * deny all access to anonymous
3597 */
3598 const WCHAR *sddlString =
3599 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)";
3600
3601 PSECURITY_DESCRIPTOR sd = NULL;
3602 if (!ConvertStringSecurityDescriptorToSecurityDescriptor(sddlString, SDDL_REVISION_1, &sd,
3603 NULL))
3604 {
3605 MsgToEventLog(M_SYSERR, L"ConvertStringSecurityDescriptorToSecurityDescriptor failed.");
3606 return INVALID_HANDLE_VALUE;
3607 }
3608
3609 /* Set up SECURITY_ATTRIBUTES */
3610 SECURITY_ATTRIBUTES sa = { 0 };
3611 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
3612 sa.lpSecurityDescriptor = sd;
3613 sa.bInheritHandle = FALSE;
3614
3615 DWORD flags = PIPE_ACCESS_DUPLEX | WRITE_DAC | FILE_FLAG_OVERLAPPED;
3616
3617 static BOOL first = TRUE;
3618 if (first)
3619 {
3620 flags |= FILE_FLAG_FIRST_PIPE_INSTANCE;
3621 first = FALSE;
3622 }
3623
3624 WCHAR pipe_name[256]; /* The entire pipe name string can be up to 256 characters long according
3625 to MSDN. */
3626 swprintf(pipe_name, _countof(pipe_name), L"\\\\.\\pipe\\" _L(PACKAGE) L"%ls\\service",
3628 HANDLE pipe = CreateNamedPipe(
3629 pipe_name, flags, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
3630 PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0, &sa);
3631
3632 LocalFree(sd);
3633
3634 if (pipe == INVALID_HANDLE_VALUE)
3635 {
3636 MsgToEventLog(M_SYSERR, L"Could not create named pipe");
3637 return INVALID_HANDLE_VALUE;
3638 }
3639
3640 return pipe;
3641}
3642
3643
3644static DWORD
3645UpdateWaitHandles(LPHANDLE *handles_ptr, LPDWORD count, HANDLE io_event, HANDLE exit_event,
3646 list_item_t *threads)
3647{
3648 static DWORD size = 10;
3649 static LPHANDLE handles = NULL;
3650 DWORD pos = 0;
3651
3652 if (handles == NULL)
3653 {
3654 handles = malloc(size * sizeof(HANDLE));
3655 *handles_ptr = handles;
3656 if (handles == NULL)
3657 {
3658 return ERROR_OUTOFMEMORY;
3659 }
3660 }
3661
3662 handles[pos++] = io_event;
3663
3664 if (!threads)
3665 {
3666 handles[pos++] = exit_event;
3667 }
3668
3669 while (threads)
3670 {
3671 if (pos == size)
3672 {
3673 LPHANDLE tmp;
3674 size += 10;
3675 tmp = realloc(handles, size * sizeof(HANDLE));
3676 if (tmp == NULL)
3677 {
3678 size -= 10;
3679 *count = pos;
3680 return ERROR_OUTOFMEMORY;
3681 }
3682 handles = tmp;
3683 *handles_ptr = handles;
3684 }
3685 handles[pos++] = threads->data;
3686 threads = threads->next;
3687 }
3688
3689 *count = pos;
3690 return NO_ERROR;
3691}
3692
3693
3694static VOID
3696{
3697 free(h);
3698}
3699
3700static BOOL
3701CmpHandle(LPVOID item, LPVOID hnd)
3702{
3703 return item == hnd;
3704}
3705
3706
3707VOID WINAPI
3708ServiceStartInteractiveOwn(DWORD dwArgc, LPWSTR *lpszArgv)
3709{
3710 status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
3711 ServiceStartInteractive(dwArgc, lpszArgv);
3712}
3713
3719static void
3721{
3722 BOOL changed = FALSE;
3723
3724 /* Clean up leftover NRPT rules */
3725 BOOL gpol_nrpt;
3726 changed = DeleteNrptRules(0, &gpol_nrpt);
3727
3728 /* Clean up leftover DNS search list fragments */
3729 HKEY key;
3730 BOOL gpol_list;
3731 GetDnsSearchListKey(NULL, &gpol_list, &key);
3732 if (key != INVALID_HANDLE_VALUE)
3733 {
3735 {
3736 changed = TRUE;
3737 }
3738 RegCloseKey(key);
3739 }
3740
3741 if (changed)
3742 {
3743 ApplyDnsSettings(gpol_nrpt || gpol_list);
3744 }
3745}
3746
3747VOID WINAPI
3748ServiceStartInteractive(DWORD dwArgc, LPWSTR *lpszArgv)
3749{
3750 HANDLE pipe, io_event = NULL;
3751 OVERLAPPED overlapped;
3752 DWORD error = NO_ERROR;
3753 list_item_t *threads = NULL;
3754 PHANDLE handles = NULL;
3755 DWORD handle_count;
3756
3757 service =
3758 RegisterServiceCtrlHandlerEx(interactive_service.name, ServiceCtrlInteractive, &status);
3759 if (!service)
3760 {
3761 return;
3762 }
3763
3764 status.dwCurrentState = SERVICE_START_PENDING;
3765 status.dwServiceSpecificExitCode = NO_ERROR;
3766 status.dwWin32ExitCode = NO_ERROR;
3767 status.dwWaitHint = 3000;
3769
3770 /* Clean up potentially left over registry values */
3772
3773 /* Read info from registry in key HKLM\SOFTWARE\OpenVPN */
3774 error = GetOpenvpnSettings(&settings);
3775 if (error != ERROR_SUCCESS)
3776 {
3777 goto out;
3778 }
3779
3780 io_event = InitOverlapped(&overlapped);
3781 exit_event = CreateEvent(NULL, TRUE, FALSE, NULL);
3782 if (!exit_event || !io_event)
3783 {
3784 error = MsgToEventLog(M_SYSERR, L"Could not create event");
3785 goto out;
3786 }
3787
3788 rdns_semaphore = CreateSemaphoreW(NULL, 1, 1, NULL);
3789 if (!rdns_semaphore)
3790 {
3791 error = MsgToEventLog(M_SYSERR, L"Could not create semaphore for register-dns");
3792 goto out;
3793 }
3794
3795 error = UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
3796 if (error != NO_ERROR)
3797 {
3798 goto out;
3799 }
3800
3801 pipe = CreateClientPipeInstance();
3802 if (pipe == INVALID_HANDLE_VALUE)
3803 {
3804 goto out;
3805 }
3806
3807 status.dwCurrentState = SERVICE_RUNNING;
3808 status.dwWaitHint = 0;
3810
3811 while (TRUE)
3812 {
3813 if (ConnectNamedPipe(pipe, &overlapped) == FALSE && GetLastError() != ERROR_PIPE_CONNECTED
3814 && GetLastError() != ERROR_IO_PENDING)
3815 {
3816 MsgToEventLog(M_SYSERR, L"Could not connect pipe");
3817 break;
3818 }
3819
3820 error = WaitForMultipleObjects(handle_count, handles, FALSE, INFINITE);
3821 if (error == WAIT_OBJECT_0)
3822 {
3823 /* Client connected, spawn a worker thread for it */
3824 HANDLE next_pipe = CreateClientPipeInstance();
3825 HANDLE thread = CreateThread(NULL, 0, RunOpenvpn, pipe, CREATE_SUSPENDED, NULL);
3826 if (thread)
3827 {
3828 error = AddListItem(&threads, thread);
3829 if (!error)
3830 {
3831 error =
3832 UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
3833 }
3834 if (error)
3835 {
3836 ReturnError(pipe, error, L"Insufficient resources to service new clients", 1,
3837 &exit_event);
3838 /* Update wait handles again after removing the last worker thread */
3839 RemoveListItem(&threads, CmpHandle, thread);
3840 UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
3841 TerminateThread(thread, 1);
3842 CloseHandleEx(&thread);
3843 CloseHandleEx(&pipe);
3844 }
3845 else
3846 {
3847 ResumeThread(thread);
3848 }
3849 }
3850 else
3851 {
3852 CloseHandleEx(&pipe);
3853 }
3854
3855 ResetOverlapped(&overlapped);
3856 pipe = next_pipe;
3857 }
3858 else
3859 {
3860 CancelIo(pipe);
3861 if (error == WAIT_FAILED)
3862 {
3863 MsgToEventLog(M_SYSERR, L"WaitForMultipleObjects failed");
3864 SetEvent(exit_event);
3865 /* Give some time for worker threads to exit and then terminate */
3866 Sleep(1000);
3867 break;
3868 }
3869 if (!threads)
3870 {
3871 /* exit event signaled */
3872 CloseHandleEx(&pipe);
3873 ResetEvent(exit_event);
3874 error = NO_ERROR;
3875 break;
3876 }
3877
3878 /* Worker thread ended */
3879 HANDLE thread = RemoveListItem(&threads, CmpHandle, handles[error]);
3880 UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
3881 CloseHandleEx(&thread);
3882 }
3883 }
3884
3885out:
3886 FreeWaitHandles(handles);
3887 CloseHandleEx(&io_event);
3890
3891 status.dwCurrentState = SERVICE_STOPPED;
3892 status.dwWin32ExitCode = error;
3894}
static int buf_len(const struct buffer *buf)
Definition buffer.h:253
wchar_t * utf8to16_size(const char *utf8, int size)
Convert a UTF-8 string to UTF-16.
Definition common.c:276
DWORD MsgToEventLog(DWORD flags, LPCWSTR format,...)
Definition common.c:235
LPCWSTR service_instance
Definition common.c:27
DWORD GetOpenvpnSettings(settings_t *s)
Definition common.c:58
#define PACKAGE_NAME
Definition config.h:492
#define PACKAGE
Definition config.h:486
#define M_INFO
Definition errlevel.h:54
static LSTATUS GetItfDnsServersV4(HKEY itf_key, PSTR addrs, PDWORD size)
Get DNS server IPv4 addresses of an interface.
static LSTATUS SetNameServerAddresses(PWSTR itf_id, const nrpt_address_t *addresses)
Set name servers from a NRPT address list.
static VOID ReturnLastError(HANDLE pipe, LPCWSTR func)
static BOOL GetInterfacesKey(short family, PHKEY key)
Return the interfaces registry key for the specified address family.
static DWORD ReadPipeAsync(HANDLE pipe, LPVOID buffer, DWORD size, DWORD count, LPHANDLE events)
static void UndoNrptRules(DWORD ovpn_pid)
Delete a process' NRPT rules and apply the reduced set of rules.
static BOOL ApplyGpolSettings(void)
Signal the DNS resolver (and others potentially) to reload the group policy (DNS) settings.
static VOID ReturnProcessId(HANDLE pipe, DWORD pid, DWORD count, LPHANDLE events)
static BOOL GetDnsSearchListKey(PCSTR itf_name, PBOOL gpol, PHKEY key)
Find the registry key for storing the DNS domains for the VPN interface.
static DWORD HandleWINSConfigMessage(const wins_cfg_message_t *msg, undo_lists_t *lists)
static BOOL CmpAddress(LPVOID item, LPVOID address)
static LSTATUS GetItfDnsDomains(HKEY itf, PCWSTR search_domains, PWSTR domains, PDWORD size)
Return interface specific domain suffix(es)
static DWORD 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 BOOL CmpWString(LPVOID item, LPVOID str)
static HANDLE CreateClientPipeInstance(VOID)
static void GetNrptExcludeData(PCWSTR search_domains, nrpt_exclude_data_t *data, size_t data_size)
Collect interface DNS settings to be used in excluding NRPT rules.
static DWORD SetNameServersValue(PCWSTR itf_id, short family, PCSTR value)
Set the DNS name servers in a registry interface configuration.
static BOOL GetStartupData(HANDLE pipe, STARTUP_DATA *sud)
static DWORD DeleteWfpBlock(const wfp_block_message_t *msg, undo_lists_t *lists)
static BOOL DeleteNrptRules(DWORD pid, PBOOL gpol)
Delete OpenVPN NRPT rules from the registry.
static VOID Undo(undo_lists_t *lists)
static BOOL ApplyDnsSettings(BOOL apply_gpol)
Signal the DNS resolver to reload its settings.
#define ERROR_STARTUP_DATA
Definition interactive.c:46
static DWORD WINAPI RunOpenvpn(LPVOID p)
static settings_t settings
Definition interactive.c:53
VOID WINAPI ServiceStartInteractive(DWORD dwArgc, LPWSTR *lpszArgv)
static DWORD DeleteRoute(PMIB_IPFORWARD_ROW2 fwd_row)
static SERVICE_STATUS status
Definition interactive.c:51
static DWORD HandleDNSConfigNrptMessage(const nrpt_dns_cfg_message_t *msg, DWORD ovpn_pid, undo_lists_t *lists)
Add Name Resolution Policy Table (NRPT) rules as documented in https://msdn.microsoft....
static DWORD SetDnsSearchDomains(PCSTR itf_name, PCSTR domains, PBOOL gpol, undo_lists_t *lists)
Add or remove DNS search domains.
static void CleanupRegistry(void)
Clean up remains of previous sessions in registry.
#define ERROR_MESSAGE_TYPE
Definition interactive.c:48
static SOCKADDR_INET sockaddr_inet(short family, inet_address_t *addr)
static LPVOID RemoveListItem(list_item_t **pfirst, match_fn_t match, LPVOID ctx)
static BOOL CmpHandle(LPVOID item, LPVOID hnd)
static BOOL ApplyGpolSettings64(void)
Signal the DNS resolver (and others potentially) to reload the group policy (DNS) settings on 64 bit ...
static DWORD HandleAddressMessage(address_message_t *msg, undo_lists_t *lists)
static VOID ReturnError(HANDLE pipe, DWORD error, LPCWSTR func, DWORD count, LPHANDLE events)
static DWORD AddListItem(list_item_t **pfirst, LPVOID data)
static void BlockDNSErrHandler(DWORD err, const char *msg)
static DWORD ResetNameServers(PCWSTR itf_id, short family)
Delete all DNS name servers from a registry interface configuration.
static LSTATUS OpenNrptBaseKey(PHKEY key, PBOOL gpol)
Return the registry key where NRPT rules are stored.
#define RDNS_TIMEOUT
Definition interactive.c:55
undo_type_t
Definition interactive.c:83
@ wfp_block
Definition interactive.c:86
@ _undo_type_max
Definition interactive.c:92
@ undo_dns6
Definition interactive.c:88
@ undo_dns4
Definition interactive.c:87
@ undo_wins
Definition interactive.c:91
@ route
Definition interactive.c:85
@ undo_nrpt
Definition interactive.c:89
@ address
Definition interactive.c:84
@ undo_domains
Definition interactive.c:90
static BOOL HasValidSearchList(HKEY key)
Check for a valid search list in a certain key of the registry.
static DWORD HandleRouteMessage(route_message_t *msg, undo_lists_t *lists)
static DWORD WINAPI RegisterDNS(LPVOID unused)
static HANDLE InitOverlapped(LPOVERLAPPED overlapped)
BOOL(* match_fn_t)(LPVOID item, LPVOID ctx)
static HANDLE CloseHandleEx(LPHANDLE handle)
static DWORD WINAPI ServiceCtrlInteractive(DWORD ctrl_code, DWORD event, LPVOID data, LPVOID ctx)
static BOOL StoreInitialDnsSearchList(HKEY key, PCWSTR list)
Prepare DNS domain "SearchList" registry value, so additional VPN domains can be added and its origin...
struct _list_item list_item_t
static DWORD DeleteAddress(PMIB_UNICASTIPADDRESS_ROW addr_row)
static BOOL IsInterfaceConnected(PWSTR iid_str)
Check if an interface is connected and up.
#define ERROR_OPENVPN_STARTUP
Definition interactive.c:45
static DWORD SetNrptRules(HKEY nrpt_key, const nrpt_address_t *addresses, const char *domains, const char *search_domains, BOOL dnssec, DWORD ovpn_pid)
Set NRPT rules for a openvpn process.
static LSTATUS GetItfDnsServersV6(HKEY itf_key, PSTR addrs, PDWORD size)
Get DNS server IPv6 addresses of an interface.
static DWORD SetNrptRule(HKEY nrpt_key, PCWSTR subkey, PCSTR address, PCWSTR domains, DWORD dom_size, BOOL dnssec)
Set a NRPT rule (subkey) and its values in the registry.
static BOOL AddDnsSearchDomains(HKEY key, BOOL have_list, PCWSTR domains)
Append domain suffixes to an existing search list.
static VOID FreeWaitHandles(LPHANDLE h)
openvpn_service_t interactive_service
Definition interactive.c:60
VOID WINAPI ServiceStartInteractiveOwn(DWORD dwArgc, LPWSTR *lpszArgv)
static DWORD AsyncPipeOp(async_op_t op, HANDLE pipe, LPVOID buffer, DWORD size, DWORD count, LPHANDLE events)
#define IO_TIMEOUT
Definition interactive.c:43
static BOOL ListContainsDomain(PCWSTR list, PCWSTR domain, size_t len)
Check if a domain is contained in a comma separated list of domains.
static BOOL IsDhcpEnabled(HKEY key)
Checks if DHCP is enabled for an interface.
static DWORD HandleFlushNeighborsMessage(flush_neighbors_message_t *msg)
static BOOL ApplyGpolSettings32(void)
Signal the DNS resolver (and others potentially) to reload the group policy (DNS) settings on 32 bit ...
static DWORD HandleMTUMessage(const set_mtu_message_t *mtu)
list_item_t * undo_lists_t[_undo_type_max]
Definition interactive.c:94
static VOID HandleMessage(HANDLE pipe, PPROCESS_INFORMATION proc_info, DWORD bytes, DWORD count, LPHANDLE events, undo_lists_t *lists)
static DWORD HandleRegisterDNSMessage(void)
static void RemoveDnsSearchDomains(HKEY key, PCWSTR domains)
Remove domain suffixes from an existing search list.
static BOOL InitialSearchListExists(HKEY key)
Check if a initial list had already been created.
#define ERROR_MESSAGE_DATA
Definition interactive.c:47
static HANDLE exit_event
Definition interactive.c:52
static VOID FreeStartupData(STARTUP_DATA *sud)
static DWORD HandleWfpBlockMessage(const wfp_block_message_t *msg, undo_lists_t *lists)
static HANDLE rdns_semaphore
Definition interactive.c:54
static DWORD InterfaceLuid(const char *iface_name, PNET_LUID luid)
static BOOL ValidateOptions(HANDLE pipe, const WCHAR *workdir, const WCHAR *options, WCHAR *errmsg, DWORD capacity)
static DWORD UpdateWaitHandles(LPHANDLE *handles_ptr, LPDWORD count, HANDLE io_event, HANDLE exit_event, list_item_t *threads)
static BOOL CmpRoute(LPVOID item, LPVOID route)
static DWORD HandleDNSConfigMessage(const dns_cfg_message_t *msg, undo_lists_t *lists)
static BOOL CmpAny(LPVOID item, LPVOID any)
async_op_t
@ peek
@ write
@ read
static DWORD netsh_wins_cmd(const wchar_t *action, const wchar_t *if_name, const wchar_t *addr)
Run the command: netsh interface ip $action wins $if_name [static] $addr.
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:53
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
interface_t iface
#define _L(q)
Definition basic.h:38
const char * msg2
const char * msg1
char ** res
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:142
BOOL CheckOption(const WCHAR *workdir, int argc, WCHAR *argv[], const settings_t *s)
Definition validate.c:320
static BOOL IsOption(const WCHAR *o)
Definition validate.h:47
int get_interface_metric(const NET_IFINDEX index, const ADDRESS_FAMILY family, int *is_auto)
Return interface metric value for the specified interface index.
Definition wfp_block.c:369
DWORD set_interface_metric(const NET_IFINDEX index, const ADDRESS_FAMILY family, const ULONG metric)
Sets interface metric value for specified interface index.
Definition wfp_block.c:408
DWORD delete_wfp_block_filters(HANDLE engine_handle)
Definition wfp_block.c:344
DWORD add_wfp_block_filters(HANDLE *engine_handle, int index, const WCHAR *exe_path, wfp_block_msg_handler_t msg_handler, BOOL dns_only)
Definition wfp_block.c:153
#define WFP_BLOCK_IFACE_METRIC
Definition wfp_block.h:33
char * get_win_sys_path(void)
Definition win32.c:1108