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