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