h3c.c 13 KB

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