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
1263static BOOL
1265{
1266 char data[64];
1267 DWORD size = sizeof(data);
1268 LSTATUS err = RegGetValueA(key, NULL, "SearchList", RRF_RT_REG_SZ, NULL, (PBYTE)data, &size);
1269 if (!err || err == ERROR_MORE_DATA)
1270 {
1271 data[sizeof(data) - 1] = '\0';
1272 for (int i = 0; i < strlen(data); ++i)
1273 {
1274 if (isalnum(data[i]) || data[i] == '-' || data[i] == '.')
1275 {
1276 return TRUE;
1277 }
1278 }
1279 }
1280 return FALSE;
1281}
1282
1300static BOOL
1301GetDnsSearchListKey(PCSTR itf_name, PBOOL gpol, PHKEY key)
1302{
1303 LSTATUS err;
1304
1305 *gpol = FALSE;
1306
1307 /* Try the group policy search list */
1308 err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient",
1309 0, KEY_ALL_ACCESS, key);
1310 if (!err)
1311 {
1312 if (HasValidSearchList(*key))
1313 {
1314 *gpol = TRUE;
1315 return TRUE;
1316 }
1317 RegCloseKey(*key);
1318 }
1319
1320 /* Try the system-wide search list */
1321 err =
1322 RegOpenKeyExA(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\TCPIP\\Parameters",
1323 0, KEY_ALL_ACCESS, key);
1324 if (!err)
1325 {
1326 if (HasValidSearchList(*key))
1327 {
1328 return TRUE;
1329 }
1330 RegCloseKey(*key);
1331 }
1332
1333 if (itf_name)
1334 {
1335 /* Always return the VPN interface key (if it exists) */
1336 WCHAR iid[64];
1337 DWORD iid_err = InterfaceIdString(itf_name, iid, _countof(iid));
1338 if (!iid_err)
1339 {
1340 HKEY itfs;
1341 err =
1342 RegOpenKeyExA(HKEY_LOCAL_MACHINE,
1343 "System\\CurrentControlSet\\Services\\TCPIP\\Parameters\\Interfaces",
1344 0, KEY_ALL_ACCESS, &itfs);
1345 if (!err)
1346 {
1347 err = RegOpenKeyExW(itfs, iid, 0, KEY_ALL_ACCESS, key);
1348 RegCloseKey(itfs);
1349 if (!err)
1350 {
1351 return FALSE; /* No need to preserve the VPN itf search list */
1352 }
1353 }
1354 }
1355 }
1356
1357 *key = INVALID_HANDLE_VALUE;
1358 return FALSE;
1359}
1360
1368static BOOL
1370{
1371 LSTATUS err;
1372
1373 err = RegGetValueA(key, NULL, "InitialSearchList", RRF_RT_REG_SZ, NULL, NULL, NULL);
1374 if (err)
1375 {
1376 if (err == ERROR_FILE_NOT_FOUND)
1377 {
1378 return FALSE;
1379 }
1380 MsgToEventLog(M_ERR, L"%S: failed to get InitialSearchList (%lu)", __func__, err);
1381 }
1382
1383 return TRUE;
1384}
1385
1390static DWORD
1391RegWStringSize(PCWSTR string)
1392{
1393 size_t length = (wcslen(string) + 1) * sizeof(wchar_t);
1394 if (length > UINT_MAX)
1395 {
1396 length = UINT_MAX;
1397 }
1398 return (DWORD)length;
1399}
1400
1411static BOOL
1413{
1414 if (!list || wcslen(list) == 0)
1415 {
1416 MsgToEventLog(M_ERR, L"%S: empty search list", __func__);
1417 return FALSE;
1418 }
1419
1421 {
1422 /* Initial list had already been stored */
1423 return TRUE;
1424 }
1425
1426 DWORD size = RegWStringSize(list);
1427 LSTATUS err = RegSetValueExW(key, L"InitialSearchList", 0, REG_SZ, (PBYTE)list, size);
1428 if (err)
1429 {
1430 MsgToEventLog(M_ERR, L"%S: failed to set InitialSearchList value (%lu)", __func__, err);
1431 return FALSE;
1432 }
1433
1434 return TRUE;
1435}
1436
1446static BOOL
1447AddDnsSearchDomains(HKEY key, BOOL have_list, PCWSTR domains)
1448{
1449 LSTATUS err;
1450 WCHAR list[2048] = { 0 };
1451 DWORD size = sizeof(list);
1452
1453 if (have_list)
1454 {
1455 err = RegGetValueW(key, NULL, L"SearchList", RRF_RT_REG_SZ, NULL, list, &size);
1456 if (err)
1457 {
1458 MsgToEventLog(M_SYSERR, L"%S: could not get SearchList from registry (%lu)", __func__,
1459 err);
1460 return FALSE;
1461 }
1462
1463 if (!StoreInitialDnsSearchList(key, list))
1464 {
1465 return FALSE;
1466 }
1467
1468 size_t listlen = (size / sizeof(list[0])) - 1; /* returned size is in bytes */
1469 size_t domlen = wcslen(domains);
1470 if (listlen + domlen + 2 > _countof(list))
1471 {
1472 MsgToEventLog(M_SYSERR, L"%S: not enough space in list for search domains (len=%lu)",
1473 __func__, domlen);
1474 return FALSE;
1475 }
1476
1477 /* Append to end of the search list */
1478 PWSTR pos = list + listlen;
1479 *pos = ',';
1480 wcsncpy(pos + 1, domains, domlen + 1);
1481 }
1482 else
1483 {
1484 wcsncpy(list, domains, wcslen(domains) + 1);
1485 }
1486
1487 size = RegWStringSize(list);
1488 err = RegSetValueExW(key, L"SearchList", 0, REG_SZ, (PBYTE)list, size);
1489 if (err)
1490 {
1491 MsgToEventLog(M_SYSERR, L"%S: could not set SearchList to registry (%lu)", __func__, err);
1492 return FALSE;
1493 }
1494
1495 return TRUE;
1496}
1497
1509static BOOL
1511{
1512 LSTATUS err;
1513 BOOL ret = FALSE;
1514 WCHAR list[2048];
1515 DWORD size = sizeof(list);
1516
1517 err = RegGetValueW(key, NULL, L"InitialSearchList", RRF_RT_REG_SZ, NULL, list, &size);
1518 if (err)
1519 {
1520 if (err != ERROR_FILE_NOT_FOUND)
1521 {
1522 MsgToEventLog(M_SYSERR, L"%S: could not get InitialSearchList from registry (%lu)",
1523 __func__, err);
1524 }
1525 goto out;
1526 }
1527
1528 size = RegWStringSize(list);
1529 err = RegSetValueExW(key, L"SearchList", 0, REG_SZ, (PBYTE)list, size);
1530 if (err)
1531 {
1532 MsgToEventLog(M_SYSERR, L"%S: could not set SearchList in registry (%lu)", __func__, err);
1533 goto out;
1534 }
1535
1536 RegDeleteValueA(key, "InitialSearchList");
1537 ret = TRUE;
1538
1539out:
1540 return ret;
1541}
1542
1549static void
1550RemoveDnsSearchDomains(HKEY key, PCWSTR domains)
1551{
1552 LSTATUS err;
1553 WCHAR list[2048];
1554 DWORD size = sizeof(list);
1555
1556 err = RegGetValueW(key, NULL, L"SearchList", RRF_RT_REG_SZ, NULL, list, &size);
1557 if (err)
1558 {
1559 MsgToEventLog(M_SYSERR, L"%S: could not get SearchList from registry (%lu)", __func__, err);
1560 return;
1561 }
1562
1563 PWSTR dst = wcsstr(list, domains);
1564 if (!dst)
1565 {
1566 MsgToEventLog(M_ERR, L"%S: could not find domains in search list", __func__);
1567 return;
1568 }
1569
1570 /* Cut out domains from list */
1571 size_t domlen = wcslen(domains);
1572 PCWSTR src = dst + domlen;
1573 /* Also remove the leading comma, if there is one */
1574 dst = dst > list ? dst - 1 : dst;
1575 wmemmove(dst, src, domlen);
1576
1577 size_t list_len = wcslen(list);
1578 if (list_len)
1579 {
1580 /* Now check if the shortened list equals the initial search list */
1581 WCHAR initial[2048];
1582 size = sizeof(initial);
1583 err = RegGetValueW(key, NULL, L"InitialSearchList", RRF_RT_REG_SZ, NULL, initial, &size);
1584 if (err)
1585 {
1586 MsgToEventLog(M_SYSERR, L"%S: could not get InitialSearchList from registry (%lu)",
1587 __func__, err);
1588 return;
1589 }
1590
1591 /* If the search list is back to its initial state reset it */
1592 if (wcsncmp(list, initial, list_len) == 0)
1593 {
1595 return;
1596 }
1597 }
1598
1599 size = RegWStringSize(list);
1600 err = RegSetValueExW(key, L"SearchList", 0, REG_SZ, (PBYTE)list, size);
1601 if (err)
1602 {
1603 MsgToEventLog(M_SYSERR, L"%S: could not set SearchList in registry (%lu)", __func__, err);
1604 }
1605}
1606
1612static void
1614{
1615 BOOL gpol;
1616 HKEY dns_searchlist_key;
1617 GetDnsSearchListKey(undo_data->itf_name, &gpol, &dns_searchlist_key);
1618 if (dns_searchlist_key != INVALID_HANDLE_VALUE)
1619 {
1620 RemoveDnsSearchDomains(dns_searchlist_key, undo_data->domains);
1621 RegCloseKey(dns_searchlist_key);
1622 ApplyDnsSettings(gpol);
1623
1624 free(undo_data->domains);
1625 undo_data->domains = NULL;
1626 }
1627}
1628
1650static DWORD
1651SetDnsSearchDomains(PCSTR itf_name, PCSTR domains, PBOOL gpol, undo_lists_t *lists)
1652{
1653 DWORD err = ERROR_OUTOFMEMORY;
1654
1655 HKEY list_key;
1656 BOOL have_list = GetDnsSearchListKey(itf_name, gpol, &list_key);
1657 if (list_key == INVALID_HANDLE_VALUE)
1658 {
1659 MsgToEventLog(M_SYSERR, L"%S: could not get search list registry key", __func__);
1660 return ERROR_FILE_NOT_FOUND;
1661 }
1662
1663 /* Remove previously installed search domains */
1664 dns_domains_undo_data_t *undo_data = RemoveListItem(&(*lists)[undo_domains], CmpAny, NULL);
1665 if (undo_data)
1666 {
1667 RemoveDnsSearchDomains(list_key, undo_data->domains);
1668 free(undo_data->domains);
1669 free(undo_data);
1670 undo_data = NULL;
1671 }
1672
1673 /* If there are search domains, add them */
1674 if (domains && *domains)
1675 {
1676 wchar_t *wide_domains = utf8to16(domains); /* utf8 to wide-char */
1677 if (!wide_domains)
1678 {
1679 goto out;
1680 }
1681
1682 undo_data = malloc(sizeof(*undo_data));
1683 if (!undo_data)
1684 {
1685 free(wide_domains);
1686 wide_domains = NULL;
1687 goto out;
1688 }
1689 strncpy(undo_data->itf_name, itf_name, sizeof(undo_data->itf_name));
1690 undo_data->domains = wide_domains;
1691
1692 if (AddDnsSearchDomains(list_key, have_list, wide_domains) == FALSE
1693 || AddListItem(&(*lists)[undo_domains], undo_data) != NO_ERROR)
1694 {
1695 RemoveDnsSearchDomains(list_key, wide_domains);
1696 free(wide_domains);
1697 free(undo_data);
1698 undo_data = NULL;
1699 goto out;
1700 }
1701 }
1702
1703 err = NO_ERROR;
1704
1705out:
1706 RegCloseKey(list_key);
1707 return err;
1708}
1709
1717static BOOL
1718GetInterfacesKey(short family, PHKEY key)
1719{
1720 PCSTR itfs_key = family == AF_INET6
1721 ? "SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters\\Interfaces"
1722 : "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces";
1723
1724 LSTATUS err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, itfs_key, 0, KEY_ALL_ACCESS, key);
1725 if (err)
1726 {
1727 *key = INVALID_HANDLE_VALUE;
1728 MsgToEventLog(M_SYSERR, L"%S: could not open interfaces registry key for family %d (%lu)",
1729 __func__, family, err);
1730 }
1731
1732 return err ? FALSE : TRUE;
1733}
1734
1744static DWORD
1745SetNameServersValue(PCWSTR itf_id, short family, PCSTR value)
1746{
1747 DWORD err;
1748
1749 HKEY itfs;
1750 if (!GetInterfacesKey(family, &itfs))
1751 {
1752 return ERROR_FILE_NOT_FOUND;
1753 }
1754
1755 HKEY itf = INVALID_HANDLE_VALUE;
1756 err = RegOpenKeyExW(itfs, itf_id, 0, KEY_ALL_ACCESS, &itf);
1757 if (err)
1758 {
1759 MsgToEventLog(M_SYSERR, L"%S: could not open interface key for %s family %d (%lu)",
1760 __func__, itf_id, family, err);
1761 goto out;
1762 }
1763
1764 err = RegSetValueExA(itf, "NameServer", 0, REG_SZ, (PBYTE)value, (DWORD)strlen(value) + 1);
1765 if (err)
1766 {
1767 MsgToEventLog(M_SYSERR, L"%S: could not set name servers '%S' for %s family %d (%lu)",
1768 __func__, value, itf_id, family, err);
1769 }
1770
1771out:
1772 if (itf != INVALID_HANDLE_VALUE)
1773 {
1774 RegCloseKey(itf);
1775 }
1776 if (itfs != INVALID_HANDLE_VALUE)
1777 {
1778 RegCloseKey(itfs);
1779 }
1780 return err;
1781}
1782
1792static DWORD
1793SetNameServers(PCWSTR itf_id, short family, PCSTR addrs)
1794{
1795 return SetNameServersValue(itf_id, family, addrs);
1796}
1797
1806static DWORD
1807ResetNameServers(PCWSTR itf_id, short family)
1808{
1809 return SetNameServersValue(itf_id, family, "");
1810}
1811
1812static DWORD
1814{
1815 DWORD err = 0;
1816 undo_type_t undo_type = (msg->family == AF_INET6) ? undo_dns6 : undo_dns4;
1817 int addr_len = msg->addr_len;
1818
1819 /* sanity check */
1820 const int max_addrs = _countof(msg->addr);
1821 if (addr_len > max_addrs)
1822 {
1823 addr_len = max_addrs;
1824 }
1825
1826 if (!msg->iface.name[0]) /* interface name is required */
1827 {
1828 return ERROR_MESSAGE_DATA;
1829 }
1830
1831 /* use a non-const reference with limited scope to enforce null-termination of strings from
1832 * client */
1833 {
1835 msgptr->iface.name[_countof(msg->iface.name) - 1] = '\0';
1836 msgptr->domains[_countof(msg->domains) - 1] = '\0';
1837 }
1838
1839 WCHAR iid[64];
1840 err = InterfaceIdString(msg->iface.name, iid, _countof(iid));
1841 if (err)
1842 {
1843 return err;
1844 }
1845
1846 /* We delete all current addresses before adding any
1847 * OR if the message type is del_dns_cfg
1848 */
1849 if (addr_len > 0 || msg->header.type == msg_del_dns_cfg)
1850 {
1851 err = ResetNameServers(iid, msg->family);
1852 if (err)
1853 {
1854 return err;
1855 }
1856 free(RemoveListItem(&(*lists)[undo_type], CmpAny, iid));
1857 }
1858
1859 if (msg->header.type == msg_del_dns_cfg)
1860 {
1861 BOOL gpol = FALSE;
1862 if (msg->domains[0])
1863 {
1864 /* setting an empty domain list removes any previous value */
1865 err = SetDnsSearchDomains(msg->iface.name, NULL, &gpol, lists);
1866 }
1867 ApplyDnsSettings(gpol);
1868 return err; /* job done */
1869 }
1870
1871 if (msg->addr_len > 0)
1872 {
1873 /* prepare the comma separated address list */
1874 /* cannot use max_addrs here as that is not considered compile
1875 * time constant by all compilers and constexpr is C23 */
1876 CHAR addrs[_countof(msg->addr) * 64]; /* 64 is enough for one IPv4/6 address */
1877 size_t offset = 0;
1878 for (int i = 0; i < addr_len; ++i)
1879 {
1880 if (i != 0)
1881 {
1882 addrs[offset++] = ',';
1883 }
1884 if (msg->family == AF_INET6)
1885 {
1886 RtlIpv6AddressToStringA(&msg->addr[i].ipv6, addrs + offset);
1887 }
1888 else
1889 {
1890 RtlIpv4AddressToStringA(&msg->addr[i].ipv4, addrs + offset);
1891 }
1892 offset = strlen(addrs);
1893 }
1894
1895 err = SetNameServers(iid, msg->family, addrs);
1896 if (err)
1897 {
1898 return err;
1899 }
1900
1901 wchar_t *tmp_iid = _wcsdup(iid);
1902 if (!tmp_iid || AddListItem(&(*lists)[undo_type], tmp_iid))
1903 {
1904 free(tmp_iid);
1905 ResetNameServers(iid, msg->family);
1906 return ERROR_OUTOFMEMORY;
1907 }
1908 }
1909
1910 BOOL gpol = FALSE;
1911 if (msg->domains[0])
1912 {
1913 err = SetDnsSearchDomains(msg->iface.name, msg->domains, &gpol, lists);
1914 }
1915 ApplyDnsSettings(gpol);
1916
1917 return err;
1918}
1919
1928static BOOL
1930{
1931 DWORD dhcp;
1932 DWORD size = sizeof(dhcp);
1933 LSTATUS err;
1934
1935 err = RegGetValueA(key, NULL, "EnableDHCP", RRF_RT_REG_DWORD, NULL, (PBYTE)&dhcp, &size);
1936 if (err != NO_ERROR)
1937 {
1938 MsgToEventLog(M_SYSERR, L"%S: Could not read DHCP status (%lu)", __func__, err);
1939 return FALSE;
1940 }
1941
1942 return dhcp ? TRUE : FALSE;
1943}
1944
1953static LSTATUS
1954SetNameServerAddresses(PWSTR itf_id, const nrpt_address_t *addresses)
1955{
1956 const short families[] = { AF_INET, AF_INET6 };
1957 for (int i = 0; i < _countof(families); i++)
1958 {
1959 short family = families[i];
1960
1961 /* Create a comma sparated list of addresses of this family */
1962 size_t offset = 0;
1963 char addr_list[NRPT_ADDR_SIZE * NRPT_ADDR_NUM];
1964 for (int j = 0; j < NRPT_ADDR_NUM && addresses[j][0]; j++)
1965 {
1966 if ((family == AF_INET6 && strchr(addresses[j], ':') == NULL)
1967 || (family == AF_INET && strchr(addresses[j], ':') != NULL))
1968 {
1969 /* Address family doesn't match, skip this one */
1970 continue;
1971 }
1972 if (offset)
1973 {
1974 addr_list[offset++] = ',';
1975 }
1976 strcpy(addr_list + offset, addresses[j]);
1977 offset += strlen(addresses[j]);
1978 }
1979
1980 if (offset == 0)
1981 {
1982 /* No address for this family to set */
1983 continue;
1984 }
1985
1986 /* Set name server addresses */
1987 LSTATUS err = SetNameServers(itf_id, family, addr_list);
1988 if (err)
1989 {
1990 return err;
1991 }
1992 }
1993 return NO_ERROR;
1994}
1995
2006static LSTATUS
2007GetItfDnsServersV4(HKEY itf_key, PSTR addrs, PDWORD size)
2008{
2009 addrs[*size - 1] = '\0';
2010
2011 LSTATUS err;
2012 DWORD s = *size;
2013 err = RegGetValueA(itf_key, NULL, "NameServer", RRF_RT_REG_SZ, NULL, (PBYTE)addrs, &s);
2014 if (err && err != ERROR_FILE_NOT_FOUND)
2015 {
2016 *size = 0;
2017 return err;
2018 }
2019
2020 /* Try DHCP addresses if we don't have some already */
2021 if (!strchr(addrs, '.') && IsDhcpEnabled(itf_key))
2022 {
2023 s = *size;
2024 RegGetValueA(itf_key, NULL, "DhcpNameServer", RRF_RT_REG_SZ, NULL, (PBYTE)addrs, &s);
2025 if (err)
2026 {
2027 *size = 0;
2028 return err;
2029 }
2030 }
2031
2032 if (strchr(addrs, '.'))
2033 {
2034 *size = s;
2035 return NO_ERROR;
2036 }
2037
2038 *size = 0;
2039 return ERROR_FILE_NOT_FOUND;
2040}
2041
2051static LSTATUS
2052GetItfDnsServersV6(HKEY itf_key, PSTR addrs, PDWORD size)
2053{
2054 addrs[*size - 1] = '\0';
2055
2056 LSTATUS err;
2057 DWORD s = *size;
2058 err = RegGetValueA(itf_key, NULL, "NameServer", RRF_RT_REG_SZ, NULL, (PBYTE)addrs, &s);
2059 if (err && err != ERROR_FILE_NOT_FOUND)
2060 {
2061 *size = 0;
2062 return err;
2063 }
2064
2065 /* Try DHCP addresses if we don't have some already */
2066 if (!strchr(addrs, ':') && IsDhcpEnabled(itf_key))
2067 {
2068 IN6_ADDR in_addrs[8];
2069 DWORD in_addrs_size = sizeof(in_addrs);
2070 err = RegGetValueA(itf_key, NULL, "Dhcpv6DNSServers", RRF_RT_REG_BINARY, NULL,
2071 (PBYTE)in_addrs, &in_addrs_size);
2072 if (err)
2073 {
2074 *size = 0;
2075 return err;
2076 }
2077
2078 s = *size;
2079 PSTR pos = addrs;
2080 size_t in_addrs_read = in_addrs_size / sizeof(IN6_ADDR);
2081 for (size_t i = 0; i < in_addrs_read; ++i)
2082 {
2083 if (i != 0)
2084 {
2085 /* Add separator */
2086 *pos++ = ',';
2087 s--;
2088 }
2089
2090 if (inet_ntop(AF_INET6, &in_addrs[i], pos, s) != NULL)
2091 {
2092 *size = 0;
2093 return ERROR_MORE_DATA;
2094 }
2095
2096 size_t addr_len = strlen(pos);
2097 pos += addr_len;
2098 s -= (DWORD)addr_len;
2099 }
2100 s = (DWORD)strlen(addrs) + 1;
2101 }
2102
2103 if (strchr(addrs, ':'))
2104 {
2105 *size = s;
2106 return NO_ERROR;
2107 }
2108
2109 *size = 0;
2110 return ERROR_FILE_NOT_FOUND;
2111}
2112
2122static BOOL
2123ListContainsDomain(PCWSTR list, PCWSTR domain, size_t len)
2124{
2125 PCWSTR match = list;
2126 while (match)
2127 {
2128 match = wcsstr(match, domain);
2129 if (!match)
2130 {
2131 /* Domain has not matched */
2132 break;
2133 }
2134 if ((match == list || *(match - 1) == ',')
2135 && (*(match + len) == ',' || *(match + len) == '\0'))
2136 {
2137 /* Domain has matched fully */
2138 return TRUE;
2139 }
2140 match += len;
2141 }
2142 return FALSE;
2143}
2144
2166static LSTATUS
2167ConvertItfDnsDomains(PCWSTR search_domains, PWSTR domains, PDWORD size, const DWORD buf_size)
2168{
2169 const DWORD glyph_size = sizeof(*domains);
2170 const DWORD buf_len = buf_size / glyph_size;
2171
2172 /*
2173 * Found domain(s), now convert them:
2174 * - prefix each domain with a dot
2175 * - convert comma separated list to MULTI_SZ
2176 */
2177 PWCHAR pos = domains;
2178 while (TRUE)
2179 {
2180 /* Terminate the domain at the next comma */
2181 PWCHAR comma = wcschr(pos, ',');
2182 if (comma)
2183 {
2184 *comma = '\0';
2185 }
2186
2187 DWORD domain_len = (DWORD)wcslen(pos);
2188 DWORD domain_size = domain_len * glyph_size;
2189 DWORD converted_size = (DWORD)(pos - domains) * glyph_size;
2190
2191 /* Ignore itf domains which match a pushed search domain */
2192 if (ListContainsDomain(search_domains, pos, domain_len))
2193 {
2194 if (comma)
2195 {
2196 /* Overwrite the ignored domain with remaining one(s) */
2197 memmove(pos, comma + 1, buf_size - converted_size);
2198 *size -= domain_size + glyph_size;
2199 continue;
2200 }
2201 else
2202 {
2203 /* This was the last domain */
2204 *pos = '\0';
2205 *size -= domain_size;
2206 return wcslen(domains) ? NO_ERROR : ERROR_FILE_NOT_FOUND;
2207 }
2208 }
2209
2210 /* Add space for the leading dot */
2211 domain_len += 1;
2212 domain_size += glyph_size;
2213
2214 /* Space for the terminating zeros */
2215 const DWORD extra_size = 2 * glyph_size;
2216
2217 /* Check for enough space to convert this domain */
2218 if (converted_size + domain_size + extra_size > buf_size)
2219 {
2220 /* Domain doesn't fit, bad luck if it's the first one */
2221 *pos = '\0';
2222 *size = converted_size == 0 ? 0 : converted_size + glyph_size;
2223 return ERROR_MORE_DATA;
2224 }
2225
2226 /* Prefix domain at pos with the dot */
2227 memmove(pos + 1, pos, buf_size - converted_size - glyph_size);
2228 domains[buf_len - 1] = '\0';
2229 *pos = '.';
2230 *size += glyph_size;
2231
2232 if (!comma)
2233 {
2234 /* Conversion is done */
2235 *(pos + domain_len) = '\0';
2236 *size += glyph_size;
2237 return NO_ERROR;
2238 }
2239
2240 /* Comma pos is now +1 after adding leading dot */
2241 pos = comma + 2;
2242 }
2243}
2244
2266static LSTATUS
2267GetItfDnsDomains(HKEY itf, PCWSTR search_domains, PWSTR domains, PDWORD size)
2268{
2269 if (domains == NULL || size == NULL || *size == 0)
2270 {
2271 return ERROR_INVALID_PARAMETER;
2272 }
2273
2274 LSTATUS err = ERROR_FILE_NOT_FOUND;
2275 const DWORD buf_size = *size;
2276 const DWORD glyph_size = sizeof(*domains);
2277 PWSTR values[] = { L"SearchList", L"Domain", L"DhcpDomainSearchList", L"DhcpDomain", NULL };
2278
2279 for (int i = 0; values[i]; i++)
2280 {
2281 *size = buf_size;
2282 err = RegGetValueW(itf, NULL, values[i], RRF_RT_REG_SZ, NULL, (PBYTE)domains, size);
2283 if (!err && *size > glyph_size && domains[(*size / glyph_size) - 1] == '\0' && wcschr(domains, '.'))
2284 {
2285 return ConvertItfDnsDomains(search_domains, domains, size, buf_size);
2286 }
2287 }
2288
2289 *size = 0;
2290 return err;
2291}
2292
2301static BOOL
2303{
2304 GUID iid;
2305 BOOL res = FALSE;
2306 MIB_IF_ROW2 itf_row;
2307
2308 /* Get GUID from string */
2309 if (IIDFromString(iid_str, &iid) != S_OK)
2310 {
2311 MsgToEventLog(M_SYSERR, L"%S: could not convert interface %s GUID string", __func__,
2312 iid_str);
2313 goto out;
2314 }
2315
2316 /* Get LUID from GUID */
2317 if (ConvertInterfaceGuidToLuid(&iid, &itf_row.InterfaceLuid) != NO_ERROR)
2318 {
2319 goto out;
2320 }
2321
2322 /* Look up interface status */
2323 if (GetIfEntry2(&itf_row) != NO_ERROR)
2324 {
2325 MsgToEventLog(M_SYSERR, L"%S: could not get interface %s status", __func__, iid_str);
2326 goto out;
2327 }
2328
2329 if (itf_row.MediaConnectState == MediaConnectStateConnected
2330 && itf_row.OperStatus == IfOperStatusUp)
2331 {
2332 res = TRUE;
2333 }
2334
2335out:
2336 return res;
2337}
2338
2348static void
2349GetNrptExcludeData(PCWSTR search_domains, nrpt_exclude_data_t *data, size_t data_size)
2350{
2351 HKEY v4_itfs = INVALID_HANDLE_VALUE;
2352 HKEY v6_itfs = INVALID_HANDLE_VALUE;
2353
2354 if (!GetInterfacesKey(AF_INET, &v4_itfs) || !GetInterfacesKey(AF_INET6, &v6_itfs))
2355 {
2356 goto out;
2357 }
2358
2359 size_t i = 0;
2360 DWORD enum_index = 0;
2361 while (i < data_size)
2362 {
2363 WCHAR itf_guid[MAX_PATH];
2364 DWORD itf_guid_len = _countof(itf_guid);
2365 LSTATUS err =
2366 RegEnumKeyExW(v4_itfs, enum_index++, itf_guid, &itf_guid_len, NULL, NULL, NULL, NULL);
2367 if (err)
2368 {
2369 if (err != ERROR_NO_MORE_ITEMS)
2370 {
2371 MsgToEventLog(M_SYSERR, L"%S: could not enumerate interfaces (%lu)", __func__, err);
2372 }
2373 goto out;
2374 }
2375
2376 /* Ignore interfaces that are not connected or disabled */
2377 if (!IsInterfaceConnected(itf_guid))
2378 {
2379 continue;
2380 }
2381
2382 HKEY v4_itf;
2383 if (RegOpenKeyExW(v4_itfs, itf_guid, 0, KEY_READ, &v4_itf) != NO_ERROR)
2384 {
2385 MsgToEventLog(M_SYSERR, L"%S: could not open interface %s v4 registry key", __func__,
2386 itf_guid);
2387 goto out;
2388 }
2389
2390 /* Get the DNS domain(s) for exclude routing */
2391 data[i].domains_size = sizeof(data[0].domains);
2392 memset(data[i].domains, 0, data[i].domains_size);
2393 err = GetItfDnsDomains(v4_itf, search_domains, data[i].domains, &data[i].domains_size);
2394 if (err)
2395 {
2396 if (err != ERROR_FILE_NOT_FOUND)
2397 {
2398 MsgToEventLog(M_SYSERR, L"%S: could not read interface %s domain suffix", __func__,
2399 itf_guid);
2400 }
2401 goto next_itf;
2402 }
2403
2404 /* Get the IPv4 DNS servers */
2405 DWORD v4_addrs_size = sizeof(data[0].addresses);
2406 err = GetItfDnsServersV4(v4_itf, data[i].addresses, &v4_addrs_size);
2407 if (err && err != ERROR_FILE_NOT_FOUND)
2408 {
2409 MsgToEventLog(M_SYSERR, L"%S: could not read interface %s v4 name servers (%ld)",
2410 __func__, itf_guid, err);
2411 goto next_itf;
2412 }
2413
2414 /* Get the IPv6 DNS servers, if there's space left */
2415 PSTR v6_addrs = data[i].addresses + v4_addrs_size;
2416 DWORD v6_addrs_size = sizeof(data[0].addresses) - v4_addrs_size;
2417 if (v6_addrs_size > NRPT_ADDR_SIZE)
2418 {
2419 HKEY v6_itf;
2420 if (RegOpenKeyExW(v6_itfs, itf_guid, 0, KEY_READ, &v6_itf) != NO_ERROR)
2421 {
2422 MsgToEventLog(M_SYSERR, L"%S: could not open interface %s v6 registry key",
2423 __func__, itf_guid);
2424 goto next_itf;
2425 }
2426 err = GetItfDnsServersV6(v6_itf, v6_addrs, &v6_addrs_size);
2427 RegCloseKey(v6_itf);
2428 if (err && err != ERROR_FILE_NOT_FOUND)
2429 {
2430 MsgToEventLog(M_SYSERR, L"%S: could not read interface %s v6 name servers (%ld)",
2431 __func__, itf_guid, err);
2432 goto next_itf;
2433 }
2434 }
2435
2436 if (v4_addrs_size || v6_addrs_size)
2437 {
2438 /* Replace delimiters with semicolons, as required by NRPT */
2439 for (int j = 0; j < sizeof(data[0].addresses) && data[i].addresses[j]; j++)
2440 {
2441 if (data[i].addresses[j] == ',' || data[i].addresses[j] == ' ')
2442 {
2443 data[i].addresses[j] = ';';
2444 }
2445 }
2446 ++i;
2447 }
2448
2449next_itf:
2450 RegCloseKey(v4_itf);
2451 }
2452
2453out:
2454 RegCloseKey(v6_itfs);
2455 RegCloseKey(v4_itfs);
2456}
2457
2470static DWORD
2471SetNrptRule(HKEY nrpt_key, PCWSTR subkey, PCSTR address, PCWSTR domains, DWORD dom_size,
2472 BOOL dnssec)
2473{
2474 /* Create rule subkey */
2475 DWORD err = NO_ERROR;
2476 HKEY rule_key;
2477 err = RegCreateKeyExW(nrpt_key, subkey, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &rule_key, NULL);
2478 if (err)
2479 {
2480 return err;
2481 }
2482
2483 /* Set name(s) for DNS routing */
2484 err = RegSetValueExW(rule_key, L"Name", 0, REG_MULTI_SZ, (PBYTE)domains, dom_size);
2485 if (err)
2486 {
2487 goto out;
2488 }
2489
2490 /* Set DNS Server address */
2491 err = RegSetValueExA(rule_key, "GenericDNSServers", 0, REG_SZ, (PBYTE)address,
2492 (DWORD)strlen(address) + 1);
2493 if (err)
2494 {
2495 goto out;
2496 }
2497
2498 DWORD reg_val;
2499 /* Set DNSSEC if required */
2500 if (dnssec)
2501 {
2502 reg_val = 1;
2503 err = RegSetValueExA(rule_key, "DNSSECValidationRequired", 0, REG_DWORD, (PBYTE)&reg_val,
2504 sizeof(reg_val));
2505 if (err)
2506 {
2507 goto out;
2508 }
2509
2510 reg_val = 0;
2511 err = RegSetValueExA(rule_key, "DNSSECQueryIPSECRequired", 0, REG_DWORD, (PBYTE)&reg_val,
2512 sizeof(reg_val));
2513 if (err)
2514 {
2515 goto out;
2516 }
2517
2518 reg_val = 0;
2519 err = RegSetValueExA(rule_key, "DNSSECQueryIPSECEncryption", 0, REG_DWORD, (PBYTE)&reg_val,
2520 sizeof(reg_val));
2521 if (err)
2522 {
2523 goto out;
2524 }
2525 }
2526
2527 /* Set NRPT config options */
2528 reg_val = dnssec ? 0x0000000A : 0x00000008;
2529 err = RegSetValueExA(rule_key, "ConfigOptions", 0, REG_DWORD, (const PBYTE)&reg_val,
2530 sizeof(reg_val));
2531 if (err)
2532 {
2533 goto out;
2534 }
2535
2536 /* Mandatory NRPT version */
2537 reg_val = 2;
2538 err = RegSetValueExA(rule_key, "Version", 0, REG_DWORD, (const PBYTE)&reg_val, sizeof(reg_val));
2539 if (err)
2540 {
2541 goto out;
2542 }
2543
2544out:
2545 if (err)
2546 {
2547 RegDeleteKeyW(nrpt_key, subkey);
2548 }
2549 RegCloseKey(rule_key);
2550 return err;
2551}
2552
2562static void
2563SetNrptExcludeRules(HKEY nrpt_key, DWORD ovpn_pid, PCWSTR search_domains)
2564{
2565 nrpt_exclude_data_t data[8]; /* data from up to 8 interfaces */
2566 memset(data, 0, sizeof(data));
2567 GetNrptExcludeData(search_domains, data, _countof(data));
2568
2569 unsigned n = 0;
2570 for (int i = 0; i < _countof(data); ++i)
2571 {
2572 nrpt_exclude_data_t *d = &data[i];
2573 if (d->domains_size == 0)
2574 {
2575 break;
2576 }
2577
2578 DWORD err;
2579 WCHAR subkey[48];
2580 swprintf(subkey, _countof(subkey), L"OpenVPNDNSRoutingX-%02x-%lu", ++n, ovpn_pid);
2581 err = SetNrptRule(nrpt_key, subkey, d->addresses, d->domains, d->domains_size, FALSE);
2582 if (err)
2583 {
2584 MsgToEventLog(M_ERR, L"%S: failed to set rule %s (%lu)", __func__, subkey, err);
2585 }
2586 }
2587}
2588
2601static DWORD
2602SetNrptRules(HKEY nrpt_key, const nrpt_address_t *addresses, const char *domains,
2603 const char *search_domains, BOOL dnssec, DWORD ovpn_pid)
2604{
2605 DWORD err = NO_ERROR;
2606 PWSTR wide_domains = L".\0"; /* DNS route everything by default */
2607 DWORD dom_size = 6;
2608
2609 /* Prepare DNS routing domains / split DNS */
2610 if (domains[0])
2611 {
2612 size_t domains_len = strlen(domains);
2613 dom_size = (DWORD)domains_len + 2; /* len + the trailing NULs */
2614
2615 wide_domains = utf8to16_size(domains, dom_size);
2616 dom_size *= sizeof(*wide_domains);
2617 if (!wide_domains)
2618 {
2619 return ERROR_OUTOFMEMORY;
2620 }
2621 /* Make a MULTI_SZ from a comma separated list */
2622 for (size_t i = 0; i < domains_len; ++i)
2623 {
2624 if (wide_domains[i] == ',')
2625 {
2626 wide_domains[i] = 0;
2627 }
2628 }
2629 }
2630 else
2631 {
2632 PWSTR wide_search_domains;
2633 wide_search_domains = utf8to16(search_domains);
2634 if (!wide_search_domains)
2635 {
2636 return ERROR_OUTOFMEMORY;
2637 }
2638 SetNrptExcludeRules(nrpt_key, ovpn_pid, wide_search_domains);
2639 free(wide_search_domains);
2640 }
2641
2642 /* Create address string list */
2643 CHAR addr_list[NRPT_ADDR_NUM * NRPT_ADDR_SIZE];
2644 PSTR pos = addr_list;
2645 for (int i = 0; i < NRPT_ADDR_NUM && addresses[i][0]; ++i)
2646 {
2647 if (i != 0)
2648 {
2649 *pos++ = ';';
2650 }
2651 strcpy(pos, addresses[i]);
2652 pos += strlen(pos);
2653 }
2654
2655 WCHAR subkey[MAX_PATH];
2656 swprintf(subkey, _countof(subkey), L"OpenVPNDNSRouting-%lu", ovpn_pid);
2657 err = SetNrptRule(nrpt_key, subkey, addr_list, wide_domains, dom_size, dnssec);
2658 if (err)
2659 {
2660 MsgToEventLog(M_ERR, L"%S: failed to set rule %s (%lu)", __func__, subkey, err);
2661 }
2662
2663 if (domains[0])
2664 {
2665 free(wide_domains);
2666 }
2667 return err;
2668}
2669
2678static LSTATUS
2679OpenNrptBaseKey(PHKEY key, PBOOL gpol)
2680{
2681 /*
2682 * Registry keys Name Service Policy Table (NRPT) rules can be stored at.
2683 * When the group policy key exists, NRPT rules must be placed there.
2684 * It is created when NRPT rules are pushed via group policy and it
2685 * remains in the registry even if the last GP-NRPT rule is deleted.
2686 */
2687 static PCSTR gpol_key = "SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient\\DnsPolicyConfig";
2688 static PCSTR sys_key =
2689 "SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters\\DnsPolicyConfig";
2690
2691 HKEY nrpt;
2692 *gpol = TRUE;
2693 LSTATUS err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, gpol_key, 0, KEY_ALL_ACCESS, &nrpt);
2694 if (err == ERROR_FILE_NOT_FOUND)
2695 {
2696 *gpol = FALSE;
2697 err = RegCreateKeyExA(HKEY_LOCAL_MACHINE, sys_key, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &nrpt,
2698 NULL);
2699 if (err)
2700 {
2701 nrpt = INVALID_HANDLE_VALUE;
2702 }
2703 }
2704 *key = nrpt;
2705 return err;
2706}
2707
2719static BOOL
2720DeleteNrptRules(DWORD pid, PBOOL gpol)
2721{
2722 HKEY key;
2723 LSTATUS err = OpenNrptBaseKey(&key, gpol);
2724 if (err)
2725 {
2726 MsgToEventLog(M_SYSERR, L"%S: could not open NRPT base key (%lu)", __func__, err);
2727 return FALSE;
2728 }
2729
2730 /* PID suffix string to compare against later */
2731 WCHAR pid_str[16];
2732 size_t pidlen = 0;
2733 if (pid)
2734 {
2735 swprintf(pid_str, _countof(pid_str), L"-%lu", pid);
2736 pidlen = wcslen(pid_str);
2737 }
2738
2739 int deleted = 0;
2740 DWORD enum_index = 0;
2741 while (TRUE)
2742 {
2743 WCHAR name[MAX_PATH];
2744 DWORD namelen = _countof(name);
2745 err = RegEnumKeyExW(key, enum_index++, name, &namelen, NULL, NULL, NULL, NULL);
2746 if (err)
2747 {
2748 if (err != ERROR_NO_MORE_ITEMS)
2749 {
2750 MsgToEventLog(M_SYSERR, L"%S: could not enumerate NRPT rules (%lu)", __func__, err);
2751 }
2752 break;
2753 }
2754
2755 /* Keep rule if name doesn't match */
2756 if (wcsncmp(name, L"OpenVPNDNSRouting", 17) != 0
2757 || (pid && wcsncmp(name + namelen - pidlen, pid_str, pidlen) != 0))
2758 {
2759 continue;
2760 }
2761
2762 if (RegDeleteKeyW(key, name) == NO_ERROR)
2763 {
2764 enum_index--;
2765 deleted++;
2766 }
2767 }
2768
2769 RegCloseKey(key);
2770 return deleted ? TRUE : FALSE;
2771}
2772
2778static void
2779UndoNrptRules(DWORD ovpn_pid)
2780{
2781 BOOL gpol;
2782 if (DeleteNrptRules(ovpn_pid, &gpol))
2783 {
2784 ApplyDnsSettings(gpol);
2785 }
2786}
2787
2799static DWORD
2801{
2802 /*
2803 * Use a non-const reference with limited scope to
2804 * enforce null-termination of strings from client
2805 */
2806 {
2808 msgptr->iface.name[_countof(msg->iface.name) - 1] = '\0';
2809 msgptr->search_domains[_countof(msg->search_domains) - 1] = '\0';
2810 msgptr->resolve_domains[_countof(msg->resolve_domains) - 1] = '\0';
2811 for (size_t i = 0; i < NRPT_ADDR_NUM; ++i)
2812 {
2813 msgptr->addresses[i][_countof(msg->addresses[0]) - 1] = '\0';
2814 }
2815 }
2816
2817 /* Make sure we have the VPN interface name */
2818 if (msg->iface.name[0] == 0)
2819 {
2820 return ERROR_MESSAGE_DATA;
2821 }
2822
2823 /* Some sanity checks on the add message data */
2824 if (msg->header.type == msg_add_nrpt_cfg)
2825 {
2826 /* At least one name server address is set */
2827 if (msg->addresses[0][0] == 0)
2828 {
2829 return ERROR_MESSAGE_DATA;
2830 }
2831 /* Resolve domains are double zero terminated (MULTI_SZ) */
2832 const char *rdom = msg->resolve_domains;
2833 size_t rdom_size = sizeof(msg->resolve_domains);
2834 size_t rdom_len = strlen(rdom);
2835 if (rdom_len && (rdom_len + 1 >= rdom_size || rdom[rdom_len + 2] != 0))
2836 {
2837 return ERROR_MESSAGE_DATA;
2838 }
2839 }
2840
2841 BOOL gpol_nrpt = FALSE;
2842 BOOL gpol_list = FALSE;
2843
2844 WCHAR iid[64];
2845 DWORD iid_err = InterfaceIdString(msg->iface.name, iid, _countof(iid));
2846 if (iid_err)
2847 {
2848 return iid_err;
2849 }
2850
2851 /* Delete previously set values for this instance first, if any */
2852 PDWORD undo_pid = RemoveListItem(&(*lists)[undo_nrpt], CmpAny, NULL);
2853 if (undo_pid)
2854 {
2855 if (*undo_pid != ovpn_pid)
2856 {
2858 L"%S: PID stored for undo doesn't match: %lu vs %lu. "
2859 "This is likely an error. Cleaning up anyway.",
2860 __func__, *undo_pid, ovpn_pid);
2861 }
2862 DeleteNrptRules(*undo_pid, &gpol_nrpt);
2863 free(undo_pid);
2864
2865 ResetNameServers(iid, AF_INET);
2866 ResetNameServers(iid, AF_INET6);
2867 }
2868 SetDnsSearchDomains(msg->iface.name, NULL, &gpol_list, lists);
2869
2870 if (msg->header.type == msg_del_nrpt_cfg)
2871 {
2872 ApplyDnsSettings(gpol_nrpt || gpol_list);
2873 return NO_ERROR; /* Done dealing with del message */
2874 }
2875
2876 HKEY key;
2877 LSTATUS err = OpenNrptBaseKey(&key, &gpol_nrpt);
2878 if (err)
2879 {
2880 goto out;
2881 }
2882
2883 /* Add undo information first in case there's no heap left */
2884 PDWORD pid = malloc(sizeof(ovpn_pid));
2885 if (!pid)
2886 {
2887 err = ERROR_OUTOFMEMORY;
2888 goto out;
2889 }
2890 *pid = ovpn_pid;
2891 if (AddListItem(&(*lists)[undo_nrpt], pid))
2892 {
2893 err = ERROR_OUTOFMEMORY;
2894 free(pid);
2895 goto out;
2896 }
2897
2898 /* Set NRPT rules */
2899 BOOL dnssec = (msg->flags & nrpt_dnssec) != 0;
2900 err = SetNrptRules(key, msg->addresses, msg->resolve_domains, msg->search_domains, dnssec,
2901 ovpn_pid);
2902 if (err)
2903 {
2904 goto out;
2905 }
2906
2907 /*
2908 * Set DNS on the adapter for search domains to be considered.
2909 * If split DNS is configured, do this only when search domains
2910 * are given, so that look-ups for other domains do not go over
2911 * the VPN all the time.
2912 */
2913 if (msg->search_domains[0] || !msg->resolve_domains[0])
2914 {
2915 err = SetNameServerAddresses(iid, msg->addresses);
2916 if (err)
2917 {
2918 goto out;
2919 }
2920 }
2921
2922 /* Set search domains, if any */
2923 if (msg->search_domains[0])
2924 {
2925 err = SetDnsSearchDomains(msg->iface.name, msg->search_domains, &gpol_list, lists);
2926 }
2927
2928 ApplyDnsSettings(gpol_nrpt || gpol_list);
2929
2930out:
2931 return err;
2932}
2933
2934static DWORD
2936{
2937 DWORD err = NO_ERROR;
2938 wchar_t addr[16]; /* large enough to hold string representation of an ipv4 */
2939 int addr_len = msg->addr_len;
2940
2941 /* sanity check */
2942 if (addr_len > _countof(msg->addr))
2943 {
2944 addr_len = _countof(msg->addr);
2945 }
2946
2947 if (!msg->iface.index) /* interface index is required */
2948 {
2949 return ERROR_MESSAGE_DATA;
2950 }
2951
2952 /* We delete all current addresses before adding any
2953 * OR if the message type is del_wins_cfg
2954 */
2955 if (addr_len > 0 || msg->header.type == msg_del_wins_cfg)
2956 {
2957 err = netsh_wins_cmd(L"delete", msg->iface.index, NULL);
2958 if (err)
2959 {
2960 goto out;
2961 }
2962 free(RemoveListItem(&(*lists)[undo_wins], CmpAny, NULL));
2963 }
2964
2965 if (addr_len == 0 || msg->header.type == msg_del_wins_cfg)
2966 {
2967 goto out; /* job done */
2968 }
2969
2970 for (int i = 0; i < addr_len; ++i)
2971 {
2972 RtlIpv4AddressToStringW(&msg->addr[i].ipv4, addr);
2973 err = netsh_wins_cmd(i == 0 ? L"set" : L"add", msg->iface.index, addr);
2974 if (i == 0 && err)
2975 {
2976 goto out;
2977 }
2978 /* We do not check for duplicate addresses, so any error in adding
2979 * additional addresses is ignored.
2980 */
2981 }
2982
2983 int *if_index = malloc(sizeof(msg->iface.index));
2984 if (if_index)
2985 {
2986 *if_index = msg->iface.index;
2987 }
2988
2989 if (!if_index || AddListItem(&(*lists)[undo_wins], if_index))
2990 {
2991 free(if_index);
2992 netsh_wins_cmd(L"delete", msg->iface.index, NULL);
2993 err = ERROR_OUTOFMEMORY;
2994 goto out;
2995 }
2996
2997 err = 0;
2998
2999out:
3000 return err;
3001}
3002
3003static DWORD
3005{
3006 DWORD err = 0;
3007 DWORD timeout = 5000; /* in milli seconds */
3008 wchar_t argv0[MAX_PATH];
3009
3010 /* Path of netsh */
3011 swprintf(argv0, _countof(argv0), L"%ls\\%ls", get_win_sys_path(), L"netsh.exe");
3012
3013 /* cmd template:
3014 * netsh interface ipv4 set address name=$if_index source=dhcp
3015 */
3016 const wchar_t *fmt = L"netsh interface ipv4 set address name=\"%d\" source=dhcp";
3017
3018 /* max cmdline length in wchars -- include room for if index:
3019 * 10 chars for 32 bit int in decimal and +1 for NUL
3020 */
3021 size_t ncmdline = wcslen(fmt) + 10 + 1;
3022 wchar_t *cmdline = malloc(ncmdline * sizeof(wchar_t));
3023 if (!cmdline)
3024 {
3025 err = ERROR_OUTOFMEMORY;
3026 return err;
3027 }
3028
3029 swprintf(cmdline, ncmdline, fmt, dhcp->iface.index);
3030
3031 err = ExecCommand(argv0, cmdline, timeout);
3032
3033 /* Note: This could fail if dhcp is already enabled, so the caller
3034 * may not want to treat errors as FATAL.
3035 */
3036
3037 free(cmdline);
3038 return err;
3039}
3040
3041static DWORD
3043{
3044 DWORD err = 0;
3045 MIB_IPINTERFACE_ROW ipiface;
3046 InitializeIpInterfaceEntry(&ipiface);
3047 ipiface.Family = mtu->family;
3048 ipiface.InterfaceIndex = mtu->iface.index;
3049 err = GetIpInterfaceEntry(&ipiface);
3050 if (err != NO_ERROR)
3051 {
3052 return err;
3053 }
3054 if (mtu->family == AF_INET)
3055 {
3056 ipiface.SitePrefixLength = 0;
3057 }
3058 ipiface.NlMtu = mtu->mtu;
3059
3060 err = SetIpInterfaceEntry(&ipiface);
3061 return err;
3062}
3063
3071static DWORD
3073{
3074 const WCHAR *hwid;
3075
3076 switch (msg->adapter_type)
3077 {
3078 case ADAPTER_TYPE_DCO:
3079 hwid = L"ovpn-dco";
3080 break;
3081
3082 case ADAPTER_TYPE_TAP:
3083 hwid = L"root\\tap0901";
3084 break;
3085
3086 default:
3087 return ERROR_INVALID_PARAMETER;
3088 }
3089
3090 WCHAR cmd[MAX_PATH];
3091 WCHAR args[MAX_PATH];
3092
3093 if (swprintf_s(cmd, _countof(cmd), L"%s\\tapctl.exe", settings.bin_dir) < 0)
3094 {
3095 return ERROR_BUFFER_OVERFLOW;
3096 }
3097
3098 if (swprintf_s(args, _countof(args), L"tapctl create --hwid %s", hwid) < 0)
3099 {
3100 return ERROR_BUFFER_OVERFLOW;
3101 }
3102
3103 return ExecCommand(cmd, args, 10000);
3104}
3105
3106static VOID
3107HandleMessage(HANDLE pipe, PPROCESS_INFORMATION proc_info, DWORD bytes, DWORD count,
3108 LPHANDLE events, undo_lists_t *lists)
3109{
3111 ack_message_t ack = {
3112 .header = { .type = msg_acknowledgement, .size = sizeof(ack), .message_id = -1 },
3113 .error_number = ERROR_MESSAGE_DATA
3114 };
3115
3116 DWORD read = ReadPipeAsync(pipe, &msg, bytes, count, events);
3117 if (read != bytes || read < sizeof(msg.header) || read != msg.header.size)
3118 {
3119 goto out;
3120 }
3121
3122 ack.header.message_id = msg.header.message_id;
3123
3124 switch (msg.header.type)
3125 {
3126 case msg_add_address:
3127 case msg_del_address:
3128 if (msg.header.size == sizeof(msg.address))
3129 {
3130 ack.error_number = HandleAddressMessage(&msg.address, lists);
3131 }
3132 break;
3133
3134 case msg_add_route:
3135 case msg_del_route:
3136 if (msg.header.size == sizeof(msg.route))
3137 {
3138 ack.error_number = HandleRouteMessage(&msg.route, lists);
3139 }
3140 break;
3141
3143 if (msg.header.size == sizeof(msg.flush_neighbors))
3144 {
3145 ack.error_number = HandleFlushNeighborsMessage(&msg.flush_neighbors);
3146 }
3147 break;
3148
3149 case msg_add_wfp_block:
3150 case msg_del_wfp_block:
3151 if (msg.header.size == sizeof(msg.wfp_block))
3152 {
3153 ack.error_number = HandleWfpBlockMessage(&msg.wfp_block, lists);
3154 }
3155 break;
3156
3157 case msg_register_dns:
3159 break;
3160
3161 case msg_add_dns_cfg:
3162 case msg_del_dns_cfg:
3163 ack.error_number = HandleDNSConfigMessage(&msg.dns, lists);
3164 break;
3165
3166 case msg_add_nrpt_cfg:
3167 case msg_del_nrpt_cfg:
3168 {
3169 DWORD ovpn_pid = proc_info->dwProcessId;
3170 ack.error_number = HandleDNSConfigNrptMessage(&msg.nrpt_dns, ovpn_pid, lists);
3171 }
3172 break;
3173
3174 case msg_add_wins_cfg:
3175 case msg_del_wins_cfg:
3176 ack.error_number = HandleWINSConfigMessage(&msg.wins, lists);
3177 break;
3178
3179 case msg_enable_dhcp:
3180 if (msg.header.size == sizeof(msg.dhcp))
3181 {
3183 }
3184 break;
3185
3186 case msg_set_mtu:
3187 if (msg.header.size == sizeof(msg.mtu))
3188 {
3189 ack.error_number = HandleMTUMessage(&msg.mtu);
3190 }
3191 break;
3192
3193 case msg_create_adapter:
3194 if (msg.header.size == sizeof(msg.create_adapter))
3195 {
3196 ack.error_number = HandleCreateAdapterMessage(&msg.create_adapter);
3197 }
3198 break;
3199
3200 default:
3202 MsgToEventLog(MSG_FLAGS_ERROR, L"Unknown message type %d", msg.header.type);
3203 break;
3204 }
3205
3206out:
3207 WritePipeAsync(pipe, &ack, sizeof(ack), count, events);
3208}
3209
3210
3211static VOID
3213{
3214 undo_type_t type;
3215 wfp_block_data_t *interface_data;
3216 for (type = 0; type < _undo_type_max; type++)
3217 {
3218 list_item_t **pnext = &(*lists)[type];
3219 while (*pnext)
3220 {
3221 list_item_t *item = *pnext;
3222 switch (type)
3223 {
3224 case address:
3225 DeleteAddress(item->data);
3226 break;
3227
3228 case route:
3229 DeleteRoute(item->data);
3230 break;
3231
3232 case undo_dns4:
3233 ResetNameServers(item->data, AF_INET);
3234 break;
3235
3236 case undo_dns6:
3237 ResetNameServers(item->data, AF_INET6);
3238 break;
3239
3240 case undo_nrpt:
3241 UndoNrptRules(*(PDWORD)item->data);
3242 break;
3243
3244 case undo_domains:
3246 break;
3247
3248 case undo_wins:
3249 netsh_wins_cmd(L"delete", *(int *)item->data, NULL);
3250 break;
3251
3252 case wfp_block:
3253 interface_data = (wfp_block_data_t *)(item->data);
3254 delete_wfp_block_filters(interface_data->engine);
3255 if (interface_data->metric_v4 >= 0)
3256 {
3257 set_interface_metric(interface_data->index, AF_INET,
3258 interface_data->metric_v4);
3259 }
3260 if (interface_data->metric_v6 >= 0)
3261 {
3262 set_interface_metric(interface_data->index, AF_INET6,
3263 interface_data->metric_v6);
3264 }
3265 break;
3266
3267 case _undo_type_max:
3268 /* unreachable */
3269 break;
3270 }
3271
3272 /* Remove from the list and free memory */
3273 *pnext = item->next;
3274 free(item->data);
3275 free(item);
3276 }
3277 }
3278}
3279
3280static DWORD WINAPI
3281RunOpenvpn(LPVOID p)
3282{
3283 HANDLE pipe = p;
3284 HANDLE ovpn_pipe = NULL, svc_pipe = NULL;
3285 PTOKEN_USER svc_user = NULL, ovpn_user = NULL;
3286 HANDLE svc_token = NULL, imp_token = NULL, pri_token = NULL;
3287 HANDLE stdin_read = NULL, stdin_write = NULL;
3288 HANDLE stdout_write = NULL;
3289 DWORD pipe_mode, len, exit_code = 0;
3290 STARTUP_DATA sud = { 0, 0, 0 };
3291 STARTUPINFOW startup_info;
3292 PROCESS_INFORMATION proc_info;
3293 LPVOID user_env = NULL;
3294 WCHAR ovpn_pipe_name[256]; /* The entire pipe name string can be up to 256 characters long
3295 according to MSDN. */
3296 LPCWSTR exe_path;
3297 WCHAR *cmdline = NULL;
3298 size_t cmdline_size;
3299 undo_lists_t undo_lists;
3300 WCHAR errmsg[512] = L"";
3301 BOOL flush_pipe = TRUE;
3302
3303 SECURITY_ATTRIBUTES inheritable = { .nLength = sizeof(inheritable),
3304 .lpSecurityDescriptor = NULL,
3305 .bInheritHandle = TRUE };
3306
3307 PACL ovpn_dacl;
3308 EXPLICIT_ACCESS ea[2];
3309 SECURITY_DESCRIPTOR ovpn_sd;
3310 SECURITY_ATTRIBUTES ovpn_sa = { .nLength = sizeof(ovpn_sa),
3311 .lpSecurityDescriptor = &ovpn_sd,
3312 .bInheritHandle = FALSE };
3313
3314 ZeroMemory(&ea, sizeof(ea));
3315 ZeroMemory(&startup_info, sizeof(startup_info));
3316 ZeroMemory(&undo_lists, sizeof(undo_lists));
3317 ZeroMemory(&proc_info, sizeof(proc_info));
3318
3319 if (!GetStartupData(pipe, &sud))
3320 {
3321 flush_pipe = FALSE; /* client did not provide startup data */
3322 goto out;
3323 }
3324
3325 if (!InitializeSecurityDescriptor(&ovpn_sd, SECURITY_DESCRIPTOR_REVISION))
3326 {
3327 ReturnLastError(pipe, L"InitializeSecurityDescriptor");
3328 goto out;
3329 }
3330
3331 /* Get SID of user the service is running under */
3332 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &svc_token))
3333 {
3334 ReturnLastError(pipe, L"OpenProcessToken");
3335 goto out;
3336 }
3337 len = 0;
3338 while (!GetTokenInformation(svc_token, TokenUser, svc_user, len, &len))
3339 {
3340 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
3341 {
3342 ReturnLastError(pipe, L"GetTokenInformation (service token)");
3343 goto out;
3344 }
3345 free(svc_user);
3346 svc_user = malloc(len);
3347 if (svc_user == NULL)
3348 {
3349 ReturnLastError(pipe, L"malloc (service token user)");
3350 goto out;
3351 }
3352 }
3353 if (!IsValidSid(svc_user->User.Sid))
3354 {
3355 ReturnLastError(pipe, L"IsValidSid (service token user)");
3356 goto out;
3357 }
3358
3359 if (!ImpersonateNamedPipeClient(pipe))
3360 {
3361 ReturnLastError(pipe, L"ImpersonateNamedPipeClient");
3362 goto out;
3363 }
3364 if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &imp_token))
3365 {
3366 ReturnLastError(pipe, L"OpenThreadToken");
3367 goto out;
3368 }
3369 len = 0;
3370 while (!GetTokenInformation(imp_token, TokenUser, ovpn_user, len, &len))
3371 {
3372 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
3373 {
3374 ReturnLastError(pipe, L"GetTokenInformation (impersonation token)");
3375 goto out;
3376 }
3377 free(ovpn_user);
3378 ovpn_user = malloc(len);
3379 if (ovpn_user == NULL)
3380 {
3381 ReturnLastError(pipe, L"malloc (impersonation token user)");
3382 goto out;
3383 }
3384 }
3385 if (!IsValidSid(ovpn_user->User.Sid))
3386 {
3387 ReturnLastError(pipe, L"IsValidSid (impersonation token user)");
3388 goto out;
3389 }
3390
3391 /*
3392 * Only authorized users are allowed to use any command line options or
3393 * have the config file in locations other than the global config directory.
3394 *
3395 * Check options are white-listed and config is in the global directory
3396 * OR user is authorized to run any config.
3397 */
3398 if (!ValidateOptions(pipe, sud.directory, sud.options, errmsg, _countof(errmsg))
3399 && !IsAuthorizedUser(ovpn_user->User.Sid, imp_token, settings.ovpn_admin_group,
3401 {
3402 ReturnError(pipe, ERROR_STARTUP_DATA, errmsg, 1, &exit_event);
3403 goto out;
3404 }
3405
3406 /* OpenVPN process DACL entry for access by service and user */
3407 ea[0].grfAccessPermissions = SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL;
3408 ea[0].grfAccessMode = SET_ACCESS;
3409 ea[0].grfInheritance = NO_INHERITANCE;
3410 ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
3411 ea[0].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
3412 ea[0].Trustee.ptstrName = (LPWSTR)svc_user->User.Sid;
3413 ea[1].grfAccessPermissions = READ_CONTROL | SYNCHRONIZE | PROCESS_VM_READ | SYNCHRONIZE
3414 | PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION;
3415 ea[1].grfAccessMode = SET_ACCESS;
3416 ea[1].grfInheritance = NO_INHERITANCE;
3417 ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
3418 ea[1].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
3419 ea[1].Trustee.ptstrName = (LPWSTR)ovpn_user->User.Sid;
3420
3421 /* Set owner and DACL of OpenVPN security descriptor */
3422 if (!SetSecurityDescriptorOwner(&ovpn_sd, svc_user->User.Sid, FALSE))
3423 {
3424 ReturnLastError(pipe, L"SetSecurityDescriptorOwner");
3425 goto out;
3426 }
3427 if (SetEntriesInAcl(2, ea, NULL, &ovpn_dacl) != ERROR_SUCCESS)
3428 {
3429 ReturnLastError(pipe, L"SetEntriesInAcl");
3430 goto out;
3431 }
3432 if (!SetSecurityDescriptorDacl(&ovpn_sd, TRUE, ovpn_dacl, FALSE))
3433 {
3434 ReturnLastError(pipe, L"SetSecurityDescriptorDacl");
3435 goto out;
3436 }
3437
3438 /* Create primary token from impersonation token */
3439 if (!DuplicateTokenEx(imp_token, TOKEN_ALL_ACCESS, NULL, 0, TokenPrimary, &pri_token))
3440 {
3441 ReturnLastError(pipe, L"DuplicateTokenEx");
3442 goto out;
3443 }
3444
3445 /* use /dev/null for stdout of openvpn (client should use --log for output) */
3446 stdout_write = CreateFile(_L("NUL"), GENERIC_WRITE, FILE_SHARE_WRITE, &inheritable,
3447 OPEN_EXISTING, 0, NULL);
3448 if (stdout_write == INVALID_HANDLE_VALUE)
3449 {
3450 ReturnLastError(pipe, L"CreateFile for stdout");
3451 goto out;
3452 }
3453
3454 if (!CreatePipe(&stdin_read, &stdin_write, &inheritable, 0)
3455 || !SetHandleInformation(stdin_write, HANDLE_FLAG_INHERIT, 0))
3456 {
3457 ReturnLastError(pipe, L"CreatePipe");
3458 goto out;
3459 }
3460
3461 UUID pipe_uuid;
3462 RPC_STATUS rpc_stat = UuidCreate(&pipe_uuid);
3463 if (rpc_stat != RPC_S_OK)
3464 {
3465 ReturnError(pipe, rpc_stat, L"UuidCreate", 1, &exit_event);
3466 goto out;
3467 }
3468
3469 RPC_WSTR pipe_uuid_str = NULL;
3470 rpc_stat = UuidToStringW(&pipe_uuid, &pipe_uuid_str);
3471 if (rpc_stat != RPC_S_OK)
3472 {
3473 ReturnError(pipe, rpc_stat, L"UuidToString", 1, &exit_event);
3474 goto out;
3475 }
3476 swprintf(ovpn_pipe_name, _countof(ovpn_pipe_name),
3477 L"\\\\.\\pipe\\" _L(PACKAGE) L"%ls\\service_%lu_%ls", service_instance,
3478 GetCurrentThreadId(), pipe_uuid_str);
3479 RpcStringFree(&pipe_uuid_str);
3480
3481 /* make a security descriptor for the named pipe with access
3482 * restricted to the user and SYSTEM
3483 */
3484
3485 SECURITY_ATTRIBUTES sa;
3486 PSECURITY_DESCRIPTOR pSD = NULL;
3487 LPCWSTR szSDDL = L"D:(A;;GA;;;SY)(A;;GA;;;OW)";
3488 if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(
3489 szSDDL, SDDL_REVISION_1, &pSD, NULL))
3490 {
3491 ReturnLastError(pipe, L"ConvertSDDL");
3492 goto out;
3493 }
3494 sa.nLength = sizeof(sa);
3495 sa.lpSecurityDescriptor = pSD;
3496 sa.bInheritHandle = FALSE;
3497
3498 ovpn_pipe = CreateNamedPipe(
3499 ovpn_pipe_name, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED,
3500 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS, 1, 128, 128, 0, &sa);
3501 if (ovpn_pipe == INVALID_HANDLE_VALUE)
3502 {
3503 ReturnLastError(pipe, L"CreateNamedPipe");
3504 goto out;
3505 }
3506
3507 svc_pipe = CreateFile(ovpn_pipe_name, GENERIC_READ | GENERIC_WRITE, 0, &inheritable,
3508 OPEN_EXISTING, 0, NULL);
3509 if (svc_pipe == INVALID_HANDLE_VALUE)
3510 {
3511 ReturnLastError(pipe, L"CreateFile");
3512 goto out;
3513 }
3514
3515 pipe_mode = PIPE_READMODE_MESSAGE;
3516 if (!SetNamedPipeHandleState(svc_pipe, &pipe_mode, NULL, NULL))
3517 {
3518 ReturnLastError(pipe, L"SetNamedPipeHandleState");
3519 goto out;
3520 }
3521
3522 cmdline_size = wcslen(sud.options) + 128;
3523 cmdline = malloc(cmdline_size * sizeof(*cmdline));
3524 if (cmdline == NULL)
3525 {
3526 ReturnLastError(pipe, L"malloc");
3527 goto out;
3528 }
3529 /* there seem to be no common printf specifier that works on all
3530 * mingw/msvc platforms without trickery, so convert to void* and use
3531 * PRIuPTR to print that as best compromise */
3532 swprintf(cmdline, cmdline_size, L"openvpn %ls --msg-channel %" PRIuPTR, sud.options,
3533 (uintptr_t)svc_pipe);
3534
3535 if (!CreateEnvironmentBlock(&user_env, imp_token, FALSE))
3536 {
3537 ReturnLastError(pipe, L"CreateEnvironmentBlock");
3538 goto out;
3539 }
3540
3541 startup_info.cb = sizeof(startup_info);
3542 startup_info.dwFlags = STARTF_USESTDHANDLES;
3543 startup_info.hStdInput = stdin_read;
3544 startup_info.hStdOutput = stdout_write;
3545 startup_info.hStdError = stdout_write;
3546
3547 exe_path = settings.exe_path;
3548
3549 /* TODO: make sure HKCU is correct or call LoadUserProfile() */
3550 if (!CreateProcessAsUserW(pri_token, exe_path, cmdline, &ovpn_sa, NULL, TRUE,
3551 settings.priority | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
3552 user_env, sud.directory, &startup_info, &proc_info))
3553 {
3554 ReturnLastError(pipe, L"CreateProcessAsUser");
3555 goto out;
3556 }
3557
3558 if (!RevertToSelf())
3559 {
3560 TerminateProcess(proc_info.hProcess, 1);
3561 ReturnLastError(pipe, L"RevertToSelf");
3562 goto out;
3563 }
3564
3565 ReturnProcessId(pipe, proc_info.dwProcessId, 1, &exit_event);
3566
3567 CloseHandleEx(&stdout_write);
3568 CloseHandleEx(&stdin_read);
3569 CloseHandleEx(&svc_pipe);
3570
3571 DWORD input_size = WideCharToMultiByte(CP_UTF8, 0, sud.std_input, -1, NULL, 0, NULL, NULL);
3572 LPSTR input = NULL;
3573 if (input_size && (input = malloc(input_size)))
3574 {
3575 DWORD written;
3576 WideCharToMultiByte(CP_UTF8, 0, sud.std_input, -1, input, input_size, NULL, NULL);
3577 WriteFile(stdin_write, input, (DWORD)strlen(input), &written, NULL);
3578 free(input);
3579 }
3580
3581 while (TRUE)
3582 {
3583 DWORD bytes = PeekNamedPipeAsync(ovpn_pipe, 1, &exit_event);
3584 if (bytes == 0)
3585 {
3586 break;
3587 }
3588
3589 if (bytes > sizeof(pipe_message_t))
3590 {
3591 /* process at the other side of the pipe is misbehaving, shut it down */
3594 L"OpenVPN process sent too large payload length to the pipe (%lu bytes), it will be terminated",
3595 bytes);
3596 break;
3597 }
3598
3599 HandleMessage(ovpn_pipe, &proc_info, bytes, 1, &exit_event, &undo_lists);
3600 }
3601
3602 WaitForSingleObject(proc_info.hProcess, IO_TIMEOUT);
3603 GetExitCodeProcess(proc_info.hProcess, &exit_code);
3604 if (exit_code == STILL_ACTIVE)
3605 {
3606 TerminateProcess(proc_info.hProcess, 1);
3607 }
3608 else if (exit_code != 0)
3609 {
3610 WCHAR buf[256];
3611 swprintf(buf, _countof(buf), L"OpenVPN exited with error: exit code = %lu", exit_code);
3613 }
3614 Undo(&undo_lists);
3615
3616out:
3617 if (flush_pipe)
3618 {
3619 FlushFileBuffers(pipe);
3620 }
3621 DisconnectNamedPipe(pipe);
3622
3623 free(ovpn_user);
3624 free(svc_user);
3625 free(cmdline);
3626 DestroyEnvironmentBlock(user_env);
3627 FreeStartupData(&sud);
3628 CloseHandleEx(&proc_info.hProcess);
3629 CloseHandleEx(&proc_info.hThread);
3630 CloseHandleEx(&stdin_read);
3631 CloseHandleEx(&stdin_write);
3632 CloseHandleEx(&stdout_write);
3633 CloseHandleEx(&svc_token);
3634 CloseHandleEx(&imp_token);
3635 CloseHandleEx(&pri_token);
3636 CloseHandleEx(&ovpn_pipe);
3637 CloseHandleEx(&svc_pipe);
3638 CloseHandleEx(&pipe);
3639
3640 return 0;
3641}
3642
3643
3644static DWORD WINAPI
3645ServiceCtrlInteractive(DWORD ctrl_code, DWORD event, LPVOID data, LPVOID ctx)
3646{
3647 SERVICE_STATUS *status = ctx;
3648 switch (ctrl_code)
3649 {
3650 case SERVICE_CONTROL_STOP:
3651 status->dwCurrentState = SERVICE_STOP_PENDING;
3653 if (exit_event)
3654 {
3655 SetEvent(exit_event);
3656 }
3657 return NO_ERROR;
3658
3659 case SERVICE_CONTROL_INTERROGATE:
3660 return NO_ERROR;
3661
3662 default:
3663 return ERROR_CALL_NOT_IMPLEMENTED;
3664 }
3665}
3666
3667
3668static HANDLE
3670{
3671 /*
3672 * allow all access for local system
3673 * deny FILE_CREATE_PIPE_INSTANCE for everyone
3674 * allow read/write for authenticated users
3675 * deny all access to anonymous
3676 */
3677 const WCHAR *sddlString =
3678 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)";
3679
3680 PSECURITY_DESCRIPTOR sd = NULL;
3681 if (!ConvertStringSecurityDescriptorToSecurityDescriptor(sddlString, SDDL_REVISION_1, &sd,
3682 NULL))
3683 {
3684 MsgToEventLog(M_SYSERR, L"ConvertStringSecurityDescriptorToSecurityDescriptor failed.");
3685 return INVALID_HANDLE_VALUE;
3686 }
3687
3688 /* Set up SECURITY_ATTRIBUTES */
3689 SECURITY_ATTRIBUTES sa = { 0 };
3690 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
3691 sa.lpSecurityDescriptor = sd;
3692 sa.bInheritHandle = FALSE;
3693
3694 DWORD flags = PIPE_ACCESS_DUPLEX | WRITE_DAC | FILE_FLAG_OVERLAPPED;
3695
3696 static BOOL first = TRUE;
3697 if (first)
3698 {
3699 flags |= FILE_FLAG_FIRST_PIPE_INSTANCE;
3700 first = FALSE;
3701 }
3702
3703 WCHAR pipe_name[256]; /* The entire pipe name string can be up to 256 characters long according
3704 to MSDN. */
3705 swprintf(pipe_name, _countof(pipe_name), L"\\\\.\\pipe\\" _L(PACKAGE) L"%ls\\service",
3707 HANDLE pipe = CreateNamedPipe(
3708 pipe_name, flags, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
3709 PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0, &sa);
3710
3711 LocalFree(sd);
3712
3713 if (pipe == INVALID_HANDLE_VALUE)
3714 {
3715 MsgToEventLog(M_SYSERR, L"Could not create named pipe");
3716 return INVALID_HANDLE_VALUE;
3717 }
3718
3719 return pipe;
3720}
3721
3722
3723static DWORD
3724UpdateWaitHandles(LPHANDLE *handles_ptr, LPDWORD count, HANDLE io_event, HANDLE exit_event,
3725 list_item_t *threads)
3726{
3727 static DWORD size = 10;
3728 static LPHANDLE handles = NULL;
3729 DWORD pos = 0;
3730
3731 if (handles == NULL)
3732 {
3733 handles = malloc(size * sizeof(HANDLE));
3734 *handles_ptr = handles;
3735 if (handles == NULL)
3736 {
3737 return ERROR_OUTOFMEMORY;
3738 }
3739 }
3740
3741 handles[pos++] = io_event;
3742
3743 if (!threads)
3744 {
3745 handles[pos++] = exit_event;
3746 }
3747
3748 while (threads)
3749 {
3750 if (pos == size)
3751 {
3752 LPHANDLE tmp;
3753 size += 10;
3754 tmp = realloc(handles, size * sizeof(HANDLE));
3755 if (tmp == NULL)
3756 {
3757 size -= 10;
3758 *count = pos;
3759 return ERROR_OUTOFMEMORY;
3760 }
3761 handles = tmp;
3762 *handles_ptr = handles;
3763 }
3764 handles[pos++] = threads->data;
3765 threads = threads->next;
3766 }
3767
3768 *count = pos;
3769 return NO_ERROR;
3770}
3771
3772
3773static VOID
3775{
3776 free(h);
3777}
3778
3779static BOOL
3780CmpHandle(LPVOID item, LPVOID hnd)
3781{
3782 return item == hnd;
3783}
3784
3785
3786VOID WINAPI
3787ServiceStartInteractiveOwn(DWORD dwArgc, LPWSTR *lpszArgv)
3788{
3789 status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
3790 ServiceStartInteractive(dwArgc, lpszArgv);
3791}
3792
3798static void
3800{
3801 BOOL changed = FALSE;
3802
3803 /* Clean up leftover NRPT rules */
3804 BOOL gpol_nrpt;
3805 changed = DeleteNrptRules(0, &gpol_nrpt);
3806
3807 /* Clean up leftover DNS search list fragments */
3808 HKEY key;
3809 BOOL gpol_list;
3810 GetDnsSearchListKey(NULL, &gpol_list, &key);
3811 if (key != INVALID_HANDLE_VALUE)
3812 {
3814 {
3815 changed = TRUE;
3816 }
3817 RegCloseKey(key);
3818 }
3819
3820 if (changed)
3821 {
3822 ApplyDnsSettings(gpol_nrpt || gpol_list);
3823 }
3824}
3825
3826VOID WINAPI
3827ServiceStartInteractive(DWORD dwArgc, LPWSTR *lpszArgv)
3828{
3829 HANDLE pipe, io_event = NULL;
3830 OVERLAPPED overlapped;
3831 DWORD error = NO_ERROR;
3832 list_item_t *threads = NULL;
3833 PHANDLE handles = NULL;
3834 DWORD handle_count;
3835
3836 service =
3837 RegisterServiceCtrlHandlerEx(interactive_service.name, ServiceCtrlInteractive, &status);
3838 if (!service)
3839 {
3840 return;
3841 }
3842
3843 status.dwCurrentState = SERVICE_START_PENDING;
3844 status.dwServiceSpecificExitCode = NO_ERROR;
3845 status.dwWin32ExitCode = NO_ERROR;
3846 status.dwWaitHint = 3000;
3848
3849 /* Clean up potentially left over registry values */
3851
3852 /* Read info from registry in key HKLM\SOFTWARE\OpenVPN */
3853 error = GetOpenvpnSettings(&settings);
3854 if (error != ERROR_SUCCESS)
3855 {
3856 goto out;
3857 }
3858
3859 io_event = InitOverlapped(&overlapped);
3860 exit_event = CreateEvent(NULL, TRUE, FALSE, NULL);
3861 if (!exit_event || !io_event)
3862 {
3863 error = MsgToEventLog(M_SYSERR, L"Could not create event");
3864 goto out;
3865 }
3866
3867 rdns_semaphore = CreateSemaphoreW(NULL, 1, 1, NULL);
3868 if (!rdns_semaphore)
3869 {
3870 error = MsgToEventLog(M_SYSERR, L"Could not create semaphore for register-dns");
3871 goto out;
3872 }
3873
3874 error = UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
3875 if (error != NO_ERROR)
3876 {
3877 goto out;
3878 }
3879
3880 pipe = CreateClientPipeInstance();
3881 if (pipe == INVALID_HANDLE_VALUE)
3882 {
3883 goto out;
3884 }
3885
3886 status.dwCurrentState = SERVICE_RUNNING;
3887 status.dwWaitHint = 0;
3889
3890 while (TRUE)
3891 {
3892 if (!ConnectNamedPipe(pipe, &overlapped))
3893 {
3894 DWORD connect_error = GetLastError();
3895 if (connect_error == ERROR_NO_DATA)
3896 {
3897 /*
3898 * Client connected and disconnected before we could process it.
3899 * Disconnect and retry instead of aborting the service.
3900 */
3901 MsgToEventLog(M_ERR, L"ConnectNamedPipe returned ERROR_NO_DATA (client dropped)");
3902 DisconnectNamedPipe(pipe);
3903 ResetOverlapped(&overlapped);
3904 continue;
3905 }
3906 else if (connect_error == ERROR_PIPE_CONNECTED)
3907 {
3908 /* No async I/O pending in this case; signal manually. */
3909 SetEvent(overlapped.hEvent);
3910 }
3911 else if (connect_error != ERROR_IO_PENDING)
3912 {
3913 MsgToEventLog(M_SYSERR, L"Could not connect pipe");
3914 break;
3915 }
3916 }
3917
3918 error = WaitForMultipleObjects(handle_count, handles, FALSE, INFINITE);
3919 if (error == WAIT_OBJECT_0)
3920 {
3921 /* Client connected, spawn a worker thread for it */
3922 HANDLE next_pipe = CreateClientPipeInstance();
3923
3924 /* Avoid exceeding WaitForMultipleObjects MAXIMUM_WAIT_OBJECTS */
3925 if (handle_count + 1 > MAXIMUM_WAIT_OBJECTS)
3926 {
3927 ReturnError(pipe, ERROR_CANT_WAIT, L"Too many concurrent clients", 1, &exit_event);
3928 CloseHandleEx(&pipe);
3929 pipe = next_pipe;
3930 ResetOverlapped(&overlapped);
3931 continue;
3932 }
3933
3934 HANDLE thread = CreateThread(NULL, 0, RunOpenvpn, pipe, CREATE_SUSPENDED, NULL);
3935 if (thread)
3936 {
3937 error = AddListItem(&threads, thread);
3938 if (!error)
3939 {
3940 error =
3941 UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
3942 }
3943 if (error)
3944 {
3945 ReturnError(pipe, error, L"Insufficient resources to service new clients", 1,
3946 &exit_event);
3947 /* Update wait handles again after removing the last worker thread */
3948 RemoveListItem(&threads, CmpHandle, thread);
3949 UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
3950 TerminateThread(thread, 1);
3951 CloseHandleEx(&thread);
3952 CloseHandleEx(&pipe);
3953 }
3954 else
3955 {
3956 ResumeThread(thread);
3957 }
3958 }
3959 else
3960 {
3961 CloseHandleEx(&pipe);
3962 }
3963
3964 ResetOverlapped(&overlapped);
3965 pipe = next_pipe;
3966 }
3967 else
3968 {
3969 CancelIo(pipe);
3970 if (error == WAIT_FAILED)
3971 {
3972 MsgToEventLog(M_SYSERR, L"WaitForMultipleObjects failed");
3973 SetEvent(exit_event);
3974 /* Give some time for worker threads to exit and then terminate */
3975 Sleep(1000);
3976 break;
3977 }
3978 if (!threads)
3979 {
3980 /* exit event signaled */
3981 CloseHandleEx(&pipe);
3982 ResetEvent(exit_event);
3983 error = NO_ERROR;
3984 break;
3985 }
3986
3987 /* Worker thread ended */
3988 HANDLE thread = RemoveListItem(&threads, CmpHandle, handles[error]);
3989 UpdateWaitHandles(&handles, &handle_count, io_event, exit_event, threads);
3990 CloseHandleEx(&thread);
3991 }
3992 }
3993
3994out:
3995 FreeWaitHandles(handles);
3996 CloseHandleEx(&io_event);
3999
4000 status.dwCurrentState = SERVICE_STOPPED;
4001 status.dwWin32ExitCode = error;
4003}
static int buf_len(const struct buffer *buf)
Definition buffer.h:253
wchar_t * utf8to16_size(const char *utf8, int size)
Convert a UTF-8 string to UTF-16.
Definition common.c: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:369
DWORD set_interface_metric(const NET_IFINDEX index, const ADDRESS_FAMILY family, const ULONG metric)
Sets interface metric value for specified interface index.
Definition wfp_block.c:408
DWORD delete_wfp_block_filters(HANDLE engine_handle)
Definition wfp_block.c:344
DWORD add_wfp_block_filters(HANDLE *engine_handle, int index, const WCHAR *exe_path, wfp_block_msg_handler_t msg_handler, BOOL dns_only)
Definition wfp_block.c:153
#define WFP_BLOCK_IFACE_METRIC
Definition wfp_block.h:33
char * get_win_sys_path(void)
Definition win32.c:1109