script.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. /*
  2. * OpenConnect (SSL + DTLS) VPN client
  3. *
  4. * Copyright © 2008-2015 Intel Corporation.
  5. *
  6. * Author: 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 <unistd.h>
  20. #include <sys/types.h>
  21. #include <sys/stat.h>
  22. #include <fcntl.h>
  23. #ifdef _WIN32
  24. #include <process.h>
  25. #define getpid _getpid
  26. #else
  27. #include <sys/wait.h>
  28. #endif
  29. #include <errno.h>
  30. #include <ctype.h>
  31. #include <string.h>
  32. #include <stdio.h>
  33. #include <stdlib.h>
  34. int script_setenv(struct openconnect_info *vpninfo,
  35. const char *opt, const char *val, int trunc, int append)
  36. {
  37. struct oc_vpn_option *p;
  38. char *str;
  39. for (p = vpninfo->script_env; p; p = p->next) {
  40. if (!strcmp(opt, p->option)) {
  41. if (append) {
  42. if (asprintf(&str, "%s %s", p->value, val) == -1)
  43. return -ENOMEM;
  44. } else
  45. str = val ? strdup(val) : NULL;
  46. free (p->value);
  47. p->value = str;
  48. return 0;
  49. }
  50. }
  51. p = malloc(sizeof(*p));
  52. if (!p)
  53. return -ENOMEM;
  54. p->next = vpninfo->script_env;
  55. p->option = strdup(opt);
  56. p->value = val ? (trunc ? strndup(val, trunc) : strdup(val)) : NULL;
  57. vpninfo->script_env = p;
  58. return 0;
  59. }
  60. int script_setenv_int(struct openconnect_info *vpninfo, const char *opt, int value)
  61. {
  62. char buf[16];
  63. sprintf(buf, "%d", value);
  64. return script_setenv(vpninfo, opt, buf, 0, 0);
  65. }
  66. static inline int cls(uint32_t word)
  67. {
  68. #if defined(HAVE_BUILTIN_CLZ) && UINT_MAX == UINT32_MAX
  69. word = ~word;
  70. if (!word)
  71. return 32;
  72. return __builtin_clz(word);
  73. #else
  74. int masklen = 0;
  75. while (word & 0x80000000) {
  76. word <<= 1;
  77. masklen++;
  78. }
  79. return masklen;
  80. #endif
  81. }
  82. static int netmasklen(struct in_addr addr)
  83. {
  84. return cls(ntohl(addr.s_addr));
  85. }
  86. static int netmasklen6(struct in6_addr *addr)
  87. {
  88. int masklen;
  89. uint32_t *p = (uint32_t *)(addr->s6_addr);
  90. for (masklen = 0; masklen < 128; p++, masklen += 32) {
  91. uint32_t v = ntohl(*p);
  92. if (~v == 0)
  93. continue;
  94. return masklen + cls(v);
  95. }
  96. return 128;
  97. }
  98. static uint32_t netmaskbits(int masklen)
  99. {
  100. if (masklen)
  101. return htonl(0xffffffff << (32-masklen));
  102. else /* Shifting by 32 is invalid, so special-case it */
  103. return 0;
  104. }
  105. static int process_split_xxclude(struct openconnect_info *vpninfo,
  106. int include, const char *route, int *v4_incs,
  107. int *v6_incs)
  108. {
  109. struct in_addr net_addr, mask_addr;
  110. const char *in_ex = include ? "IN" : "EX";
  111. char envname[80], uptoslash[20], abuf[INET_ADDRSTRLEN];
  112. const char *slash;
  113. char *endp;
  114. int masklen;
  115. slash = strchr(route, '/');
  116. envname[79] = uptoslash[19] = 0;
  117. if (strchr(route, ':')) {
  118. snprintf(envname, 79, "CISCO_IPV6_SPLIT_%sC_%d_ADDR", in_ex,
  119. *v6_incs);
  120. script_setenv(vpninfo, envname, route, slash ? slash - route : 0, 0);
  121. /* Accept IPv6 netmask in several forms */
  122. snprintf(envname, 79, "CISCO_IPV6_SPLIT_%sC_%d_MASKLEN", in_ex,
  123. *v6_incs);
  124. if (!slash) {
  125. /* no mask (same as /128) */
  126. script_setenv_int(vpninfo, envname, 128);
  127. } else if ((masklen = strtol(slash+1, &endp, 10))<=128 && (*endp=='\0' || isspace(*endp))) {
  128. /* mask is /N */
  129. script_setenv_int(vpninfo, envname, masklen);
  130. } else {
  131. /* mask is /dead:beef:: */
  132. struct in6_addr a;
  133. if (inet_pton(AF_INET6, slash+1, &a) <= 0)
  134. goto bad;
  135. masklen = netmasklen6(&a);
  136. /* something invalid like /ffff::1 */
  137. for (int ii = (masklen >> 3); ii < 16; ii++) {
  138. if (ii == (masklen >> 3) && (~a.s6_addr[ii] & 0xff) != (0xff >> (masklen & 0x07)))
  139. goto bad;
  140. else if (a.s6_addr[ii] != 0)
  141. goto bad;
  142. }
  143. script_setenv_int(vpninfo, envname, masklen);
  144. }
  145. (*v6_incs)++;
  146. return 0;
  147. }
  148. if (!slash)
  149. strncpy(uptoslash, route, sizeof(uptoslash)-1);
  150. else {
  151. int l = MIN(slash - route, sizeof(uptoslash)-1);
  152. strncpy(uptoslash, route, l);
  153. uptoslash[l] = 0;
  154. }
  155. /* Network address must be parseable */
  156. if (!inet_aton(uptoslash, &net_addr)) {
  157. bad:
  158. if (include)
  159. vpn_progress(vpninfo, PRG_ERR,
  160. _("Discard bad split include: \"%s\"\n"),
  161. route);
  162. else
  163. vpn_progress(vpninfo, PRG_ERR,
  164. _("Discard bad split exclude: \"%s\"\n"),
  165. route);
  166. return -EINVAL;
  167. }
  168. /* Accept netmask in several forms */
  169. if (!slash) {
  170. /* no mask (same as /32) */
  171. masklen = 32;
  172. mask_addr.s_addr = netmaskbits(32);
  173. } else if ((masklen = strtol(slash+1, &endp, 10))<=32 && *endp!='.') {
  174. /* mask is /N */
  175. mask_addr.s_addr = netmaskbits(masklen);
  176. } else if (inet_aton(slash+1, &mask_addr)) {
  177. /* mask is /A.B.C.D */
  178. masklen = netmasklen(mask_addr);
  179. /* something invalid like /255.0.0.1 */
  180. if (netmaskbits(masklen) != mask_addr.s_addr)
  181. goto bad;
  182. } else
  183. goto bad;
  184. /* Fix incorrectly-set host bits */
  185. if (net_addr.s_addr & ~mask_addr.s_addr) {
  186. net_addr.s_addr &= mask_addr.s_addr;
  187. inet_ntop(AF_INET, &net_addr, abuf, sizeof(abuf));
  188. if (include)
  189. vpn_progress(vpninfo, PRG_ERR,
  190. _("WARNING: Split include \"%s\" has host bits set, replacing with \"%s/%d\".\n"),
  191. route, abuf, masklen);
  192. else
  193. vpn_progress(vpninfo, PRG_ERR,
  194. _("WARNING: Split exclude \"%s\" has host bits set, replacing with \"%s/%d\".\n"),
  195. route, abuf, masklen);
  196. }
  197. snprintf(envname, 79, "CISCO_SPLIT_%sC_%d_ADDR", in_ex, *v4_incs);
  198. script_setenv(vpninfo, envname, inet_ntop(AF_INET, &net_addr, abuf, sizeof(abuf)), 0, 0);
  199. snprintf(envname, 79, "CISCO_SPLIT_%sC_%d_MASK", in_ex, *v4_incs);
  200. script_setenv(vpninfo, envname, inet_ntop(AF_INET, &mask_addr, abuf, sizeof(abuf)), 0, 0);
  201. snprintf(envname, 79, "CISCO_SPLIT_%sC_%d_MASKLEN", in_ex, *v4_incs);
  202. script_setenv_int(vpninfo, envname, masklen);
  203. (*v4_incs)++;
  204. return 0;
  205. }
  206. static void setenv_cstp_opts(struct openconnect_info *vpninfo)
  207. {
  208. char *env_buf;
  209. int buflen = 0;
  210. int bufofs = 0;
  211. struct oc_vpn_option *opt;
  212. for (opt = vpninfo->cstp_options; opt; opt = opt->next)
  213. buflen += 2 + strlen(opt->option) + strlen(opt->value);
  214. env_buf = malloc(buflen + 1);
  215. if (!env_buf)
  216. return;
  217. env_buf[buflen] = 0;
  218. for (opt = vpninfo->cstp_options; opt; opt = opt->next)
  219. bufofs += snprintf(env_buf + bufofs, buflen - bufofs,
  220. "%s=%s\n", opt->option, opt->value);
  221. script_setenv(vpninfo, "CISCO_CSTP_OPTIONS", env_buf, 0, 0);
  222. free(env_buf);
  223. }
  224. static unsigned char nybble(unsigned char n)
  225. {
  226. if (n >= '0' && n <= '9') return n - '0';
  227. else if (n >= 'A' && n <= 'F') return n - ('A' - 10);
  228. else if (n >= 'a' && n <= 'f') return n - ('a' - 10);
  229. return 0;
  230. }
  231. unsigned char unhex(const char *data)
  232. {
  233. return (nybble(data[0]) << 4) | nybble(data[1]);
  234. }
  235. static void set_banner(struct openconnect_info *vpninfo)
  236. {
  237. char *banner, *legacy_banner, *q;
  238. const char *p;
  239. if (!vpninfo->banner || !(banner = malloc(strlen(vpninfo->banner)+1))) {
  240. script_setenv(vpninfo, "CISCO_BANNER", NULL, 0, 0);
  241. return;
  242. }
  243. p = vpninfo->banner;
  244. q = banner;
  245. while (*p) {
  246. if (*p == '%' && isxdigit((int)(unsigned char)p[1]) &&
  247. isxdigit((int)(unsigned char)p[2])) {
  248. *(q++) = unhex(p + 1);
  249. p += 3;
  250. } else
  251. *(q++) = *(p++);
  252. }
  253. *q = 0;
  254. legacy_banner = openconnect_utf8_to_legacy(vpninfo, banner);
  255. script_setenv(vpninfo, "CISCO_BANNER", legacy_banner, 0, 0);
  256. if (legacy_banner != banner)
  257. free(legacy_banner);
  258. free(banner);
  259. }
  260. void prepare_script_env(struct openconnect_info *vpninfo)
  261. {
  262. if (vpninfo->ip_info.gateway_addr)
  263. script_setenv(vpninfo, "VPNGATEWAY", vpninfo->ip_info.gateway_addr, 0, 0);
  264. set_banner(vpninfo);
  265. script_setenv(vpninfo, "CISCO_SPLIT_INC", NULL, 0, 0);
  266. script_setenv(vpninfo, "CISCO_SPLIT_EXC", NULL, 0, 0);
  267. script_setenv_int(vpninfo, "INTERNAL_IP4_MTU", vpninfo->ip_info.mtu);
  268. script_setenv_int(vpninfo, "VPNPID", (int)getpid());
  269. script_setenv_int(vpninfo, "LOG_LEVEL", vpninfo->verbose);
  270. if (vpninfo->idle_timeout)
  271. script_setenv_int(vpninfo, "IDLE_TIMEOUT", vpninfo->idle_timeout);
  272. else
  273. script_setenv(vpninfo, "IDLE_TIMEOUT", NULL, 0, 0);
  274. if (vpninfo->ip_info.addr) {
  275. script_setenv(vpninfo, "INTERNAL_IP4_ADDRESS", vpninfo->ip_info.addr, 0, 0);
  276. if (vpninfo->ip_info.netmask) {
  277. struct in_addr addr;
  278. struct in_addr mask;
  279. if (!inet_aton(vpninfo->ip_info.addr, &addr))
  280. vpn_progress(vpninfo, PRG_ERR,
  281. _("Ignoring legacy network because address \"%s\" is invalid.\n"),
  282. vpninfo->ip_info.addr);
  283. else if (!inet_aton(vpninfo->ip_info.netmask, &mask))
  284. bad_netmask:
  285. vpn_progress(vpninfo, PRG_ERR,
  286. _("Ignoring legacy network because netmask \"%s\" is invalid.\n"),
  287. vpninfo->ip_info.netmask);
  288. else {
  289. char netaddr[INET_ADDRSTRLEN];
  290. int masklen = netmasklen(mask);
  291. if (netmaskbits(masklen) != mask.s_addr)
  292. goto bad_netmask;
  293. addr.s_addr &= mask.s_addr;
  294. inet_ntop(AF_INET, &addr, netaddr, sizeof(netaddr));
  295. script_setenv(vpninfo, "INTERNAL_IP4_NETADDR", netaddr, 0, 0);
  296. script_setenv(vpninfo, "INTERNAL_IP4_NETMASK", vpninfo->ip_info.netmask, 0, 0);
  297. script_setenv_int(vpninfo, "INTERNAL_IP4_NETMASKLEN", masklen);
  298. }
  299. }
  300. }
  301. if (vpninfo->ip_info.addr6)
  302. script_setenv(vpninfo, "INTERNAL_IP6_ADDRESS", vpninfo->ip_info.addr6, 0, 0);
  303. if (vpninfo->ip_info.netmask6)
  304. script_setenv(vpninfo, "INTERNAL_IP6_NETMASK", vpninfo->ip_info.netmask6, 0, 0);
  305. /* The 'netmask6' is actually the address *and* netmask. From which we
  306. * obtain just the address on its own, if we don't have it separately */
  307. if (vpninfo->ip_info.netmask6 && !vpninfo->ip_info.addr6) {
  308. char *slash = strchr(vpninfo->ip_info.netmask6, '/');
  309. if (slash)
  310. script_setenv(vpninfo, "INTERNAL_IP6_ADDRESS", vpninfo->ip_info.netmask6,
  311. slash - vpninfo->ip_info.netmask6, 0);
  312. }
  313. if (vpninfo->ip_info.dns[0])
  314. script_setenv(vpninfo, "INTERNAL_IP4_DNS", vpninfo->ip_info.dns[0], 0, 0);
  315. else
  316. script_setenv(vpninfo, "INTERNAL_IP4_DNS", NULL, 0, 0);
  317. if (vpninfo->ip_info.dns[1])
  318. script_setenv(vpninfo, "INTERNAL_IP4_DNS", vpninfo->ip_info.dns[1], 0, 1);
  319. if (vpninfo->ip_info.dns[2])
  320. script_setenv(vpninfo, "INTERNAL_IP4_DNS", vpninfo->ip_info.dns[2], 0, 1);
  321. if (vpninfo->ip_info.nbns[0])
  322. script_setenv(vpninfo, "INTERNAL_IP4_NBNS", vpninfo->ip_info.nbns[0], 0, 0);
  323. else
  324. script_setenv(vpninfo, "INTERNAL_IP4_NBNS", NULL, 0, 0);
  325. if (vpninfo->ip_info.nbns[1])
  326. script_setenv(vpninfo, "INTERNAL_IP4_NBNS", vpninfo->ip_info.nbns[1], 0, 1);
  327. if (vpninfo->ip_info.nbns[2])
  328. script_setenv(vpninfo, "INTERNAL_IP4_NBNS", vpninfo->ip_info.nbns[2], 0, 1);
  329. if (vpninfo->ip_info.domain)
  330. script_setenv(vpninfo, "CISCO_DEF_DOMAIN", vpninfo->ip_info.domain, 0, 0);
  331. else
  332. script_setenv(vpninfo, "CISCO_DEF_DOMAIN", NULL, 0, 0);
  333. if (vpninfo->ip_info.proxy_pac)
  334. script_setenv(vpninfo, "CISCO_PROXY_PAC", vpninfo->ip_info.proxy_pac, 0, 0);
  335. if (vpninfo->ip_info.split_dns) {
  336. char *list;
  337. int len = 0;
  338. struct oc_split_include *dns = vpninfo->ip_info.split_dns;
  339. while (dns) {
  340. len += strlen(dns->route) + 1;
  341. dns = dns->next;
  342. }
  343. list = malloc(len);
  344. if (list) {
  345. char *p = list;
  346. dns = vpninfo->ip_info.split_dns;
  347. while (1) {
  348. strcpy(p, dns->route);
  349. p += strlen(p);
  350. dns = dns->next;
  351. if (!dns)
  352. break;
  353. *(p++) = ',';
  354. }
  355. script_setenv(vpninfo, "CISCO_SPLIT_DNS", list, 0, 0);
  356. free(list);
  357. }
  358. }
  359. if (vpninfo->ip_info.split_includes) {
  360. struct oc_split_include *this = vpninfo->ip_info.split_includes;
  361. int nr_split_includes = 0;
  362. int nr_v6_split_includes = 0;
  363. while (this) {
  364. process_split_xxclude(vpninfo, 1, this->route,
  365. &nr_split_includes,
  366. &nr_v6_split_includes);
  367. this = this->next;
  368. }
  369. if (nr_split_includes)
  370. script_setenv_int(vpninfo, "CISCO_SPLIT_INC", nr_split_includes);
  371. if (nr_v6_split_includes)
  372. script_setenv_int(vpninfo, "CISCO_IPV6_SPLIT_INC", nr_v6_split_includes);
  373. }
  374. if (vpninfo->ip_info.split_excludes) {
  375. struct oc_split_include *this = vpninfo->ip_info.split_excludes;
  376. int nr_split_excludes = 0;
  377. int nr_v6_split_excludes = 0;
  378. while (this) {
  379. process_split_xxclude(vpninfo, 0, this->route,
  380. &nr_split_excludes,
  381. &nr_v6_split_excludes);
  382. this = this->next;
  383. }
  384. if (nr_split_excludes)
  385. script_setenv_int(vpninfo, "CISCO_SPLIT_EXC", nr_split_excludes);
  386. if (nr_v6_split_excludes)
  387. script_setenv_int(vpninfo, "CISCO_IPV6_SPLIT_EXC", nr_v6_split_excludes);
  388. }
  389. setenv_cstp_opts(vpninfo);
  390. }
  391. void free_split_routes(struct oc_ip_info *ip_info)
  392. {
  393. struct oc_split_include *inc;
  394. for (inc = ip_info->split_includes; inc; ) {
  395. struct oc_split_include *next = inc->next;
  396. free(inc);
  397. inc = next;
  398. }
  399. for (inc = ip_info->split_excludes; inc; ) {
  400. struct oc_split_include *next = inc->next;
  401. free(inc);
  402. inc = next;
  403. }
  404. for (inc = ip_info->split_dns; inc; ) {
  405. struct oc_split_include *next = inc->next;
  406. free(inc);
  407. inc = next;
  408. }
  409. ip_info->split_dns = ip_info->split_includes =
  410. ip_info->split_excludes = NULL;
  411. }
  412. #ifdef _WIN32
  413. static wchar_t *create_script_env(struct openconnect_info *vpninfo)
  414. {
  415. struct oc_vpn_option *opt;
  416. struct oc_text_buf *envbuf;
  417. wchar_t **oldenv, **p, *newenv = NULL;
  418. int nr_envs = 0, i;
  419. /* _wenviron is NULL until we call _wgetenv() */
  420. (void)_wgetenv(L"PATH");
  421. /* Take a copy of _wenviron (but not of its strings) */
  422. for (p = _wenviron; *p; p++)
  423. nr_envs++;
  424. oldenv = malloc(nr_envs * sizeof(*oldenv));
  425. if (!oldenv)
  426. return NULL;
  427. memcpy(oldenv, _wenviron, nr_envs * sizeof(*oldenv));
  428. envbuf = buf_alloc();
  429. /* Add the script environment variables, prodding out any members of
  430. oldenv which are obsoleted by them. */
  431. for (opt = vpninfo->script_env; opt && !buf_error(envbuf); opt = opt->next) {
  432. struct oc_text_buf *buf;
  433. buf = buf_alloc();
  434. buf_append_utf16le(buf, opt->option);
  435. buf_append_utf16le(buf, "=");
  436. if (buf_error(buf)) {
  437. buf_free(buf);
  438. goto err;
  439. }
  440. /* See if we can find it in the existing environment */
  441. for (i = 0; i < nr_envs; i++) {
  442. if (oldenv[i] &&
  443. !wcsncmp((wchar_t *)buf->data, oldenv[i], buf->pos / 2)) {
  444. oldenv[i] = NULL;
  445. break;
  446. }
  447. }
  448. if (opt->value) {
  449. buf_append_bytes(envbuf, buf->data, buf->pos);
  450. buf_append_utf16le(envbuf, opt->value);
  451. buf_append_bytes(envbuf, "\0\0", 2);
  452. }
  453. buf_free(buf);
  454. }
  455. for (i = 0; i < nr_envs && !buf_error(envbuf); i++) {
  456. if (oldenv[i])
  457. buf_append_bytes(envbuf, oldenv[i],
  458. (wcslen(oldenv[i]) + 1) * sizeof(wchar_t));
  459. }
  460. buf_append_bytes(envbuf, "\0\0", 2);
  461. if (!buf_error(envbuf)) {
  462. newenv = (wchar_t *)envbuf->data;
  463. envbuf->data = NULL;
  464. }
  465. err:
  466. free(oldenv);
  467. buf_free(envbuf);
  468. return newenv;
  469. }
  470. int script_config_tun(struct openconnect_info *vpninfo, const char *reason)
  471. {
  472. wchar_t *script_w;
  473. wchar_t *script_env;
  474. int nr_chars;
  475. int ret;
  476. char *cmd;
  477. PROCESS_INFORMATION pi;
  478. STARTUPINFOW si;
  479. DWORD cpflags, exit_status;
  480. if (!vpninfo->vpnc_script || vpninfo->script_tun)
  481. return 0;
  482. memset(&si, 0, sizeof(si));
  483. si.cb = sizeof(si);
  484. /* probably superfluous */
  485. si.dwFlags = STARTF_USESHOWWINDOW;
  486. si.wShowWindow = SW_HIDE;
  487. script_setenv(vpninfo, "reason", reason, 0, 0);
  488. if (asprintf(&cmd, "cscript.exe \"%s\"", vpninfo->vpnc_script) == -1)
  489. return 0;
  490. nr_chars = MultiByteToWideChar(CP_UTF8, 0, cmd, -1, NULL, 0);
  491. script_w = malloc(nr_chars * sizeof(wchar_t));
  492. if (!script_w) {
  493. free(cmd);
  494. return -ENOMEM;
  495. }
  496. MultiByteToWideChar(CP_UTF8, 0, cmd, -1, script_w, nr_chars);
  497. free(cmd);
  498. script_env = create_script_env(vpninfo);
  499. cpflags = CREATE_UNICODE_ENVIRONMENT;
  500. /* If we're running from a console, let the script use it too. */
  501. if (!GetConsoleWindow())
  502. cpflags |= CREATE_NO_WINDOW;
  503. if (CreateProcessW(NULL, script_w, NULL, NULL, FALSE, cpflags,
  504. script_env, NULL, &si, &pi)) {
  505. ret = WaitForSingleObject(pi.hProcess,10000);
  506. if (!GetExitCodeProcess(pi.hProcess, &exit_status)) {
  507. vpn_progress(vpninfo, PRG_ERR,
  508. _("Failed to get script exit status: %s\n"),
  509. openconnect__win32_strerror(GetLastError()));
  510. ret = -EIO;
  511. } else if (exit_status > 0 && exit_status != STILL_ACTIVE) {
  512. /* STILL_ACTIVE == 259. That means that a perfectly normal positive integer return value overlaps with
  513. * an exceptional condition. Don't blame me. I didn't design this.
  514. * https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getexitcodeprocess#remarks
  515. */
  516. vpn_progress(vpninfo, PRG_ERR,
  517. _("Script '%s' returned error %ld\n"),
  518. vpninfo->vpnc_script, exit_status);
  519. ret = -EIO;
  520. }
  521. CloseHandle(pi.hThread);
  522. CloseHandle(pi.hProcess);
  523. if (ret == WAIT_TIMEOUT || exit_status == STILL_ACTIVE) {
  524. vpn_progress(vpninfo, PRG_ERR,
  525. _("Script did not complete within 10 seconds.\n"));
  526. ret = -ETIMEDOUT;
  527. } else if (ret != -EIO)
  528. ret = 0;
  529. } else {
  530. ret = -EIO;
  531. }
  532. free(script_env);
  533. if (ret < 0) {
  534. char *errstr = openconnect__win32_strerror(GetLastError());
  535. vpn_progress(vpninfo, PRG_ERR,
  536. _("Failed to spawn script '%s' for %s: %s\n"),
  537. vpninfo->vpnc_script, reason, errstr);
  538. free(errstr);
  539. goto cleanup;
  540. }
  541. cleanup:
  542. free(script_w);
  543. return ret;
  544. }
  545. #else
  546. /* Must only be run after fork(). */
  547. int apply_script_env(struct oc_vpn_option *envs)
  548. {
  549. struct oc_vpn_option *p;
  550. for (p = envs; p; p = p->next) {
  551. if (p->value)
  552. setenv(p->option, p->value, 1);
  553. else
  554. unsetenv(p->option);
  555. }
  556. return 0;
  557. }
  558. int script_config_tun(struct openconnect_info *vpninfo, const char *reason)
  559. {
  560. int ret;
  561. pid_t pid;
  562. if (!vpninfo->vpnc_script || vpninfo->script_tun)
  563. return 0;
  564. pid = fork();
  565. if (!pid) {
  566. /* Child */
  567. char *script = openconnect_utf8_to_legacy(vpninfo, vpninfo->vpnc_script);
  568. apply_script_env(vpninfo->script_env);
  569. setenv("reason", reason, 1);
  570. execl("/bin/sh", "/bin/sh", "-c", script, NULL);
  571. exit(127);
  572. }
  573. if (pid == -1 || waitpid(pid, &ret, 0) == -1) {
  574. int err = errno;
  575. vpn_progress(vpninfo, PRG_ERR,
  576. _("Failed to spawn script '%s' for %s: %s\n"),
  577. vpninfo->vpnc_script, reason, strerror(err));
  578. return -err;
  579. }
  580. if (!WIFEXITED(ret)) {
  581. vpn_progress(vpninfo, PRG_ERR,
  582. _("Script '%s' exited abnormally (%x)\n"),
  583. vpninfo->vpnc_script, ret);
  584. return -EIO;
  585. }
  586. ret = WEXITSTATUS(ret);
  587. if (ret) {
  588. vpn_progress(vpninfo, PRG_ERR,
  589. _("Script '%s' returned error %d\n"),
  590. vpninfo->vpnc_script, ret);
  591. return -EIO;
  592. }
  593. return 0;
  594. }
  595. #endif