h3c.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. // SPDX-License-Identifier: LGPL-2.1-or-later
  2. // Author: vimacs <vimacs.hacks@gmail.com>
  3. // NOTE: we need to include this first
  4. #include <config.h>
  5. #include "openconnect-internal.h"
  6. #include <assert.h>
  7. #include <libxml/tree.h>
  8. #if 0
  9. static void
  10. print_element_names(xmlNode * a_node)
  11. {
  12. xmlNode *cur_node = NULL;
  13. for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
  14. if (cur_node->type == XML_ELEMENT_NODE) {
  15. printf("node type: Element, name: %s\n", cur_node->name);
  16. }
  17. print_element_names(cur_node->children);
  18. }
  19. }
  20. #endif
  21. static int h3c_request_user_auth(struct openconnect_info *vpninfo, char *username, char *password)
  22. {
  23. struct oc_auth_form f;
  24. struct oc_form_opt o[2];
  25. int ret;
  26. memset(&f, 0, sizeof(f));
  27. memset(o, 0, sizeof(o));
  28. f.auth_id = (char*)"H3C user";
  29. f.message = _("Enter user credentials:");
  30. if (true) {
  31. f.opts = &o[0];
  32. o[0].next = NULL; /* Again, for now */
  33. o[0].type = OC_FORM_OPT_TEXT;
  34. o[0].name = (char *)"username";
  35. o[0].label = (char *) _("Username:");
  36. }
  37. if (true) {
  38. /* Might be referenced from o[0] or directly from f.opts */
  39. o[0].next = &o[1];
  40. o[1].type = OC_FORM_OPT_PASSWORD;
  41. o[1].name = (char *)"password";
  42. o[1].label = (char *) _("Password:");
  43. }
  44. ret = process_auth_form(vpninfo, &f);
  45. if (ret != 0) {
  46. return ret;
  47. }
  48. if (o[0]._value) {
  49. // FIXME: Yes, it's unsafe...
  50. strcpy(username, o[0]._value);
  51. free_pass(&o[0]._value);
  52. }
  53. if (o[1]._value) {
  54. strcpy(password, o[1]._value);
  55. free_pass(&o[1]._value);
  56. }
  57. return 0;
  58. }
  59. static int h3c_get_vpn_param_from_headers(struct openconnect_info *vpninfo, char *k, char *v)
  60. {
  61. bool set_value = true;
  62. if (strcmp(k, "IPADDRESS") == 0) {
  63. vpninfo->ip_info.addr = strdup(v);
  64. } else if (strcmp(k, "SUBNETMASK") == 0) {
  65. if (strcmp(v, "24") == 0) {
  66. vpninfo->ip_info.netmask = strdup("255.255.255.0");
  67. } else {
  68. vpn_progress(vpninfo, PRG_ERR,
  69. "Unknown net mask %s\n",
  70. v);
  71. }
  72. } else if (strcmp(k, "GATEWAY") == 0) {
  73. vpninfo->ip_info.gateway_addr = strdup(v);
  74. } else if (strcmp(k, "ROUTES") == 0) {
  75. // ROUTES: ip/mask;ip/mask;...;ip/mask
  76. const char *vp = v;
  77. while (*vp != 0) {
  78. const char *scanpos = vp;
  79. while (*scanpos != 0 && *scanpos != ';') {
  80. ++scanpos;
  81. }
  82. if (scanpos != vp) {
  83. struct oc_split_include *route = (struct oc_split_include*)malloc(sizeof(struct oc_split_include));
  84. route->route = strndup(vp, scanpos - vp);
  85. route->next = vpninfo->ip_info.split_includes;
  86. vpninfo->ip_info.split_includes = route;
  87. }
  88. vp = scanpos + 1;
  89. }
  90. } else {
  91. set_value = false;
  92. }
  93. if (set_value && vpninfo->dump_http_traffic) {
  94. char buf[1000];
  95. strcpy(buf, k);
  96. strcat(buf, ":");
  97. strcat(buf, v);
  98. dump_buf(vpninfo, '<', buf);
  99. }
  100. return 0;
  101. }
  102. static int h3c_authenticate(struct openconnect_info *vpninfo)
  103. {
  104. int ret;
  105. ret = openconnect_open_https(vpninfo);
  106. if (ret) {
  107. return ret;
  108. }
  109. assert(vpninfo->ssl_write);
  110. struct oc_text_buf *reqbuf = buf_alloc();
  111. buf_append(reqbuf, "GET /svpn/index.cgi HTTP/1.1\r\n");
  112. char *orig_ua = vpninfo->useragent;
  113. vpninfo->useragent = (char*)"SSLVPN-Client/3.0";
  114. http_common_headers(vpninfo, reqbuf);
  115. vpninfo->useragent = orig_ua;
  116. buf_append(reqbuf, "\r\n");
  117. if (buf_error(reqbuf)) {
  118. vpn_progress(vpninfo, PRG_ERR,
  119. _("Error creating H3C connection request\n"));
  120. ret = buf_error(reqbuf);
  121. goto out;
  122. }
  123. if (vpninfo->dump_http_traffic) {
  124. dump_buf(vpninfo, '>', reqbuf->data);
  125. }
  126. ret = vpninfo->ssl_write(vpninfo, reqbuf->data, reqbuf->pos);
  127. if (ret < 0) {
  128. goto out;
  129. }
  130. struct oc_text_buf *resp_buf = buf_alloc();
  131. assert(resp_buf);
  132. if (buf_error(resp_buf)) {
  133. ret = buf_free(resp_buf);
  134. goto out;
  135. }
  136. ret = process_http_response(vpninfo, 1, NULL, resp_buf);
  137. /*
  138. if (vpninfo->dump_http_traffic) {
  139. dump_buf(vpninfo, '<', resp_buf->data);
  140. }
  141. */
  142. //buf_free(resp_buf);
  143. char *domain_list_str = NULL;
  144. if (ret == 302) {
  145. ret = handle_redirect(vpninfo);
  146. if (ret == 0) {
  147. buf_truncate(reqbuf);
  148. ret = do_https_request(vpninfo, "GET", NULL, reqbuf, &domain_list_str, NULL, HTTP_REDIRECT);
  149. if (ret > 0) {
  150. assert(domain_list_str != NULL);
  151. dump_buf(vpninfo, '<', domain_list_str);
  152. }
  153. }
  154. }
  155. assert(domain_list_str);
  156. xmlDocPtr xml_doc = xmlReadMemory(domain_list_str, strlen(domain_list_str), "noname.xml", NULL, XML_PARSE_NOERROR);
  157. assert(xml_doc);
  158. /*Get the root element node */
  159. xmlNode *root_element = xmlDocGetRootElement(xml_doc);
  160. // now we only use the first domain
  161. char *vpn_name = NULL, *vpn_url = NULL;
  162. // print_element_names(root_element);
  163. // <data> ... </data>
  164. if (xmlnode_is_named(root_element, "data")) {
  165. // find <domainlist> ... </domainlist>
  166. for (xmlNode *dom_list_node = root_element->children; dom_list_node; dom_list_node = dom_list_node->next) {
  167. if (xmlnode_is_named(dom_list_node, "domainlist")) {
  168. // find <domain> ... </domain>
  169. for (xmlNode *dom = dom_list_node->children; dom; dom = dom->next) {
  170. if (xmlnode_is_named(dom, "domain")) {
  171. for (xmlNode *dom_prop = dom->children; dom_prop; dom_prop = dom_prop->next) {
  172. if (xmlnode_is_named(dom_prop, "name")) {
  173. xmlnode_get_val(dom_prop, "name", &vpn_name);
  174. }
  175. if (xmlnode_is_named(dom_prop, "url")) {
  176. xmlnode_get_val(dom_prop, "url", &vpn_url);
  177. }
  178. if (vpn_name != NULL && vpn_url != NULL) {
  179. goto do_login;
  180. }
  181. }
  182. }
  183. }
  184. }
  185. }
  186. }
  187. do_login:
  188. assert (vpn_name != NULL && vpn_url != NULL);
  189. if (vpn_url[0] == '/') {
  190. vpninfo->urlpath = strdup(vpn_url + 1);
  191. } else {
  192. vpninfo->urlpath = strdup(vpn_url);
  193. }
  194. buf_truncate(reqbuf);
  195. char *gateway_info_str = NULL;
  196. ret = do_https_request(vpninfo, "GET", NULL, reqbuf, &gateway_info_str, NULL, HTTP_BODY_ON_ERROR);
  197. assert (ret >= 0 && gateway_info_str != NULL);
  198. dump_buf(vpninfo, '<', gateway_info_str);
  199. char *login_url = NULL;
  200. char *logout_url = NULL;
  201. char *checkonline_url = NULL;
  202. char *challenge_url = NULL;
  203. xmlDocPtr gwinfo_xml = xmlReadMemory(gateway_info_str, strlen(gateway_info_str), "noname.xml", NULL, XML_PARSE_NOERROR);
  204. assert(gwinfo_xml);
  205. xmlNode *gwinfo_root = xmlDocGetRootElement(gwinfo_xml);
  206. assert(gwinfo_root);
  207. // <data> ... </data>
  208. if (xmlnode_is_named(gwinfo_root, "data")) {
  209. // find <gatewayinfo> ... </gatewayinfo>
  210. for (xmlNode *gw_node = gwinfo_root->children; gw_node; gw_node = gw_node->next) {
  211. if (xmlnode_is_named(gw_node, "gatewayinfo")) {
  212. // we ignore <auth> ... </auth>, just find <url> ... </url>
  213. for (xmlNode *url_nodes = gw_node->children; url_nodes; url_nodes = url_nodes->next) {
  214. if (xmlnode_is_named(url_nodes, "url")) {
  215. for (xmlNode *method_url = url_nodes->children; method_url; method_url = method_url->next) {
  216. xmlnode_get_val(method_url, "login", &login_url);
  217. xmlnode_get_val(method_url, "logout", &logout_url);
  218. xmlnode_get_val(method_url, "checkonline", &checkonline_url);
  219. xmlnode_get_val(method_url, "challenge", &challenge_url);
  220. }
  221. if (login_url != NULL && logout_url != NULL && checkonline_url != NULL && challenge_url != NULL) {
  222. goto do_visit_login_url;
  223. }
  224. }
  225. }
  226. }
  227. }
  228. }
  229. do_visit_login_url:
  230. if (login_url[0] == '/') {
  231. add_option_dup(&vpninfo->cstp_options, "login_url", login_url + 1, -1);
  232. } else {
  233. add_option_dup(&vpninfo->cstp_options, "login_url", login_url, -1);
  234. }
  235. if (logout_url[0] == '/') {
  236. add_option_dup(&vpninfo->cstp_options, "logout_url", logout_url + 1, -1);
  237. } else {
  238. add_option_dup(&vpninfo->cstp_options, "logout_url", logout_url, -1);
  239. }
  240. if (checkonline_url[0] == '/') {
  241. add_option_dup(&vpninfo->cstp_options, "checkonline_url", checkonline_url + 1, -1);
  242. } else {
  243. add_option_dup(&vpninfo->cstp_options, "checkonline_url", checkonline_url, -1);
  244. }
  245. if (challenge_url[0] == '/') {
  246. add_option_dup(&vpninfo->cstp_options, "challenge_url", challenge_url + 1, -1);
  247. } else {
  248. add_option_dup(&vpninfo->cstp_options, "challenge_url", challenge_url, -1);
  249. }
  250. char username[1000], password[1000];
  251. assert (h3c_request_user_auth(vpninfo, username, password) == 0);
  252. assert(login_url != NULL && logout_url != NULL && checkonline_url != NULL && challenge_url != NULL);
  253. if (login_url[0] == '/') {
  254. vpninfo->urlpath = strdup(login_url + 1);
  255. } else {
  256. vpninfo->urlpath = strdup(login_url);
  257. }
  258. buf_truncate(reqbuf);
  259. struct oc_text_buf *request_data = buf_alloc();
  260. buf_append(request_data, "request=");
  261. buf_append_urlencoded(request_data, "<data><username>");
  262. buf_append_urlencoded(request_data, username);
  263. buf_append_urlencoded(request_data, "</username><password>");
  264. buf_append_urlencoded(request_data, password);
  265. buf_append_urlencoded(request_data, "</password></data>\r\n");
  266. char *login_resp = NULL;
  267. ret = do_https_request(vpninfo, "POST", "application/x-www-form-urlencoded", request_data, &login_resp, NULL, HTTP_BODY_ON_ERROR);
  268. if (ret >= 0 && login_resp != NULL) {
  269. dump_buf(vpninfo, '<', login_resp);
  270. }
  271. // get VPN parameter
  272. vpninfo->urlpath = (char*)"";
  273. buf_truncate(reqbuf);
  274. buf_truncate(request_data);
  275. char *handshake_resp = NULL;
  276. do_https_request(vpninfo, "NET_EXTEND", NULL, request_data, &handshake_resp, h3c_get_vpn_param_from_headers, HTTP_BODY_ON_ERROR);
  277. /* ignore the error */
  278. ret = 0;
  279. monitor_fd_new(vpninfo, ssl);
  280. monitor_read_fd(vpninfo, ssl);
  281. monitor_except_fd(vpninfo, ssl);
  282. vpninfo->ip_info.mtu = 1400;
  283. out:
  284. buf_free(reqbuf);
  285. return ret;
  286. }
  287. int h3c_connect(struct openconnect_info *vpninfo)
  288. {
  289. return h3c_authenticate(vpninfo);
  290. }
  291. int h3c_bye(struct openconnect_info *vpninfo, const char *reason)
  292. {
  293. openconnect_close_https(vpninfo, 0);
  294. char *logout_url = NULL;
  295. for (struct oc_vpn_option *opt = vpninfo->cstp_options; opt != NULL; opt = opt->next) {
  296. if (strcmp(opt->option, "logout_url") == 0) {
  297. logout_url = opt->value;
  298. }
  299. }
  300. if (logout_url != NULL) {
  301. char *logout_resp = NULL;
  302. vpninfo->urlpath = strdup(logout_url);
  303. int ret = do_https_request(vpninfo, "GET", NULL, NULL, &logout_resp, NULL, HTTP_BODY_ON_ERROR);
  304. return ret;
  305. }
  306. return -EINVAL;
  307. }
  308. static void h3c_handle_outgoing(struct openconnect_info *vpninfo)
  309. {
  310. vpninfo->ssl_times.last_tx = time(NULL);
  311. unmonitor_write_fd(vpninfo, ssl);
  312. vpn_progress(vpninfo, PRG_TRACE, _("Packet outgoing:\n"));
  313. store_le16(&vpninfo->current_ssl_pkt->h3c.type, 1);
  314. store_be16(&vpninfo->current_ssl_pkt->h3c.len, vpninfo->current_ssl_pkt->len);
  315. int ret = ssl_nonblock_write(vpninfo, 0, &vpninfo->current_ssl_pkt->h3c.type, vpninfo->current_ssl_pkt->len + 4);
  316. if (ret < 0) {
  317. vpn_progress(vpninfo, PRG_ERR, _("Send packet failed\n"));
  318. /* TODO: we don't know what to do now */
  319. }
  320. }
  321. int h3c_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable)
  322. {
  323. while (readable) {
  324. /* Some servers send us packets that are larger than
  325. negotiated MTU. We reserve some extra space to
  326. handle that */
  327. int receive_mtu = MAX(16384, vpninfo->deflate_pkt_size ? : vpninfo->ip_info.mtu);
  328. int len;
  329. if (!vpninfo->cstp_pkt) {
  330. vpninfo->partial_rec_size = 0;
  331. vpninfo->cstp_pkt = alloc_pkt(vpninfo, receive_mtu);
  332. if (!vpninfo->cstp_pkt) {
  333. vpn_progress(vpninfo, PRG_ERR, _("Allocation failed\n"));
  334. break;
  335. }
  336. }
  337. len = ssl_nonblock_read(vpninfo, 0,
  338. vpninfo->cstp_pkt->data + vpninfo->partial_rec_size,
  339. receive_mtu - vpninfo->partial_rec_size);
  340. if (!len)
  341. break;
  342. if (len < 0) {
  343. /* goto do_reconnect; */
  344. return -1;
  345. }
  346. if (vpninfo->partial_rec_size) {
  347. vpn_progress(vpninfo, PRG_DEBUG,
  348. _("Received %d more bytes after partial %d\n"),
  349. len, vpninfo->partial_rec_size);
  350. len += vpninfo->partial_rec_size;
  351. vpninfo->partial_rec_size = len;
  352. }
  353. vpninfo->ssl_times.last_rx = time(NULL);
  354. unsigned char *buf = vpninfo->cstp_pkt->data;
  355. if (buf[0] == 1) {
  356. uint16_t iplen = load_be16(buf + 2);
  357. if (len - 4 >= iplen) {
  358. if (len - 4 != iplen) {
  359. dump_buf_hex(vpninfo, PRG_DEBUG, '>', buf, len);
  360. }
  361. memmove(buf, buf + 4, len - 4);
  362. vpninfo->cstp_pkt->len = len - 4;
  363. vpninfo->partial_rec_size = 0;
  364. vpn_progress(vpninfo, PRG_TRACE,
  365. _("Moved down %d bytes after previous packet\n"), len);
  366. }
  367. } else {
  368. dump_buf_hex(vpninfo, PRG_ERR, '>', buf, len);
  369. }
  370. queue_packet(&vpninfo->incoming_queue, vpninfo->cstp_pkt);
  371. vpninfo->cstp_pkt = NULL;
  372. }
  373. if (vpninfo->current_ssl_pkt) {
  374. /* TODO: we don't know what to do yet */
  375. h3c_handle_outgoing(vpninfo);
  376. vpninfo->current_ssl_pkt = NULL;
  377. }
  378. while (vpninfo->current_ssl_pkt = dequeue_packet(&vpninfo->outgoing_queue)) {
  379. h3c_handle_outgoing(vpninfo);
  380. }
  381. return 1;
  382. }
  383. void h3c_http_headers(struct openconnect_info *vpninfo, struct oc_text_buf *buf)
  384. {
  385. char *orig_ua = vpninfo->useragent;
  386. vpninfo->useragent = (char*)"SSLVPN-Client/7.0";
  387. http_common_headers(vpninfo, buf);
  388. vpninfo->useragent = orig_ua;
  389. }
  390. int h3c_obtain_cookie(struct openconnect_info *vpninfo)
  391. {
  392. return 0;
  393. }