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