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