compat.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. /*
  2. * OpenConnect (SSL + DTLS) VPN client
  3. *
  4. * Copyright © 2008-2015 Intel Corporation.
  5. *
  6. * Authors: David Woodhouse <dwmw2@infradead.org>
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public License
  10. * version 2.1, as published by the Free Software Foundation.
  11. *
  12. * This program is distributed in the hope that it will be useful, but
  13. * WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. */
  17. #include <config.h>
  18. #include "openconnect-internal.h"
  19. #include <string.h>
  20. #include <stdarg.h>
  21. #include <stdlib.h>
  22. #include <errno.h>
  23. #include <ctype.h>
  24. #include <inttypes.h>
  25. #ifdef _WIN32
  26. #include <sec_api/stdlib_s.h> /* errno_t, size_t */
  27. #ifndef HAVE_GETENV_S_DECL
  28. errno_t getenv_s(
  29. size_t *ret_required_buf_size,
  30. char *buf,
  31. size_t buf_size_in_bytes,
  32. const char *name
  33. );
  34. #endif
  35. #ifndef HAVE_PUTENV_S_DECL
  36. errno_t _putenv_s(
  37. const char *varname,
  38. const char *value_string
  39. );
  40. #endif
  41. #endif
  42. #ifdef HAVE_SUNOS_BROKEN_TIME
  43. /*
  44. * On SunOS, time() goes backwards. Thankfully, gethrtime() doesn't.
  45. * https://www.illumos.org/issues/1871 and, for Solaris 11, Oracle
  46. * bug ID #15760793 (previously Sun CR ID 7121035).
  47. */
  48. #include <sys/time.h>
  49. time_t openconnect__time(time_t *t)
  50. {
  51. time_t s = gethrtime() / 1000000000LL;
  52. if (t)
  53. *t = s;
  54. return s;
  55. }
  56. #endif
  57. #ifndef HAVE_VASPRINTF
  58. int openconnect__vasprintf(char **strp, const char *fmt, va_list ap)
  59. {
  60. va_list ap2;
  61. char *res = NULL;
  62. int len = 160, len2;
  63. int ret = 0;
  64. int errno_save = -ENOMEM;
  65. res = malloc(160);
  66. if (!res)
  67. goto err;
  68. /* Use a copy of 'ap', preserving it in case we need to retry into
  69. a larger buffer. 160 characters should be sufficient for most
  70. strings in openconnect. */
  71. #ifdef HAVE_VA_COPY
  72. va_copy(ap2, ap);
  73. #elif defined(HAVE___VA_COPY)
  74. __va_copy(ap2, ap);
  75. #else
  76. #error No va_copy()!
  77. /* You could try this. */
  78. ap2 = ap;
  79. /* Or this */
  80. *ap2 = *ap;
  81. #endif
  82. len = vsnprintf(res, 160, fmt, ap2);
  83. va_end(ap2);
  84. if (len < 0) {
  85. printf_err:
  86. errno_save = errno;
  87. free(res);
  88. res = NULL;
  89. goto err;
  90. }
  91. if (len < 160)
  92. goto out;
  93. free(res);
  94. res = malloc(len+1);
  95. if (!res)
  96. goto err;
  97. len2 = vsnprintf(res, len+1, fmt, ap);
  98. if (len2 < 0 || len2 > len)
  99. goto printf_err;
  100. ret = 0;
  101. goto out;
  102. err:
  103. errno = errno_save;
  104. ret = -1;
  105. out:
  106. *strp = res;
  107. return ret;
  108. }
  109. #endif
  110. #ifndef HAVE_ASPRINTF
  111. int openconnect__asprintf(char **strp, const char *fmt, ...)
  112. {
  113. va_list ap;
  114. int ret;
  115. va_start(ap, fmt);
  116. ret = vasprintf(strp, fmt, ap);
  117. va_end(ap);
  118. return ret;
  119. }
  120. #endif
  121. #ifndef HAVE_GETLINE
  122. ssize_t openconnect__getline(char **lineptr, size_t *n, FILE *stream)
  123. {
  124. int len = 0;
  125. if (!*lineptr) {
  126. *n = 2;
  127. *lineptr = malloc(*n);
  128. if (!*lineptr)
  129. return -1;
  130. }
  131. while (fgets((*lineptr) + len, (*n) - len, stream)) {
  132. len += strlen((*lineptr) + len);
  133. if ((*lineptr)[len-1] == '\n')
  134. break;
  135. *n *= 2;
  136. realloc_inplace(*lineptr, *n);
  137. if (!*lineptr)
  138. return -1;
  139. }
  140. if (len)
  141. return len;
  142. return -1;
  143. }
  144. #endif
  145. #ifndef HAVE_STRCASESTR
  146. char *openconnect__strcasestr(const char *haystack, const char *needle)
  147. {
  148. int hlen = strlen(haystack);
  149. int nlen = strlen(needle);
  150. int i, j;
  151. for (i = 0; i < hlen - nlen + 1; i++) {
  152. for (j = 0; j < nlen; j++) {
  153. if (tolower(haystack[i + j]) !=
  154. tolower(needle[j]))
  155. break;
  156. }
  157. if (j == nlen)
  158. return (char *)haystack + i;
  159. }
  160. return NULL;
  161. }
  162. #endif
  163. #ifndef HAVE_STRNDUP
  164. char *openconnect__strndup(const char *s, size_t n)
  165. {
  166. char *r;
  167. if ((r = memchr(s, '\0', n)) != NULL)
  168. n = r - s;
  169. r = malloc(n + 1);
  170. if (r) {
  171. memcpy(r, s, n);
  172. r[n] = 0;
  173. }
  174. return r;
  175. }
  176. #endif
  177. #ifndef HAVE_STRCHRNUL
  178. const char *openconnect__strchrnul(const char *s, int c)
  179. {
  180. while (*s && *s != c) s++;
  181. return s;
  182. }
  183. #endif
  184. #ifndef HAVE_INET_ATON
  185. /* XX: unlike "real" inet_aton(), inet_pton() only accepts dotted-decimal notation, not
  186. * looser/rarer formats like 32-bit decimal values. For example, inet_aton() accepts both
  187. * "127.0.0.1" and "2130706433" as equivalent, but inet_pton() only accepts the former.
  188. */
  189. int openconnect__inet_aton(const char *cp, struct in_addr *addr)
  190. {
  191. return inet_pton(AF_INET, cp, addr);
  192. }
  193. #endif
  194. #ifdef _WIN32
  195. int openconnect__win32_setenv(const char *name, const char *value, int overwrite)
  196. {
  197. /* https://stackoverflow.com/a/23616164 */
  198. int errcode = 0;
  199. if(!overwrite) {
  200. size_t envsize = 0;
  201. errcode = getenv_s(&envsize, NULL, 0, name);
  202. if(errcode || envsize) return errcode;
  203. }
  204. return _putenv_s(name, value);
  205. }
  206. char *openconnect__win32_strerror(DWORD err)
  207. {
  208. wchar_t *msgw;
  209. char *msgutf8;
  210. int nr_chars;
  211. if (!FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
  212. FORMAT_MESSAGE_IGNORE_INSERTS |
  213. FORMAT_MESSAGE_ALLOCATE_BUFFER,
  214. NULL, err,
  215. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  216. (LPWSTR)&msgw, 0, NULL)) {
  217. if (asprintf(&msgutf8, _("(error 0x%lx)"), err) != -1)
  218. return msgutf8;
  219. fail:
  220. return strdup(_("(Error while describing error!)"));
  221. }
  222. nr_chars = wcslen(msgw);
  223. if (nr_chars && msgw[nr_chars - 1] == 10)
  224. msgw[--nr_chars] = 0;
  225. if (nr_chars && msgw[nr_chars - 1] == 13)
  226. msgw[--nr_chars] = 0;
  227. nr_chars = WideCharToMultiByte(CP_UTF8, 0, msgw, -1, NULL, 0, NULL, NULL);
  228. msgutf8 = malloc(nr_chars);
  229. if (!msgutf8)
  230. goto fail;
  231. WideCharToMultiByte(CP_UTF8, 0, msgw, -1, msgutf8, nr_chars, NULL, NULL);
  232. LocalFree(msgw);
  233. return msgutf8;
  234. }
  235. int openconnect__win32_sock_init(void)
  236. {
  237. WSADATA data;
  238. if (WSAStartup (MAKEWORD(1, 1), &data) != 0) {
  239. fprintf(stderr, _("ERROR: Cannot initialize sockets\n"));
  240. return -EIO;
  241. }
  242. return 0;
  243. }
  244. int openconnect__win32_inet_pton(int af, const char *src, void *dst)
  245. {
  246. union {
  247. struct sockaddr_in s4;
  248. struct sockaddr_in6 s6;
  249. } sa;
  250. int salen = sizeof(sa);
  251. if (af != AF_INET && af != AF_INET6) {
  252. errno = EAFNOSUPPORT;
  253. return -1;
  254. }
  255. memset(&sa, 0, sizeof(sa));
  256. sa.s4.sin_family = af;
  257. if (WSAStringToAddressA((char *)src, af, NULL, (void *)&sa, &salen))
  258. return 0;
  259. /* For Legacy IP we need to filter out a lot of crap that
  260. * inet_aton() (and WSAStringToAddress()) will support, but
  261. * which inet_pton() should not. Not to mention the fact that
  262. * Wine's implementation will even succeed for strings like
  263. * "2001::1" (https://bugs.winehq.org/show_bug.cgi?id=36991) */
  264. if (af == AF_INET) {
  265. char canon[16];
  266. unsigned char *a = (unsigned char *)&sa.s4.sin_addr;
  267. snprintf(canon, sizeof(canon), "%d.%d.%d.%d",
  268. a[0], a[1], a[2], a[3]);
  269. if (strcmp(canon, src))
  270. return 0;
  271. memcpy(dst, &sa.s4.sin_addr, sizeof(sa.s4.sin_addr));
  272. return 1;
  273. } else {
  274. memcpy(dst, &sa.s6.sin6_addr, sizeof(sa.s6.sin6_addr));
  275. return 1;
  276. }
  277. }
  278. /* https://github.com/ncm/selectable-socketpair/blob/master/socketpair.c
  279. Copyright 2007, 2010 by Nathan C. Myers <ncm@cantrip.org>
  280. Redistribution and use in source and binary forms, with or without modification,
  281. are permitted provided that the following conditions are met:
  282. Redistributions of source code must retain the above copyright notice, this
  283. list of conditions and the following disclaimer.
  284. Redistributions in binary form must reproduce the above copyright notice,
  285. this list of conditions and the following disclaimer in the documentation
  286. and/or other materials provided with the distribution.
  287. The name of the author must not be used to endorse or promote products derived
  288. from this software without specific prior written permission.
  289. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
  290. EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  291. OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
  292. SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  293. INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
  294. TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
  295. BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  296. CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
  297. WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  298. */
  299. /* Changes:
  300. * 2014-02-12: merge David Woodhouse, Ger Hobbelt improvements
  301. * git.infradead.org/users/dwmw2/openconnect.git/commitdiff/bdeefa54
  302. * github.com/GerHobbelt/selectable-socketpair
  303. * always init the socks[] to -1/INVALID_SOCKET on error, both on Win32/64
  304. * and UNIX/other platforms
  305. * 2013-07-18: Change to BSD 3-clause license
  306. * 2010-03-31:
  307. * set addr to 127.0.0.1 because win32 getsockname does not always set it.
  308. * 2010-02-25:
  309. * set SO_REUSEADDR option to avoid leaking some windows resource.
  310. * Windows System Error 10049, "Event ID 4226 TCP/IP has reached
  311. * the security limit imposed on the number of concurrent TCP connect
  312. * attempts." Bleah.
  313. * 2007-04-25:
  314. * preserve value of WSAGetLastError() on all error returns.
  315. * 2007-04-22: (Thanks to Matthew Gregan <kinetik@flim.org>)
  316. * s/EINVAL/WSAEINVAL/ fix trivial compile failure
  317. * s/socket/WSASocket/ enable creation of sockets suitable as stdin/stdout
  318. * of a child process.
  319. * add argument make_overlapped
  320. */
  321. #include <string.h>
  322. # include <ws2tcpip.h> /* socklen_t, et al (MSVC20xx) */
  323. # include <windows.h>
  324. # include <io.h>
  325. #ifdef HAVE_AFUNIX_H
  326. #include <afunix.h>
  327. #else
  328. #define UNIX_PATH_MAX 108
  329. struct sockaddr_un {
  330. ADDRESS_FAMILY sun_family; /* AF_UNIX */
  331. char sun_path[UNIX_PATH_MAX]; /* pathname */
  332. } SOCKADDR_UN, *PSOCKADDR_UN;
  333. #endif /* HAS_AFUNIX_H */
  334. /* dumb_socketpair:
  335. * If make_overlapped is nonzero, both sockets created will be usable for
  336. * "overlapped" operations via WSASend etc. If make_overlapped is zero,
  337. * socks[0] (only) will be usable with regular ReadFile etc., and thus
  338. * suitable for use as stdin or stdout of a child process. Note that the
  339. * sockets must be closed with closesocket() regardless.
  340. */
  341. int dumb_socketpair(OPENCONNECT_CMD_SOCKET socks[2], int make_overlapped)
  342. {
  343. union {
  344. struct sockaddr_un unaddr;
  345. struct sockaddr_in inaddr;
  346. struct sockaddr addr;
  347. } a;
  348. OPENCONNECT_CMD_SOCKET listener;
  349. int e, ii;
  350. int domain = AF_UNIX;
  351. socklen_t addrlen = sizeof(a.unaddr);
  352. DWORD flags = (make_overlapped ? WSA_FLAG_OVERLAPPED : 0);
  353. int reuse = 1;
  354. if (socks == 0) {
  355. WSASetLastError(WSAEINVAL);
  356. return SOCKET_ERROR;
  357. }
  358. socks[0] = socks[1] = -1;
  359. /* AF_UNIX/SOCK_STREAM became available in Windows 10
  360. * ( https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows )
  361. *
  362. * We will attempt to use AF_UNIX, but fallback to using AF_INET if
  363. * setting up AF_UNIX socket fails in any other way, which it surely will
  364. * on earlier versions of Windows.
  365. */
  366. for (ii = 0; ii < 2; ii++) {
  367. listener = socket(domain, SOCK_STREAM, domain == AF_INET ? IPPROTO_TCP : 0);
  368. if (listener == INVALID_SOCKET)
  369. goto fallback;
  370. memset(&a, 0, sizeof(a));
  371. if (domain == AF_UNIX) {
  372. /* XX: Abstract sockets (filesystem-independent) don't work, contrary to
  373. * the claims of the aforementioned blog post:
  374. * https://github.com/microsoft/WSL/issues/4240#issuecomment-549663217
  375. *
  376. * So we must use a named path, and that comes with all the attendant
  377. * problems of permissions and collisions. Trying various temporary
  378. * directories and putting high-res time and PID in the filename, that
  379. * seems like a less-bad option.
  380. */
  381. LARGE_INTEGER ticks;
  382. DWORD n;
  383. int bind_try = 0;
  384. for (;;) {
  385. switch (bind_try++) {
  386. case 0:
  387. /* "The returned string ends with a backslash" */
  388. n = GetTempPath(UNIX_PATH_MAX, a.unaddr.sun_path);
  389. break;
  390. case 1:
  391. /* Heckuva job with API consistency, Microsoft! Reversed argument order, and
  392. * "This path does not end with a backslash unless the Windows directory is the root directory.."
  393. */
  394. n = GetWindowsDirectory(a.unaddr.sun_path, UNIX_PATH_MAX);
  395. n += snprintf(a.unaddr.sun_path + n, UNIX_PATH_MAX - n, "\\Temp\\");
  396. break;
  397. case 2:
  398. n = snprintf(a.unaddr.sun_path, UNIX_PATH_MAX, "C:\\Temp\\");
  399. break;
  400. case 3:
  401. n = 0; /* Current directory */
  402. break;
  403. case 4:
  404. goto fallback;
  405. }
  406. /* GetTempFileName could be used here.
  407. * (https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettempfilenamea)
  408. * However it only adds 16 bits of time-based random bits,
  409. * fails if there isn't room for a 14-character filename, and
  410. * seems to offers no other apparent advantages. So we will
  411. * use high-res timer ticks and PID for filename.
  412. */
  413. QueryPerformanceCounter(&ticks);
  414. snprintf(a.unaddr.sun_path + n, UNIX_PATH_MAX - n,
  415. "%"PRIx64"-%lu.$$$", ticks.QuadPart, GetCurrentProcessId());
  416. a.unaddr.sun_family = AF_UNIX;
  417. if (bind(listener, &a.addr, addrlen) != SOCKET_ERROR)
  418. break;
  419. }
  420. } else {
  421. a.inaddr.sin_family = AF_INET;
  422. a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
  423. a.inaddr.sin_port = 0;
  424. if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR,
  425. (char *) &reuse, (socklen_t) sizeof(reuse)) == -1)
  426. goto fallback;
  427. if (bind(listener, &a.addr, addrlen) == SOCKET_ERROR)
  428. goto fallback;
  429. memset(&a, 0, sizeof(a));
  430. if (getsockname(listener, &a.addr, &addrlen) == SOCKET_ERROR)
  431. goto fallback;
  432. // win32 getsockname may only set the port number, p=0.0005.
  433. // ( https://docs.microsoft.com/windows/win32/api/winsock/nf-winsock-getsockname ):
  434. a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
  435. a.inaddr.sin_family = AF_INET;
  436. }
  437. if (listen(listener, 1) == SOCKET_ERROR)
  438. goto fallback;
  439. socks[0] = WSASocket(domain, SOCK_STREAM, 0, NULL, 0, flags);
  440. if (socks[0] == INVALID_SOCKET)
  441. goto fallback;
  442. if (connect(socks[0], &a.addr, addrlen) == SOCKET_ERROR)
  443. goto fallback;
  444. if (domain == AF_UNIX)
  445. DeleteFile(a.unaddr.sun_path); // Socket file no longer needed
  446. socks[1] = accept(listener, NULL, NULL);
  447. if (socks[1] == INVALID_SOCKET)
  448. goto fallback;
  449. closesocket(listener);
  450. return 0;
  451. fallback:
  452. domain = AF_INET;
  453. addrlen = sizeof(a.inaddr);
  454. e = WSAGetLastError();
  455. closesocket(listener);
  456. closesocket(socks[0]);
  457. closesocket(socks[1]);
  458. WSASetLastError(e);
  459. }
  460. socks[0] = socks[1] = -1;
  461. return SOCKET_ERROR;
  462. }
  463. #endif