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