1 /*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single 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) 2002-2022 OpenVPN Inc <sales@openvpn.net>
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 * Win32-specific OpenVPN code, targeted at the mingw
26 * development environment.
27 */
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #elif defined(_MSC_VER)
32 #include "config-msvc.h"
33 #endif
34
35 #include "syshead.h"
36
37 #ifdef _WIN32
38
39 #include "buffer.h"
40 #include "error.h"
41 #include "mtu.h"
42 #include "run_command.h"
43 #include "sig.h"
44 #include "win32.h"
45 #include "openvpn-msg.h"
46
47 #include "memdbg.h"
48
49 #ifdef HAVE_VERSIONHELPERS_H
50 #include <versionhelpers.h>
51 #else
52 #include "compat-versionhelpers.h"
53 #endif
54
55 #include "block_dns.h"
56
57 /*
58 * WFP handle
59 */
60 static HANDLE m_hEngineHandle = NULL; /* GLOBAL */
61
62 /*
63 * TAP adapter original metric value
64 */
65 static int tap_metric_v4 = -1; /* GLOBAL */
66 static int tap_metric_v6 = -1; /* GLOBAL */
67
68 /*
69 * Windows internal socket API state (opaque).
70 */
71 static struct WSAData wsa_state; /* GLOBAL */
72
73 /*
74 * Should we call win32_pause() on program exit?
75 */
76 static bool pause_exit_enabled = false; /* GLOBAL */
77
78 /*
79 * win32_signal is used to get input from the keyboard
80 * if we are running in a console, or get input from an
81 * event object if we are running as a service.
82 */
83
84 struct win32_signal win32_signal; /* GLOBAL */
85
86 /*
87 * Save our old window title so we can restore
88 * it on exit.
89 */
90 struct window_title window_title; /* GLOBAL*/
91
92 /*
93 * Special global semaphore used to protect network
94 * shell commands from simultaneous instantiation.
95 */
96
97 struct semaphore netcmd_semaphore; /* GLOBAL */
98
99 /*
100 * Windows system pathname such as c:\windows
101 */
102 static char *win_sys_path = NULL; /* GLOBAL */
103
104 /**
105 * Set OpenSSL environment variables to a safe directory
106 */
107 static void
108 set_openssl_env_vars();
109
110 void
init_win32(void)111 init_win32(void)
112 {
113 if (WSAStartup(0x0101, &wsa_state))
114 {
115 msg(M_ERR, "WSAStartup failed");
116 }
117 window_title_clear(&window_title);
118 win32_signal_clear(&win32_signal);
119
120 set_openssl_env_vars();
121 }
122
123 void
uninit_win32(void)124 uninit_win32(void)
125 {
126 netcmd_semaphore_close();
127 if (pause_exit_enabled)
128 {
129 if (win32_signal.mode == WSO_MODE_UNDEF)
130 {
131 struct win32_signal w;
132 win32_signal_open(&w, WSO_FORCE_CONSOLE, NULL, false);
133 win32_pause(&w);
134 win32_signal_close(&w);
135 }
136 else
137 {
138 win32_pause(&win32_signal);
139 }
140 }
141 window_title_restore(&window_title);
142 win32_signal_close(&win32_signal);
143 WSACleanup();
144 free(win_sys_path);
145 }
146
147 void
set_pause_exit_win32(void)148 set_pause_exit_win32(void)
149 {
150 pause_exit_enabled = true;
151 }
152
153 bool
init_security_attributes_allow_all(struct security_attributes * obj)154 init_security_attributes_allow_all(struct security_attributes *obj)
155 {
156 CLEAR(*obj);
157
158 obj->sa.nLength = sizeof(SECURITY_ATTRIBUTES);
159 obj->sa.lpSecurityDescriptor = &obj->sd;
160 obj->sa.bInheritHandle = FALSE;
161 if (!InitializeSecurityDescriptor(&obj->sd, SECURITY_DESCRIPTOR_REVISION))
162 {
163 return false;
164 }
165 if (!SetSecurityDescriptorDacl(&obj->sd, TRUE, NULL, FALSE))
166 {
167 return false;
168 }
169 return true;
170 }
171
172 void
overlapped_io_init(struct overlapped_io * o,const struct frame * frame,BOOL event_state,bool tuntap_buffer)173 overlapped_io_init(struct overlapped_io *o,
174 const struct frame *frame,
175 BOOL event_state,
176 bool tuntap_buffer) /* if true: tuntap buffer, if false: socket buffer */
177 {
178 CLEAR(*o);
179
180 /* manual reset event, initially set according to event_state */
181 o->overlapped.hEvent = CreateEvent(NULL, TRUE, event_state, NULL);
182 if (o->overlapped.hEvent == NULL)
183 {
184 msg(M_ERR, "Error: overlapped_io_init: CreateEvent failed");
185 }
186
187 /* allocate buffer for overlapped I/O */
188 alloc_buf_sock_tun(&o->buf_init, frame, tuntap_buffer, 0);
189 }
190
191 void
overlapped_io_close(struct overlapped_io * o)192 overlapped_io_close(struct overlapped_io *o)
193 {
194 if (o->overlapped.hEvent)
195 {
196 if (!CloseHandle(o->overlapped.hEvent))
197 {
198 msg(M_WARN | M_ERRNO, "Warning: CloseHandle failed on overlapped I/O event object");
199 }
200 }
201 free_buf(&o->buf_init);
202 }
203
204 char *
overlapped_io_state_ascii(const struct overlapped_io * o)205 overlapped_io_state_ascii(const struct overlapped_io *o)
206 {
207 switch (o->iostate)
208 {
209 case IOSTATE_INITIAL:
210 return "0";
211
212 case IOSTATE_QUEUED:
213 return "Q";
214
215 case IOSTATE_IMMEDIATE_RETURN:
216 return "1";
217 }
218 return "?";
219 }
220
221 /*
222 * Event-based notification of network events
223 */
224
225 void
init_net_event_win32(struct rw_handle * event,long network_events,socket_descriptor_t sd,unsigned int flags)226 init_net_event_win32(struct rw_handle *event, long network_events, socket_descriptor_t sd, unsigned int flags)
227 {
228 /* manual reset events, initially set to unsignaled */
229
230 /* initialize write event */
231 if (!(flags & NE32_PERSIST_EVENT) || !event->write)
232 {
233 if (flags & NE32_WRITE_EVENT)
234 {
235 event->write = CreateEvent(NULL, TRUE, FALSE, NULL);
236 if (event->write == NULL)
237 {
238 msg(M_ERR, "Error: init_net_event_win32: CreateEvent (write) failed");
239 }
240 }
241 else
242 {
243 event->write = NULL;
244 }
245 }
246
247 /* initialize read event */
248 if (!(flags & NE32_PERSIST_EVENT) || !event->read)
249 {
250 event->read = CreateEvent(NULL, TRUE, FALSE, NULL);
251 if (event->read == NULL)
252 {
253 msg(M_ERR, "Error: init_net_event_win32: CreateEvent (read) failed");
254 }
255 }
256
257 /* setup network events to change read event state */
258 if (WSAEventSelect(sd, event->read, network_events) != 0)
259 {
260 msg(M_FATAL | M_ERRNO, "Error: init_net_event_win32: WSAEventSelect call failed");
261 }
262 }
263
264 long
reset_net_event_win32(struct rw_handle * event,socket_descriptor_t sd)265 reset_net_event_win32(struct rw_handle *event, socket_descriptor_t sd)
266 {
267 WSANETWORKEVENTS wne;
268 if (WSAEnumNetworkEvents(sd, event->read, &wne) != 0)
269 {
270 msg(M_FATAL | M_ERRNO, "Error: reset_net_event_win32: WSAEnumNetworkEvents call failed");
271 return 0; /* NOTREACHED */
272 }
273 else
274 {
275 return wne.lNetworkEvents;
276 }
277 }
278
279 void
close_net_event_win32(struct rw_handle * event,socket_descriptor_t sd,unsigned int flags)280 close_net_event_win32(struct rw_handle *event, socket_descriptor_t sd, unsigned int flags)
281 {
282 if (event->read)
283 {
284 if (socket_defined(sd))
285 {
286 if (WSAEventSelect(sd, event->read, 0) != 0)
287 {
288 msg(M_WARN | M_ERRNO, "Warning: close_net_event_win32: WSAEventSelect call failed");
289 }
290 }
291 if (!ResetEvent(event->read))
292 {
293 msg(M_WARN | M_ERRNO, "Warning: ResetEvent (read) failed in close_net_event_win32");
294 }
295 if (!(flags & NE32_PERSIST_EVENT))
296 {
297 if (!CloseHandle(event->read))
298 {
299 msg(M_WARN | M_ERRNO, "Warning: CloseHandle (read) failed in close_net_event_win32");
300 }
301 event->read = NULL;
302 }
303 }
304
305 if (event->write)
306 {
307 if (!ResetEvent(event->write))
308 {
309 msg(M_WARN | M_ERRNO, "Warning: ResetEvent (write) failed in close_net_event_win32");
310 }
311 if (!(flags & NE32_PERSIST_EVENT))
312 {
313 if (!CloseHandle(event->write))
314 {
315 msg(M_WARN | M_ERRNO, "Warning: CloseHandle (write) failed in close_net_event_win32");
316 }
317 event->write = NULL;
318 }
319 }
320 }
321
322 /*
323 * struct net_event_win32
324 */
325
326 void
net_event_win32_init(struct net_event_win32 * ne)327 net_event_win32_init(struct net_event_win32 *ne)
328 {
329 CLEAR(*ne);
330 ne->sd = SOCKET_UNDEFINED;
331 }
332
333 void
net_event_win32_start(struct net_event_win32 * ne,long network_events,socket_descriptor_t sd)334 net_event_win32_start(struct net_event_win32 *ne, long network_events, socket_descriptor_t sd)
335 {
336 ASSERT(!socket_defined(ne->sd));
337 ne->sd = sd;
338 ne->event_mask = 0;
339 init_net_event_win32(&ne->handle, network_events, sd, NE32_PERSIST_EVENT|NE32_WRITE_EVENT);
340 }
341
342 void
net_event_win32_reset_write(struct net_event_win32 * ne)343 net_event_win32_reset_write(struct net_event_win32 *ne)
344 {
345 BOOL status;
346 if (ne->event_mask & FD_WRITE)
347 {
348 status = SetEvent(ne->handle.write);
349 }
350 else
351 {
352 status = ResetEvent(ne->handle.write);
353 }
354 if (!status)
355 {
356 msg(M_WARN | M_ERRNO, "Warning: SetEvent/ResetEvent failed in net_event_win32_reset_write");
357 }
358 }
359
360 void
net_event_win32_reset(struct net_event_win32 * ne)361 net_event_win32_reset(struct net_event_win32 *ne)
362 {
363 ne->event_mask |= reset_net_event_win32(&ne->handle, ne->sd);
364 }
365
366 void
net_event_win32_stop(struct net_event_win32 * ne)367 net_event_win32_stop(struct net_event_win32 *ne)
368 {
369 if (net_event_win32_defined(ne))
370 {
371 close_net_event_win32(&ne->handle, ne->sd, NE32_PERSIST_EVENT);
372 }
373 ne->sd = SOCKET_UNDEFINED;
374 ne->event_mask = 0;
375 }
376
377 void
net_event_win32_close(struct net_event_win32 * ne)378 net_event_win32_close(struct net_event_win32 *ne)
379 {
380 if (net_event_win32_defined(ne))
381 {
382 close_net_event_win32(&ne->handle, ne->sd, 0);
383 }
384 net_event_win32_init(ne);
385 }
386
387 /*
388 * Simulate *nix signals on Windows.
389 *
390 * Two modes:
391 * (1) Console mode -- map keyboard function keys to signals
392 * (2) Service mode -- map Windows event object to SIGTERM
393 */
394
395 static void
win_trigger_event(struct win32_signal * ws)396 win_trigger_event(struct win32_signal *ws)
397 {
398 if (ws->mode == WSO_MODE_SERVICE && HANDLE_DEFINED(ws->in.read))
399 {
400 SetEvent(ws->in.read);
401 }
402 else /* generate a key-press event */
403 {
404 DWORD tmp;
405 INPUT_RECORD ir;
406 HANDLE stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
407
408 CLEAR(ir);
409 ir.EventType = KEY_EVENT;
410 ir.Event.KeyEvent.bKeyDown = true;
411 if (!stdin_handle || !WriteConsoleInput(stdin_handle, &ir, 1, &tmp))
412 {
413 msg(M_WARN|M_ERRNO, "WARN: win_trigger_event: WriteConsoleInput");
414 }
415 }
416 }
417
418 /*
419 * Callback to handle console ctrl events
420 */
421 static bool WINAPI
win_ctrl_handler(DWORD signum)422 win_ctrl_handler(DWORD signum)
423 {
424 msg(D_LOW, "win_ctrl_handler: signal received (code=%lu)", (unsigned long) signum);
425
426 if (siginfo_static.signal_received == SIGTERM)
427 {
428 return true;
429 }
430
431 switch (signum)
432 {
433 case CTRL_C_EVENT:
434 case CTRL_BREAK_EVENT:
435 throw_signal(SIGTERM);
436 /* trigget the win32_signal to interrupt the event loop */
437 win_trigger_event(&win32_signal);
438 return true;
439 break;
440
441 default:
442 msg(D_LOW, "win_ctrl_handler: signal (code=%lu) not handled", (unsigned long) signum);
443 break;
444 }
445 /* pass all other signals to the next handler */
446 return false;
447 }
448
449 void
win32_signal_clear(struct win32_signal * ws)450 win32_signal_clear(struct win32_signal *ws)
451 {
452 CLEAR(*ws);
453 }
454
455 void
win32_signal_open(struct win32_signal * ws,int force,const char * exit_event_name,bool exit_event_initial_state)456 win32_signal_open(struct win32_signal *ws,
457 int force,
458 const char *exit_event_name,
459 bool exit_event_initial_state)
460 {
461 CLEAR(*ws);
462
463 ws->mode = WSO_MODE_UNDEF;
464 ws->in.read = INVALID_HANDLE_VALUE;
465 ws->in.write = INVALID_HANDLE_VALUE;
466 ws->console_mode_save = 0;
467 ws->console_mode_save_defined = false;
468
469 if (force == WSO_NOFORCE || force == WSO_FORCE_CONSOLE)
470 {
471 /*
472 * Try to open console.
473 */
474 ws->in.read = GetStdHandle(STD_INPUT_HANDLE);
475 if (ws->in.read != INVALID_HANDLE_VALUE)
476 {
477 if (GetConsoleMode(ws->in.read, &ws->console_mode_save))
478 {
479 /* running on a console */
480 const DWORD new_console_mode = ws->console_mode_save
481 & ~(ENABLE_WINDOW_INPUT
482 | ENABLE_PROCESSED_INPUT
483 | ENABLE_LINE_INPUT
484 | ENABLE_ECHO_INPUT
485 | ENABLE_MOUSE_INPUT);
486
487 if (new_console_mode != ws->console_mode_save)
488 {
489 if (!SetConsoleMode(ws->in.read, new_console_mode))
490 {
491 msg(M_ERR, "Error: win32_signal_open: SetConsoleMode failed");
492 }
493 ws->console_mode_save_defined = true;
494 }
495 ws->mode = WSO_MODE_CONSOLE;
496 }
497 else
498 {
499 ws->in.read = INVALID_HANDLE_VALUE; /* probably running as a service */
500 }
501 }
502 }
503
504 /*
505 * If console open failed, assume we are running
506 * as a service.
507 */
508 if ((force == WSO_NOFORCE || force == WSO_FORCE_SERVICE)
509 && !HANDLE_DEFINED(ws->in.read) && exit_event_name)
510 {
511 struct security_attributes sa;
512
513 if (!init_security_attributes_allow_all(&sa))
514 {
515 msg(M_ERR, "Error: win32_signal_open: init SA failed");
516 }
517
518 ws->in.read = CreateEvent(&sa.sa,
519 TRUE,
520 exit_event_initial_state ? TRUE : FALSE,
521 exit_event_name);
522 if (ws->in.read == NULL)
523 {
524 msg(M_WARN|M_ERRNO, "NOTE: CreateEvent '%s' failed", exit_event_name);
525 }
526 else
527 {
528 if (WaitForSingleObject(ws->in.read, 0) != WAIT_TIMEOUT)
529 {
530 msg(M_FATAL, "ERROR: Exit Event ('%s') is signaled", exit_event_name);
531 }
532 else
533 {
534 ws->mode = WSO_MODE_SERVICE;
535 }
536 }
537 }
538 /* set the ctrl handler in both console and service modes */
539 if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE) win_ctrl_handler, true))
540 {
541 msg(M_WARN|M_ERRNO, "WARN: SetConsoleCtrlHandler failed");
542 }
543 }
544
545 static bool
keyboard_input_available(struct win32_signal * ws)546 keyboard_input_available(struct win32_signal *ws)
547 {
548 ASSERT(ws->mode == WSO_MODE_CONSOLE);
549 if (HANDLE_DEFINED(ws->in.read))
550 {
551 DWORD n;
552 if (GetNumberOfConsoleInputEvents(ws->in.read, &n))
553 {
554 return n > 0;
555 }
556 }
557 return false;
558 }
559
560 static unsigned int
keyboard_ir_to_key(INPUT_RECORD * ir)561 keyboard_ir_to_key(INPUT_RECORD *ir)
562 {
563 if (ir->Event.KeyEvent.uChar.AsciiChar == 0)
564 {
565 return ir->Event.KeyEvent.wVirtualScanCode;
566 }
567
568 if ((ir->Event.KeyEvent.dwControlKeyState
569 & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
570 && (ir->Event.KeyEvent.wVirtualKeyCode != 18))
571 {
572 return ir->Event.KeyEvent.wVirtualScanCode * 256;
573 }
574
575 return ir->Event.KeyEvent.uChar.AsciiChar;
576 }
577
578 static unsigned int
win32_keyboard_get(struct win32_signal * ws)579 win32_keyboard_get(struct win32_signal *ws)
580 {
581 ASSERT(ws->mode == WSO_MODE_CONSOLE);
582 if (HANDLE_DEFINED(ws->in.read))
583 {
584 INPUT_RECORD ir;
585 do
586 {
587 DWORD n;
588 if (!keyboard_input_available(ws))
589 {
590 return 0;
591 }
592 if (!ReadConsoleInput(ws->in.read, &ir, 1, &n))
593 {
594 return 0;
595 }
596 } while (ir.EventType != KEY_EVENT || ir.Event.KeyEvent.bKeyDown != TRUE);
597
598 return keyboard_ir_to_key(&ir);
599 }
600 else
601 {
602 return 0;
603 }
604 }
605
606 void
win32_signal_close(struct win32_signal * ws)607 win32_signal_close(struct win32_signal *ws)
608 {
609 if (ws->mode == WSO_MODE_SERVICE && HANDLE_DEFINED(ws->in.read))
610 {
611 CloseHandle(ws->in.read);
612 }
613 if (ws->console_mode_save_defined)
614 {
615 if (!SetConsoleMode(ws->in.read, ws->console_mode_save))
616 {
617 msg(M_ERR, "Error: win32_signal_close: SetConsoleMode failed");
618 }
619 }
620 CLEAR(*ws);
621 }
622
623 /*
624 * Return true if interrupt occurs in service mode.
625 */
626 bool
win32_service_interrupt(struct win32_signal * ws)627 win32_service_interrupt(struct win32_signal *ws)
628 {
629 if (ws->mode == WSO_MODE_SERVICE)
630 {
631 if (HANDLE_DEFINED(ws->in.read)
632 && WaitForSingleObject(ws->in.read, 0) == WAIT_OBJECT_0)
633 {
634 return true;
635 }
636 }
637 return false;
638 }
639
640 int
win32_signal_get(struct win32_signal * ws)641 win32_signal_get(struct win32_signal *ws)
642 {
643 int ret = 0;
644 if (siginfo_static.signal_received)
645 {
646 ret = siginfo_static.signal_received;
647 }
648 else
649 {
650 if (ws->mode == WSO_MODE_SERVICE)
651 {
652 if (win32_service_interrupt(ws))
653 {
654 ret = SIGTERM;
655 }
656 }
657 else if (ws->mode == WSO_MODE_CONSOLE)
658 {
659 switch (win32_keyboard_get(ws))
660 {
661 case 0x3B: /* F1 -> USR1 */
662 ret = SIGUSR1;
663 break;
664
665 case 0x3C: /* F2 -> USR2 */
666 ret = SIGUSR2;
667 break;
668
669 case 0x3D: /* F3 -> HUP */
670 ret = SIGHUP;
671 break;
672
673 case 0x3E: /* F4 -> TERM */
674 ret = SIGTERM;
675 break;
676
677 case 0x03: /* CTRL-C -> TERM */
678 ret = SIGTERM;
679 break;
680 }
681 }
682 if (ret)
683 {
684 siginfo_static.signal_received = ret;
685 siginfo_static.source = SIG_SOURCE_HARD;
686 }
687 }
688 return ret;
689 }
690
691 void
win32_pause(struct win32_signal * ws)692 win32_pause(struct win32_signal *ws)
693 {
694 if (ws->mode == WSO_MODE_CONSOLE && HANDLE_DEFINED(ws->in.read))
695 {
696 msg(M_INFO|M_NOPREFIX, "Press any key to continue...");
697 do
698 {
699 WaitForSingleObject(ws->in.read, INFINITE);
700 } while (!win32_keyboard_get(ws));
701 }
702 }
703
704 /* window functions */
705
706 void
window_title_clear(struct window_title * wt)707 window_title_clear(struct window_title *wt)
708 {
709 CLEAR(*wt);
710 }
711
712 void
window_title_save(struct window_title * wt)713 window_title_save(struct window_title *wt)
714 {
715 if (!wt->saved)
716 {
717 if (!GetConsoleTitle(wt->old_window_title, sizeof(wt->old_window_title)))
718 {
719 wt->old_window_title[0] = 0;
720 wt->saved = false;
721 }
722 else
723 {
724 wt->saved = true;
725 }
726 }
727 }
728
729 void
window_title_restore(const struct window_title * wt)730 window_title_restore(const struct window_title *wt)
731 {
732 if (wt->saved)
733 {
734 SetConsoleTitle(wt->old_window_title);
735 }
736 }
737
738 void
window_title_generate(const char * title)739 window_title_generate(const char *title)
740 {
741 struct gc_arena gc = gc_new();
742 struct buffer out = alloc_buf_gc(256, &gc);
743 if (!title)
744 {
745 title = "";
746 }
747 buf_printf(&out, "[%s] " PACKAGE_NAME " " PACKAGE_VERSION " F4:EXIT F1:USR1 F2:USR2 F3:HUP", title);
748 SetConsoleTitle(BSTR(&out));
749 gc_free(&gc);
750 }
751
752 /* semaphore functions */
753
754 void
semaphore_clear(struct semaphore * s)755 semaphore_clear(struct semaphore *s)
756 {
757 CLEAR(*s);
758 }
759
760 void
semaphore_open(struct semaphore * s,const char * name)761 semaphore_open(struct semaphore *s, const char *name)
762 {
763 struct security_attributes sa;
764
765 s->locked = false;
766 s->name = name;
767 s->hand = NULL;
768
769 if (init_security_attributes_allow_all(&sa))
770 {
771 s->hand = CreateSemaphore(&sa.sa, 1, 1, name);
772 }
773
774 if (s->hand == NULL)
775 {
776 msg(M_WARN|M_ERRNO, "WARNING: Cannot create Win32 semaphore '%s'", name);
777 }
778 else
779 {
780 dmsg(D_SEMAPHORE, "Created Win32 semaphore '%s'", s->name);
781 }
782 }
783
784 bool
semaphore_lock(struct semaphore * s,int timeout_milliseconds)785 semaphore_lock(struct semaphore *s, int timeout_milliseconds)
786 {
787 bool ret = true;
788
789 if (s->hand)
790 {
791 DWORD status;
792 ASSERT(!s->locked);
793
794 dmsg(D_SEMAPHORE_LOW, "Attempting to lock Win32 semaphore '%s' prior to net shell command (timeout = %d sec)",
795 s->name,
796 timeout_milliseconds / 1000);
797 status = WaitForSingleObject(s->hand, timeout_milliseconds);
798 if (status == WAIT_FAILED)
799 {
800 msg(M_ERR, "Wait failed on Win32 semaphore '%s'", s->name);
801 }
802 ret = (status == WAIT_TIMEOUT) ? false : true;
803 if (ret)
804 {
805 dmsg(D_SEMAPHORE, "Locked Win32 semaphore '%s'", s->name);
806 s->locked = true;
807 }
808 else
809 {
810 dmsg(D_SEMAPHORE, "Wait on Win32 semaphore '%s' timed out after %d milliseconds",
811 s->name,
812 timeout_milliseconds);
813 }
814 }
815 return ret;
816 }
817
818 void
semaphore_release(struct semaphore * s)819 semaphore_release(struct semaphore *s)
820 {
821 if (s->hand)
822 {
823 ASSERT(s->locked);
824 dmsg(D_SEMAPHORE, "Releasing Win32 semaphore '%s'", s->name);
825 if (!ReleaseSemaphore(s->hand, 1, NULL))
826 {
827 msg(M_WARN | M_ERRNO, "ReleaseSemaphore failed on Win32 semaphore '%s'",
828 s->name);
829 }
830 s->locked = false;
831 }
832 }
833
834 void
semaphore_close(struct semaphore * s)835 semaphore_close(struct semaphore *s)
836 {
837 if (s->hand)
838 {
839 if (s->locked)
840 {
841 semaphore_release(s);
842 }
843 dmsg(D_SEMAPHORE, "Closing Win32 semaphore '%s'", s->name);
844 CloseHandle(s->hand);
845 s->hand = NULL;
846 }
847 }
848
849 /*
850 * Special global semaphore used to protect network
851 * shell commands from simultaneous instantiation.
852 */
853
854 void
netcmd_semaphore_init(void)855 netcmd_semaphore_init(void)
856 {
857 semaphore_open(&netcmd_semaphore, PACKAGE "_netcmd");
858 }
859
860 void
netcmd_semaphore_close(void)861 netcmd_semaphore_close(void)
862 {
863 semaphore_close(&netcmd_semaphore);
864 }
865
866 void
netcmd_semaphore_lock(void)867 netcmd_semaphore_lock(void)
868 {
869 const int timeout_seconds = 600;
870
871 if (!netcmd_semaphore.hand)
872 {
873 netcmd_semaphore_init();
874 }
875
876 if (!semaphore_lock(&netcmd_semaphore, timeout_seconds * 1000))
877 {
878 msg(M_FATAL, "Cannot lock net command semaphore");
879 }
880 }
881
882 void
netcmd_semaphore_release(void)883 netcmd_semaphore_release(void)
884 {
885 semaphore_release(&netcmd_semaphore);
886 /* netcmd_semaphore has max count of 1 - safe to close after release */
887 semaphore_close(&netcmd_semaphore);
888 }
889
890 /*
891 * Return true if filename is safe to be used on Windows,
892 * by avoiding the following reserved names:
893 *
894 * CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9,
895 * LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9, and CLOCK$
896 *
897 * See: http://msdn.microsoft.com/en-us/library/aa365247.aspx
898 * and http://msdn.microsoft.com/en-us/library/86k9f82k(VS.80).aspx
899 */
900
901 static bool
cmp_prefix(const char * str,const bool n,const char * pre)902 cmp_prefix(const char *str, const bool n, const char *pre)
903 {
904 size_t i = 0;
905
906 if (!str)
907 {
908 return false;
909 }
910
911 while (true)
912 {
913 const int c1 = pre[i];
914 int c2 = str[i];
915 ++i;
916 if (c1 == '\0')
917 {
918 if (n)
919 {
920 if (isdigit(c2))
921 {
922 c2 = str[i];
923 }
924 else
925 {
926 return false;
927 }
928 }
929 return c2 == '\0' || c2 == '.';
930 }
931 else if (c2 == '\0')
932 {
933 return false;
934 }
935 if (c1 != tolower(c2))
936 {
937 return false;
938 }
939 }
940 }
941
942 bool
win_safe_filename(const char * fn)943 win_safe_filename(const char *fn)
944 {
945 if (cmp_prefix(fn, false, "con"))
946 {
947 return false;
948 }
949 if (cmp_prefix(fn, false, "prn"))
950 {
951 return false;
952 }
953 if (cmp_prefix(fn, false, "aux"))
954 {
955 return false;
956 }
957 if (cmp_prefix(fn, false, "nul"))
958 {
959 return false;
960 }
961 if (cmp_prefix(fn, true, "com"))
962 {
963 return false;
964 }
965 if (cmp_prefix(fn, true, "lpt"))
966 {
967 return false;
968 }
969 if (cmp_prefix(fn, false, "clock$"))
970 {
971 return false;
972 }
973 return true;
974 }
975
976 /*
977 * Service functions for openvpn_execve
978 */
979
980 static char *
env_block(const struct env_set * es)981 env_block(const struct env_set *es)
982 {
983 char force_path[256];
984 char *sysroot = get_win_sys_path();
985
986 if (!openvpn_snprintf(force_path, sizeof(force_path), "PATH=%s\\System32;%s;%s\\System32\\Wbem",
987 sysroot, sysroot, sysroot))
988 {
989 msg(M_WARN, "env_block: default path truncated to %s", force_path);
990 }
991
992 if (es)
993 {
994 struct env_item *e;
995 char *ret;
996 char *p;
997 size_t nchars = 1;
998 bool path_seen = false;
999
1000 for (e = es->list; e != NULL; e = e->next)
1001 {
1002 nchars += strlen(e->string) + 1;
1003 }
1004
1005 nchars += strlen(force_path)+1;
1006
1007 ret = (char *) malloc(nchars);
1008 check_malloc_return(ret);
1009
1010 p = ret;
1011 for (e = es->list; e != NULL; e = e->next)
1012 {
1013 if (env_allowed(e->string))
1014 {
1015 strcpy(p, e->string);
1016 p += strlen(e->string) + 1;
1017 }
1018 if (strncmp(e->string, "PATH=", 5 ) == 0)
1019 {
1020 path_seen = true;
1021 }
1022 }
1023
1024 /* make sure PATH is set */
1025 if (!path_seen)
1026 {
1027 msg( M_INFO, "env_block: add %s", force_path );
1028 strcpy( p, force_path );
1029 p += strlen(force_path) + 1;
1030 }
1031
1032 *p = '\0';
1033 return ret;
1034 }
1035 else
1036 {
1037 return NULL;
1038 }
1039 }
1040
1041 static WCHAR *
wide_cmd_line(const struct argv * a,struct gc_arena * gc)1042 wide_cmd_line(const struct argv *a, struct gc_arena *gc)
1043 {
1044 size_t nchars = 1;
1045 size_t maxlen = 0;
1046 size_t i;
1047 struct buffer buf;
1048 char *work = NULL;
1049
1050 if (!a)
1051 {
1052 return NULL;
1053 }
1054
1055 for (i = 0; i < a->argc; ++i)
1056 {
1057 const char *arg = a->argv[i];
1058 const size_t len = strlen(arg);
1059 nchars += len + 3;
1060 if (len > maxlen)
1061 {
1062 maxlen = len;
1063 }
1064 }
1065
1066 work = gc_malloc(maxlen + 1, false, gc);
1067 check_malloc_return(work);
1068 buf = alloc_buf_gc(nchars, gc);
1069
1070 for (i = 0; i < a->argc; ++i)
1071 {
1072 const char *arg = a->argv[i];
1073 strcpy(work, arg);
1074 string_mod(work, CC_PRINT, CC_DOUBLE_QUOTE|CC_CRLF, '_');
1075 if (i)
1076 {
1077 buf_printf(&buf, " ");
1078 }
1079 if (string_class(work, CC_ANY, CC_SPACE))
1080 {
1081 buf_printf(&buf, "%s", work);
1082 }
1083 else
1084 {
1085 buf_printf(&buf, "\"%s\"", work);
1086 }
1087 }
1088
1089 return wide_string(BSTR(&buf), gc);
1090 }
1091
1092 /*
1093 * Attempt to simulate fork/execve on Windows
1094 */
1095 int
openvpn_execve(const struct argv * a,const struct env_set * es,const unsigned int flags)1096 openvpn_execve(const struct argv *a, const struct env_set *es, const unsigned int flags)
1097 {
1098 int ret = OPENVPN_EXECVE_ERROR;
1099 static bool exec_warn = false;
1100
1101 if (a && a->argv[0])
1102 {
1103 if (openvpn_execve_allowed(flags))
1104 {
1105 struct gc_arena gc = gc_new();
1106 STARTUPINFOW start_info;
1107 PROCESS_INFORMATION proc_info;
1108
1109 char *env = env_block(es);
1110 WCHAR *cl = wide_cmd_line(a, &gc);
1111 WCHAR *cmd = wide_string(a->argv[0], &gc);
1112
1113 /* this allows console programs to run, and is ignored otherwise */
1114 DWORD proc_flags = CREATE_NO_WINDOW;
1115
1116 CLEAR(start_info);
1117 CLEAR(proc_info);
1118
1119 /* fill in STARTUPINFO struct */
1120 GetStartupInfoW(&start_info);
1121 start_info.cb = sizeof(start_info);
1122 start_info.dwFlags = STARTF_USESHOWWINDOW;
1123 start_info.wShowWindow = SW_HIDE;
1124
1125 if (CreateProcessW(cmd, cl, NULL, NULL, FALSE, proc_flags, env, NULL, &start_info, &proc_info))
1126 {
1127 DWORD exit_status = 0;
1128 CloseHandle(proc_info.hThread);
1129 WaitForSingleObject(proc_info.hProcess, INFINITE);
1130 if (GetExitCodeProcess(proc_info.hProcess, &exit_status))
1131 {
1132 ret = (int)exit_status;
1133 }
1134 else
1135 {
1136 msg(M_WARN|M_ERRNO, "openvpn_execve: GetExitCodeProcess %S failed", cmd);
1137 }
1138 CloseHandle(proc_info.hProcess);
1139 }
1140 else
1141 {
1142 msg(M_WARN|M_ERRNO, "openvpn_execve: CreateProcess %S failed", cmd);
1143 }
1144 free(env);
1145 gc_free(&gc);
1146 }
1147 else
1148 {
1149 ret = OPENVPN_EXECVE_NOT_ALLOWED;
1150 if (!exec_warn && (script_security() < SSEC_SCRIPTS))
1151 {
1152 msg(M_WARN, SCRIPT_SECURITY_WARNING);
1153 exec_warn = true;
1154 }
1155 }
1156 }
1157 else
1158 {
1159 msg(M_WARN, "openvpn_execve: called with empty argv");
1160 }
1161 return ret;
1162 }
1163
1164 WCHAR *
wide_string(const char * utf8,struct gc_arena * gc)1165 wide_string(const char *utf8, struct gc_arena *gc)
1166 {
1167 int n = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
1168 WCHAR *ucs16 = gc_malloc(n * sizeof(WCHAR), false, gc);
1169 MultiByteToWideChar(CP_UTF8, 0, utf8, -1, ucs16, n);
1170 return ucs16;
1171 }
1172
1173 /*
1174 * call ourself in another process
1175 */
1176 void
fork_to_self(const char * cmdline)1177 fork_to_self(const char *cmdline)
1178 {
1179 STARTUPINFO start_info;
1180 PROCESS_INFORMATION proc_info;
1181 char self_exe[256];
1182 char *cl = string_alloc(cmdline, NULL);
1183 DWORD status;
1184
1185 CLEAR(start_info);
1186 CLEAR(proc_info);
1187 CLEAR(self_exe);
1188
1189 status = GetModuleFileName(NULL, self_exe, sizeof(self_exe));
1190 if (status == 0 || status == sizeof(self_exe))
1191 {
1192 msg(M_WARN|M_ERRNO, "fork_to_self: CreateProcess failed: cannot get module name via GetModuleFileName");
1193 goto done;
1194 }
1195
1196 /* fill in STARTUPINFO struct */
1197 GetStartupInfo(&start_info);
1198 start_info.cb = sizeof(start_info);
1199 start_info.dwFlags = STARTF_USESHOWWINDOW;
1200 start_info.wShowWindow = SW_HIDE;
1201
1202 if (CreateProcess(self_exe, cl, NULL, NULL, FALSE, 0, NULL, NULL, &start_info, &proc_info))
1203 {
1204 CloseHandle(proc_info.hThread);
1205 CloseHandle(proc_info.hProcess);
1206 }
1207 else
1208 {
1209 msg(M_WARN|M_ERRNO, "fork_to_self: CreateProcess failed: %s", cmdline);
1210 }
1211
1212 done:
1213 free(cl);
1214 }
1215
1216 char *
get_win_sys_path(void)1217 get_win_sys_path(void)
1218 {
1219 ASSERT(win_sys_path);
1220 return win_sys_path;
1221 }
1222
1223 void
set_win_sys_path(const char * newpath,struct env_set * es)1224 set_win_sys_path(const char *newpath, struct env_set *es)
1225 {
1226 free(win_sys_path);
1227 win_sys_path = string_alloc(newpath, NULL);
1228 setenv_str(es, SYS_PATH_ENV_VAR_NAME, win_sys_path); /* route.exe needs this */
1229 }
1230
1231 void
set_win_sys_path_via_env(struct env_set * es)1232 set_win_sys_path_via_env(struct env_set *es)
1233 {
1234 char buf[256];
1235 DWORD status = GetEnvironmentVariable(SYS_PATH_ENV_VAR_NAME, buf, sizeof(buf));
1236 if (!status)
1237 {
1238 msg(M_ERR, "Cannot find environmental variable %s", SYS_PATH_ENV_VAR_NAME);
1239 }
1240 if (status > sizeof(buf) - 1)
1241 {
1242 msg(M_FATAL, "String overflow attempting to read environmental variable %s", SYS_PATH_ENV_VAR_NAME);
1243 }
1244 set_win_sys_path(buf, es);
1245 }
1246
1247
1248 const char *
win_get_tempdir(void)1249 win_get_tempdir(void)
1250 {
1251 static char tmpdir[MAX_PATH];
1252 WCHAR wtmpdir[MAX_PATH];
1253
1254 if (!GetTempPathW(_countof(wtmpdir), wtmpdir))
1255 {
1256 /* Warn if we can't find a valid temporary directory, which should
1257 * be unlikely.
1258 */
1259 msg(M_WARN, "Could not find a suitable temporary directory."
1260 " (GetTempPath() failed). Consider using --tmp-dir");
1261 return NULL;
1262 }
1263
1264 if (WideCharToMultiByte(CP_UTF8, 0, wtmpdir, -1, NULL, 0, NULL, NULL) > sizeof(tmpdir))
1265 {
1266 msg(M_WARN, "Could not get temporary directory. Path is too long."
1267 " Consider using --tmp-dir");
1268 return NULL;
1269 }
1270
1271 WideCharToMultiByte(CP_UTF8, 0, wtmpdir, -1, tmpdir, sizeof(tmpdir), NULL, NULL);
1272 return tmpdir;
1273 }
1274
1275 static bool
win_block_dns_service(bool add,int index,const HANDLE pipe)1276 win_block_dns_service(bool add, int index, const HANDLE pipe)
1277 {
1278 bool ret = false;
1279 ack_message_t ack;
1280 struct gc_arena gc = gc_new();
1281
1282 block_dns_message_t data = {
1283 .header = {
1284 (add ? msg_add_block_dns : msg_del_block_dns),
1285 sizeof(block_dns_message_t),
1286 0
1287 },
1288 .iface = { .index = index, .name = "" }
1289 };
1290
1291 if (!send_msg_iservice(pipe, &data, sizeof(data), &ack, "Block_DNS"))
1292 {
1293 goto out;
1294 }
1295
1296 if (ack.error_number != NO_ERROR)
1297 {
1298 msg(M_WARN, "Block_DNS: %s block dns filters using service failed: %s [status=0x%x if_index=%d]",
1299 (add ? "adding" : "deleting"), strerror_win32(ack.error_number, &gc),
1300 ack.error_number, data.iface.index);
1301 goto out;
1302 }
1303
1304 ret = true;
1305 msg(M_INFO, "%s outside dns using service succeeded.", (add ? "Blocking" : "Unblocking"));
1306 out:
1307 gc_free(&gc);
1308 return ret;
1309 }
1310
1311 static void
block_dns_msg_handler(DWORD err,const char * msg)1312 block_dns_msg_handler(DWORD err, const char *msg)
1313 {
1314 struct gc_arena gc = gc_new();
1315
1316 if (err == 0)
1317 {
1318 msg(M_INFO, "%s", msg);
1319 }
1320 else
1321 {
1322 msg(M_WARN, "Error in add_block_dns_filters(): %s : %s [status=0x%lx]",
1323 msg, strerror_win32(err, &gc), err);
1324 }
1325
1326 gc_free(&gc);
1327 }
1328
1329 bool
win_wfp_block_dns(const NET_IFINDEX index,const HANDLE msg_channel)1330 win_wfp_block_dns(const NET_IFINDEX index, const HANDLE msg_channel)
1331 {
1332 WCHAR openvpnpath[MAX_PATH];
1333 bool ret = false;
1334 DWORD status;
1335
1336 if (msg_channel)
1337 {
1338 dmsg(D_LOW, "Using service to add block dns filters");
1339 ret = win_block_dns_service(true, index, msg_channel);
1340 goto out;
1341 }
1342
1343 status = GetModuleFileNameW(NULL, openvpnpath, _countof(openvpnpath));
1344 if (status == 0 || status == _countof(openvpnpath))
1345 {
1346 msg(M_WARN|M_ERRNO, "block_dns: cannot get executable path");
1347 goto out;
1348 }
1349
1350 status = add_block_dns_filters(&m_hEngineHandle, index, openvpnpath,
1351 block_dns_msg_handler);
1352 if (status == 0)
1353 {
1354 int is_auto = 0;
1355 tap_metric_v4 = get_interface_metric(index, AF_INET, &is_auto);
1356 if (is_auto)
1357 {
1358 tap_metric_v4 = 0;
1359 }
1360 tap_metric_v6 = get_interface_metric(index, AF_INET6, &is_auto);
1361 if (is_auto)
1362 {
1363 tap_metric_v6 = 0;
1364 }
1365 status = set_interface_metric(index, AF_INET, BLOCK_DNS_IFACE_METRIC);
1366 if (!status)
1367 {
1368 set_interface_metric(index, AF_INET6, BLOCK_DNS_IFACE_METRIC);
1369 }
1370 }
1371
1372 ret = (status == 0);
1373
1374 out:
1375
1376 return ret;
1377 }
1378
1379 bool
win_wfp_uninit(const NET_IFINDEX index,const HANDLE msg_channel)1380 win_wfp_uninit(const NET_IFINDEX index, const HANDLE msg_channel)
1381 {
1382 dmsg(D_LOW, "Uninitializing WFP");
1383
1384 if (msg_channel)
1385 {
1386 msg(D_LOW, "Using service to delete block dns filters");
1387 win_block_dns_service(false, index, msg_channel);
1388 }
1389 else
1390 {
1391 delete_block_dns_filters(m_hEngineHandle);
1392 m_hEngineHandle = NULL;
1393 if (tap_metric_v4 >= 0)
1394 {
1395 set_interface_metric(index, AF_INET, tap_metric_v4);
1396 }
1397 if (tap_metric_v6 >= 0)
1398 {
1399 set_interface_metric(index, AF_INET6, tap_metric_v6);
1400 }
1401 }
1402
1403 return true;
1404 }
1405
1406 int
win32_version_info(void)1407 win32_version_info(void)
1408 {
1409 if (!IsWindowsXPOrGreater())
1410 {
1411 msg(M_FATAL, "Error: Windows version must be XP or greater.");
1412 }
1413
1414 if (!IsWindowsVistaOrGreater())
1415 {
1416 return WIN_XP;
1417 }
1418
1419 if (!IsWindows7OrGreater())
1420 {
1421 return WIN_VISTA;
1422 }
1423
1424 if (!IsWindows8OrGreater())
1425 {
1426 return WIN_7;
1427 }
1428
1429 if (!IsWindows8Point1OrGreater())
1430 {
1431 return WIN_8;
1432 }
1433
1434 if (!IsWindows10OrGreater())
1435 {
1436 return WIN_8_1;
1437 }
1438
1439 return WIN_10;
1440 }
1441
1442 bool
win32_is_64bit(void)1443 win32_is_64bit(void)
1444 {
1445 #if defined(_WIN64)
1446 return true; /* 64-bit programs run only on Win64 */
1447 #elif defined(_WIN32)
1448 /* 32-bit programs run on both 32-bit and 64-bit Windows */
1449 BOOL f64 = FALSE;
1450 return IsWow64Process(GetCurrentProcess(), &f64) && f64;
1451 #else /* if defined(_WIN64) */
1452 return false; /* Win64 does not support Win16 */
1453 #endif
1454 }
1455
1456 const char *
win32_version_string(struct gc_arena * gc,bool add_name)1457 win32_version_string(struct gc_arena *gc, bool add_name)
1458 {
1459 int version = win32_version_info();
1460 struct buffer out = alloc_buf_gc(256, gc);
1461
1462 switch (version)
1463 {
1464 case WIN_XP:
1465 buf_printf(&out, "5.1%s", add_name ? " (Windows XP)" : "");
1466 break;
1467
1468 case WIN_VISTA:
1469 buf_printf(&out, "6.0%s", add_name ? " (Windows Vista)" : "");
1470 break;
1471
1472 case WIN_7:
1473 buf_printf(&out, "6.1%s", add_name ? " (Windows 7)" : "");
1474 break;
1475
1476 case WIN_8:
1477 buf_printf(&out, "6.2%s", add_name ? " (Windows 8)" : "");
1478 break;
1479
1480 case WIN_8_1:
1481 buf_printf(&out, "6.3%s", add_name ? " (Windows 8.1)" : "");
1482 break;
1483
1484 case WIN_10:
1485 buf_printf(&out, "10.0%s", add_name ? " (Windows 10 or greater)" : "");
1486 break;
1487
1488 default:
1489 msg(M_NONFATAL, "Unknown Windows version: %d", version);
1490 buf_printf(&out, "0.0%s", add_name ? " (unknown)" : "");
1491 break;
1492 }
1493
1494 buf_printf(&out, win32_is_64bit() ? " 64bit" : " 32bit");
1495
1496 return (const char *)out.data;
1497 }
1498
1499 bool
send_msg_iservice(HANDLE pipe,const void * data,size_t size,ack_message_t * ack,const char * context)1500 send_msg_iservice(HANDLE pipe, const void *data, size_t size,
1501 ack_message_t *ack, const char *context)
1502 {
1503 struct gc_arena gc = gc_new();
1504 DWORD len;
1505 bool ret = true;
1506
1507 if (!WriteFile(pipe, data, size, &len, NULL)
1508 || !ReadFile(pipe, ack, sizeof(*ack), &len, NULL))
1509 {
1510 msg(M_WARN, "%s: could not talk to service: %s [%lu]",
1511 context ? context : "Unknown",
1512 strerror_win32(GetLastError(), &gc), GetLastError());
1513 ret = false;
1514 }
1515
1516 gc_free(&gc);
1517 return ret;
1518 }
1519
1520 bool
openvpn_swprintf(wchar_t * const str,const size_t size,const wchar_t * const format,...)1521 openvpn_swprintf(wchar_t *const str, const size_t size, const wchar_t *const format, ...)
1522 {
1523 va_list arglist;
1524 int len = -1;
1525 if (size > 0)
1526 {
1527 va_start(arglist, format);
1528 len = vswprintf(str, size, format, arglist);
1529 va_end(arglist);
1530 str[size - 1] = L'\0';
1531 }
1532 return (len >= 0 && len < size);
1533 }
1534
1535 static BOOL
get_install_path(WCHAR * path,DWORD size)1536 get_install_path(WCHAR *path, DWORD size)
1537 {
1538 WCHAR reg_path[256];
1539 HKEY key;
1540 BOOL res = FALSE;
1541 openvpn_swprintf(reg_path, _countof(reg_path), L"SOFTWARE\\" PACKAGE_NAME);
1542
1543 LONG status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, KEY_READ, &key);
1544 if (status != ERROR_SUCCESS)
1545 {
1546 return res;
1547 }
1548
1549 /* The default value of REG_KEY is the install path */
1550 status = RegGetValueW(key, NULL, NULL, RRF_RT_REG_SZ, NULL, (LPBYTE)path, &size);
1551 res = status == ERROR_SUCCESS;
1552
1553 RegCloseKey(key);
1554
1555 return res;
1556 }
1557
1558 static void
set_openssl_env_vars()1559 set_openssl_env_vars()
1560 {
1561 const WCHAR *ssl_fallback_dir = L"C:\\Windows\\System32";
1562
1563 WCHAR install_path[MAX_PATH] = { 0 };
1564 if (!get_install_path(install_path, _countof(install_path)))
1565 {
1566 /* if we cannot find installation path from the registry,
1567 * use Windows directory as a fallback
1568 */
1569 openvpn_swprintf(install_path, _countof(install_path), L"%ls", ssl_fallback_dir);
1570 }
1571
1572 if ((install_path[wcslen(install_path) - 1]) == L'\\')
1573 {
1574 install_path[wcslen(install_path) - 1] = L'\0';
1575 }
1576
1577 static struct {
1578 WCHAR *name;
1579 WCHAR *value;
1580 } ossl_env[] = {
1581 {L"OPENSSL_CONF", L"openssl.cnf"},
1582 {L"OPENSSL_ENGINES", L"engines"},
1583 {L"OPENSSL_MODULES", L"modules"}
1584 };
1585
1586 for (size_t i = 0; i < SIZE(ossl_env); ++i)
1587 {
1588 size_t size = 0;
1589
1590 _wgetenv_s(&size, NULL, 0, ossl_env[i].name);
1591 if (size == 0)
1592 {
1593 WCHAR val[MAX_PATH] = {0};
1594 openvpn_swprintf(val, _countof(val), L"%ls\\ssl\\%ls", install_path, ossl_env[i].value);
1595 _wputenv_s(ossl_env[i].name, val);
1596 }
1597 }
1598 }
1599
1600 #endif /* ifdef _WIN32 */
1601