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