auth.c 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978
  1. /*
  2. * OpenConnect (SSL + DTLS) VPN client
  3. *
  4. * Copyright © 2008-2015 Intel Corporation.
  5. * Copyright © 2013 John Morrissey <jwm@horde.net>
  6. *
  7. * Author: David Woodhouse <dwmw2@infradead.org>
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public License
  11. * version 2.1, as published by the Free Software Foundation.
  12. *
  13. * This program is distributed in the hope that it will be useful, but
  14. * WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * Lesser General Public License for more details.
  17. */
  18. #include <config.h>
  19. #include "openconnect-internal.h"
  20. #include <libxml/parser.h>
  21. #include <libxml/tree.h>
  22. #include <unistd.h>
  23. #include <fcntl.h>
  24. #include <sys/stat.h>
  25. #include <sys/types.h>
  26. #ifndef _WIN32
  27. #include <sys/wait.h>
  28. #include <pwd.h>
  29. #include <grp.h>
  30. #endif
  31. #include <stdio.h>
  32. #include <time.h>
  33. #include <string.h>
  34. #include <ctype.h>
  35. #include <errno.h>
  36. enum {
  37. CERT1_REQUESTED = (1<<0),
  38. CERT1_AUTHENTICATED = (1<<1),
  39. CERT2_REQUESTED = (1<<2),
  40. };
  41. struct cert_request {
  42. unsigned int state:16;
  43. unsigned int hashes:16;
  44. };
  45. static int xmlpost_append_form_opts(struct openconnect_info *vpninfo,
  46. struct oc_auth_form *form, struct oc_text_buf *body);
  47. static int cstp_can_gen_tokencode(struct openconnect_info *vpninfo,
  48. struct oc_auth_form *form,
  49. struct oc_form_opt *opt);
  50. /* multiple certificate-based authentication */
  51. static void parse_multicert_request(struct openconnect_info *vpninfo,
  52. xmlNodePtr node, struct cert_request *cert_rq);
  53. static int prepare_multicert_response(struct openconnect_info *vpninfo,
  54. struct cert_request cert_rq, const char *challenge,
  55. struct oc_text_buf *body);
  56. int openconnect_set_option_value(struct oc_form_opt *opt, const char *value)
  57. {
  58. if (opt->type == OC_FORM_OPT_SELECT) {
  59. struct oc_form_opt_select *sopt = (void *)opt;
  60. int i;
  61. for (i=0; i<sopt->nr_choices; i++) {
  62. if (!strcmp(value, sopt->choices[i]->name)) {
  63. opt->_value = sopt->choices[i]->name;
  64. return 0;
  65. }
  66. }
  67. return -EINVAL;
  68. }
  69. opt->_value = strdup(value);
  70. if (!opt->_value)
  71. return -ENOMEM;
  72. return 0;
  73. }
  74. static int prop_equals(xmlNode *xml_node, const char *name, const char *value)
  75. {
  76. char *tmp = (char *)xmlGetProp(xml_node, (unsigned char *)name);
  77. int ret = 0;
  78. if (tmp && !strcasecmp(tmp, value))
  79. ret = 1;
  80. free(tmp);
  81. return ret;
  82. }
  83. static int parse_auth_choice(struct openconnect_info *vpninfo, struct oc_auth_form *form,
  84. xmlNode *xml_node)
  85. {
  86. struct oc_form_opt_select *opt;
  87. xmlNode *opt_node;
  88. int max_choices = 0, selection = 0;
  89. for (opt_node = xml_node->children; opt_node; opt_node = opt_node->next)
  90. max_choices++;
  91. /* Return early when there is a <select/> tag with no children */
  92. if (max_choices == 0) {
  93. return 0;
  94. }
  95. opt = calloc(1, sizeof(*opt));
  96. if (!opt)
  97. return -ENOMEM;
  98. opt->form.type = OC_FORM_OPT_SELECT;
  99. opt->form.name = (char *)xmlGetProp(xml_node, (unsigned char *)"name");
  100. opt->form.label = (char *)xmlGetProp(xml_node, (unsigned char *)"label");
  101. if (!opt->form.name) {
  102. vpn_progress(vpninfo, PRG_ERR, _("Form choice has no name\n"));
  103. free_opt((struct oc_form_opt *)opt);
  104. return -EINVAL;
  105. }
  106. opt->choices = calloc(1, max_choices * sizeof(struct oc_choice *));
  107. if (!opt->choices) {
  108. free_opt((struct oc_form_opt *)opt);
  109. return -ENOMEM;
  110. }
  111. for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
  112. char *form_id;
  113. struct oc_choice *choice;
  114. if (xml_node->type != XML_ELEMENT_NODE)
  115. continue;
  116. if (strcmp((char *)xml_node->name, "option"))
  117. continue;
  118. form_id = (char *)xmlGetProp(xml_node, (unsigned char *)"value");
  119. if (!form_id)
  120. form_id = (char *)xmlNodeGetContent(xml_node);
  121. if (!form_id)
  122. continue;
  123. choice = calloc(1, sizeof(*choice));
  124. if (!choice) {
  125. free_opt((struct oc_form_opt *)opt);
  126. return -ENOMEM;
  127. }
  128. choice->name = form_id;
  129. choice->label = (char *)xmlNodeGetContent(xml_node);
  130. choice->auth_type = (char *)xmlGetProp(xml_node, (unsigned char *)"auth-type");
  131. choice->override_name = (char *)xmlGetProp(xml_node, (unsigned char *)"override-name");
  132. choice->override_label = (char *)xmlGetProp(xml_node, (unsigned char *)"override-label");
  133. choice->second_auth = prop_equals(xml_node, "second-auth", "1");
  134. choice->secondary_username = (char *)xmlGetProp(xml_node,
  135. (unsigned char *)"secondary_username");
  136. choice->secondary_username_editable = prop_equals(xml_node,
  137. "secondary_username_editable", "true");
  138. choice->noaaa = prop_equals(xml_node, "noaaa", "1");
  139. if (prop_equals(xml_node, "selected", "true"))
  140. selection = opt->nr_choices;
  141. opt->choices[opt->nr_choices++] = choice;
  142. }
  143. if (!strcmp(opt->form.name, "group_list")) {
  144. form->authgroup_opt = opt;
  145. form->authgroup_selection = selection;
  146. }
  147. /* We link the choice _first_ so it's at the top of what we present
  148. to the user */
  149. opt->form.next = form->opts;
  150. form->opts = &opt->form;
  151. return 0;
  152. }
  153. static int parse_form(struct openconnect_info *vpninfo, struct oc_auth_form *form,
  154. xmlNode *xml_node)
  155. {
  156. char *input_type, *input_name, *input_label;
  157. for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
  158. struct oc_form_opt *opt, **p;
  159. if (xml_node->type != XML_ELEMENT_NODE)
  160. continue;
  161. if (!strcmp((char *)xml_node->name, "select")) {
  162. if (parse_auth_choice(vpninfo, form, xml_node))
  163. return -EINVAL;
  164. continue;
  165. }
  166. if (strcmp((char *)xml_node->name, "input")) {
  167. vpn_progress(vpninfo, PRG_DEBUG,
  168. _("name %s not input\n"), xml_node->name);
  169. continue;
  170. }
  171. input_type = (char *)xmlGetProp(xml_node, (unsigned char *)"type");
  172. if (!input_type) {
  173. vpn_progress(vpninfo, PRG_INFO,
  174. _("No input type in form\n"));
  175. continue;
  176. }
  177. if (!strcmp(input_type, "submit") || !strcmp(input_type, "reset")) {
  178. free(input_type);
  179. continue;
  180. }
  181. input_name = (char *)xmlGetProp(xml_node, (unsigned char *)"name");
  182. if (!input_name) {
  183. vpn_progress(vpninfo, PRG_INFO,
  184. _("No input name in form\n"));
  185. free(input_type);
  186. continue;
  187. }
  188. input_label = (char *)xmlGetProp(xml_node, (unsigned char *)"label");
  189. opt = calloc(1, sizeof(*opt));
  190. if (!opt) {
  191. free(input_type);
  192. free(input_name);
  193. free(input_label);
  194. return -ENOMEM;
  195. }
  196. opt->name = input_name;
  197. opt->label = input_label;
  198. opt->flags = prop_equals(xml_node, "second-auth", "1") ? OC_FORM_OPT_SECOND_AUTH : 0;
  199. if (!strcmp(input_type, "hidden")) {
  200. opt->type = OC_FORM_OPT_HIDDEN;
  201. opt->_value = (char *)xmlGetProp(xml_node, (unsigned char *)"value");
  202. } else if (!strcmp(input_type, "text")) {
  203. opt->type = OC_FORM_OPT_TEXT;
  204. } else if (!strcmp(input_type, "sso")) {
  205. opt->type = OC_FORM_OPT_SSO_TOKEN;
  206. } else if (!strcmp(input_type, "password")) {
  207. if (!cstp_can_gen_tokencode(vpninfo, form, opt))
  208. opt->type = OC_FORM_OPT_TOKEN;
  209. else
  210. opt->type = OC_FORM_OPT_PASSWORD;
  211. } else {
  212. vpn_progress(vpninfo, PRG_INFO,
  213. _("Unknown input type %s in form\n"),
  214. input_type);
  215. free(input_type);
  216. free(input_name);
  217. free(input_label);
  218. free(opt);
  219. continue;
  220. }
  221. free(input_type);
  222. p = &form->opts;
  223. while (*p)
  224. p = &(*p)->next;
  225. *p = opt;
  226. }
  227. return 0;
  228. }
  229. static char *xmlnode_msg(xmlNode *xml_node)
  230. {
  231. char *fmt = (char *)xmlNodeGetContent(xml_node);
  232. char *result, *params[2], *pct;
  233. int len;
  234. int nr_params = 0;
  235. if (!fmt || !fmt[0]) {
  236. free(fmt);
  237. return NULL;
  238. }
  239. len = strlen(fmt) + 1;
  240. params[0] = (char *)xmlGetProp(xml_node, (unsigned char *)"param1");
  241. if (params[0])
  242. len += strlen(params[0]);
  243. params[1] = (char *)xmlGetProp(xml_node, (unsigned char *)"param2");
  244. if (params[1])
  245. len += strlen(params[1]);
  246. result = malloc(len);
  247. if (!result) {
  248. result = fmt;
  249. goto out;
  250. }
  251. strcpy(result, fmt);
  252. free(fmt);
  253. for (pct = strchr(result, '%'); pct;
  254. (pct = strchr(pct, '%'))) {
  255. int paramlen;
  256. /* We only cope with '%s' */
  257. if (pct[1] != 's')
  258. goto out;
  259. if (params[nr_params]) {
  260. paramlen = strlen(params[nr_params]);
  261. /* Move rest of fmt string up... */
  262. memmove(pct + paramlen, pct + 2, strlen(pct + 2) + 1);
  263. /* ... and put the string parameter in where the '%s' was */
  264. memcpy(pct, params[nr_params], paramlen);
  265. pct += paramlen;
  266. } else
  267. pct++;
  268. if (++nr_params == 2)
  269. break;
  270. }
  271. out:
  272. free(params[0]);
  273. free(params[1]);
  274. return result;
  275. }
  276. static int xmlnode_get_text(xmlNode *xml_node, const char *name, char **var)
  277. {
  278. char *str;
  279. if (name && !xmlnode_is_named(xml_node, name))
  280. return -EINVAL;
  281. str = xmlnode_msg(xml_node);
  282. if (!str)
  283. return -ENOENT;
  284. free(*var);
  285. *var = str;
  286. return 0;
  287. }
  288. /*
  289. * Legacy server response looks like:
  290. *
  291. * <auth id="<!-- "main" for initial attempt, "success" means we have a cookie -->">
  292. * <title><!-- title to display to user --></title>
  293. * <csd
  294. * token="<!-- save to vpninfo->csd_token -->"
  295. * ticket="<!-- save to vpninfo->csd_ticket -->" />
  296. * <csd
  297. * stuburl="<!-- save to vpninfo->csd_stuburl if --os=win -->"
  298. * starturl="<!-- save to vpninfo->csd_starturl if --os=win -->"
  299. * waiturl="<!-- save to vpninfo->csd_starturl if --os=win -->"
  300. * <csdMac
  301. * stuburl="<!-- save to vpninfo->csd_stuburl if --os=mac-intel -->"
  302. * starturl="<!-- save to vpninfo->csd_starturl if --os=mac-intel -->"
  303. * waiturl="<!-- save to vpninfo->csd_waiturl if --os=mac-intel -->" />
  304. * <csdLinux
  305. * stuburl="<!-- same as above, for Linux -->"
  306. * starturl="<!-- same as above, for Linux -->"
  307. * waiturl="<!-- same as above, for Linux -->" />
  308. * <banner><!-- display this to the user --></banner>
  309. * <message>Please enter your username and password.</message>
  310. * <form method="post" action="/+webvpn+/index.html">
  311. * <input type="text" name="username" label="Username:" />
  312. * <input type="password" name="password" label="Password:" />
  313. * <input type="hidden" name="<!-- save these -->" value="<!-- ... -->" />
  314. * <input type="submit" name="Login" value="Login" />
  315. * <input type="reset" name="Clear" value="Clear" />
  316. * </form>
  317. * </auth>
  318. *
  319. * New server response looks like:
  320. *
  321. * <config-auth>
  322. * <version><!-- whatever --></version>
  323. * <session-token><!-- if present, save to vpninfo->cookie --></session-token>
  324. * <opaque>
  325. * <!-- this could contain anything; copy to vpninfo->opaque_srvdata -->
  326. * <tunnel-group>foobar</tunnel-group>
  327. * <config-hash>1234567</config-hash>
  328. * </opaque>
  329. * <auth id="<!-- see above -->
  330. * <!-- all of our old familiar fields -->
  331. * </auth>
  332. * <host-scan>
  333. * <host-scan-ticket><!-- save to vpninfo->csd_ticket --></host-scan-ticket>
  334. * <host-scan-token><!-- save to vpninfo->csd_token --></host-scan-token>
  335. * <host-scan-base-uri><!-- save to vpninfo->csd_starturl --></host-scan-base-uri>
  336. * <host-scan-wait-uri><!-- save to vpninfo->csd_waiturl --></host-scan-wait-uri>
  337. * </host-scan>
  338. * </config-auth>
  339. *
  340. * Notes:
  341. *
  342. * 1) The new host-scan-*-uri nodes do not map directly to the old CSD fields.
  343. *
  344. * 2) The new <form> tag tends to omit the method/action properties.
  345. */
  346. /* Translate platform names (derived from AnyConnect) into the relevant
  347. * CSD tag names
  348. */
  349. static inline const char *csd_tag_name(struct openconnect_info *vpninfo)
  350. {
  351. if (!strcmp(vpninfo->platname, "mac-intel"))
  352. return "csdMac";
  353. else if (!strcmp(vpninfo->platname, "win"))
  354. return "csd";
  355. else
  356. /* linux, linux-64, android, apple-ios */
  357. return "csdLinux";
  358. }
  359. /* Ignore stubs on mobile platforms */
  360. static inline int csd_use_stub(struct openconnect_info *vpninfo)
  361. {
  362. if (!strcmp(vpninfo->platname, "android") || !strcmp(vpninfo->platname, "apple-ios"))
  363. return 0;
  364. else
  365. return 1;
  366. }
  367. static int parse_auth_node(struct openconnect_info *vpninfo, xmlNode *xml_node,
  368. struct oc_auth_form *form)
  369. {
  370. int ret = 0;
  371. for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
  372. if (xml_node->type != XML_ELEMENT_NODE)
  373. continue;
  374. xmlnode_get_text(xml_node, "banner", &form->banner);
  375. xmlnode_get_text(xml_node, "message", &form->message);
  376. xmlnode_get_text(xml_node, "error", &form->error);
  377. xmlnode_get_text(xml_node, "sso-v2-login", &vpninfo->sso_login);
  378. xmlnode_get_text(xml_node, "sso-v2-login-final", &vpninfo->sso_login_final);
  379. xmlnode_get_text(xml_node, "sso-v2-token-cookie-name", &vpninfo->sso_token_cookie);
  380. xmlnode_get_text(xml_node, "sso-v2-error-cookie-name", &vpninfo->sso_error_cookie);
  381. xmlnode_get_text(xml_node, "sso-v2-browser-mode", &vpninfo->sso_browser_mode);
  382. if (xmlnode_is_named(xml_node, "form")) {
  383. /* defaults for new XML POST */
  384. form->method = strdup("POST");
  385. form->action = strdup("/");
  386. xmlnode_get_prop(xml_node, "method", &form->method);
  387. xmlnode_get_prop(xml_node, "action", &form->action);
  388. if (!form->method || !form->action ||
  389. strcasecmp(form->method, "POST") || !form->action[0]) {
  390. vpn_progress(vpninfo, PRG_ERR,
  391. _("Cannot handle form method='%s', action='%s'\n"),
  392. form->method, form->action);
  393. ret = -EINVAL;
  394. goto out;
  395. }
  396. ret = parse_form(vpninfo, form, xml_node);
  397. if (ret < 0)
  398. goto out;
  399. } else if (!vpninfo->csd_scriptname && xmlnode_is_named(xml_node, "csd")) {
  400. xmlnode_get_prop(xml_node, "token", &vpninfo->csd_token);
  401. xmlnode_get_prop(xml_node, "ticket", &vpninfo->csd_ticket);
  402. } else if (xmlnode_is_named(xml_node, "authentication-complete")) {
  403. /* Ick. Since struct oc_auth_form is public there's no
  404. * simple way to add a flag to it. So let's abuse the
  405. * auth_id string instead. */
  406. free(form->auth_id);
  407. form->auth_id = strdup("openconnect_authentication_complete");
  408. }
  409. /* For Windows, vpninfo->csd_xmltag will be "csd" and there are *two* <csd>
  410. nodes; one with token/ticket and one with the URLs. Process them both
  411. the same and rely on the fact that xmlnode_get_prop() will not *clear*
  412. the variable if no such property is found. */
  413. if (!vpninfo->csd_scriptname && xmlnode_is_named(xml_node, csd_tag_name(vpninfo))) {
  414. /* ignore the CSD trojan binary on mobile platforms */
  415. if (csd_use_stub(vpninfo))
  416. xmlnode_get_prop(xml_node, "stuburl", &vpninfo->csd_stuburl);
  417. xmlnode_get_prop(xml_node, "starturl", &vpninfo->csd_starturl);
  418. xmlnode_get_prop(xml_node, "waiturl", &vpninfo->csd_waiturl);
  419. vpninfo->csd_preurl = strdup(vpninfo->urlpath);
  420. }
  421. }
  422. out:
  423. return ret;
  424. }
  425. static int parse_host_scan_node(struct openconnect_info *vpninfo, xmlNode *xml_node)
  426. {
  427. /* ignore this whole section if the CSD trojan has already run */
  428. if (vpninfo->csd_scriptname)
  429. return 0;
  430. for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
  431. if (xml_node->type != XML_ELEMENT_NODE)
  432. continue;
  433. xmlnode_get_text(xml_node, "host-scan-ticket", &vpninfo->csd_ticket);
  434. xmlnode_get_text(xml_node, "host-scan-token", &vpninfo->csd_token);
  435. xmlnode_get_text(xml_node, "host-scan-base-uri", &vpninfo->csd_starturl);
  436. xmlnode_get_text(xml_node, "host-scan-wait-uri", &vpninfo->csd_waiturl);
  437. }
  438. return 0;
  439. }
  440. static void parse_profile_node(struct openconnect_info *vpninfo, xmlNode *xml_node)
  441. {
  442. /* ignore this whole section if we already have a URL */
  443. if (vpninfo->profile_url && vpninfo->profile_sha1)
  444. return;
  445. /* Find <vpn rev="1.0"> child... */
  446. xml_node = xml_node->children;
  447. while (1) {
  448. if (!xml_node)
  449. return;
  450. if (xml_node->type == XML_ELEMENT_NODE &&
  451. xmlnode_is_named(xml_node, "vpn") &&
  452. !xmlnode_match_prop(xml_node, "rev", "1.0"))
  453. break;
  454. xml_node = xml_node->next;
  455. }
  456. /* Find <file type="profile" service-type="user"> */
  457. xml_node = xml_node->children;
  458. while (1) {
  459. if (!xml_node)
  460. return;
  461. if (xml_node->type == XML_ELEMENT_NODE &&
  462. xmlnode_is_named(xml_node, "file") &&
  463. !xmlnode_match_prop(xml_node, "type", "profile") &&
  464. !xmlnode_match_prop(xml_node, "service-type", "user"))
  465. break;
  466. xml_node = xml_node->next;
  467. }
  468. for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
  469. if (xml_node->type != XML_ELEMENT_NODE)
  470. continue;
  471. xmlnode_get_text(xml_node, "uri", &vpninfo->profile_url);
  472. /* FIXME: Check for <hash type="sha1"> */
  473. xmlnode_get_text(xml_node, "hash", &vpninfo->profile_sha1);
  474. }
  475. }
  476. static void parse_config_node(struct openconnect_info *vpninfo, xmlNode *xml_node)
  477. {
  478. for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
  479. if (xml_node->type != XML_ELEMENT_NODE)
  480. continue;
  481. if (xmlnode_is_named(xml_node, "vpn-profile-manifest"))
  482. parse_profile_node(vpninfo, xml_node);
  483. }
  484. }
  485. /* Return value:
  486. * < 0, on error
  487. * = 0, on success; *form is populated
  488. */
  489. static int parse_xml_response(struct openconnect_info *vpninfo,
  490. char *response,struct oc_auth_form **formp,
  491. struct cert_request *cert_rq)
  492. {
  493. struct oc_auth_form *form;
  494. xmlDocPtr xml_doc;
  495. xmlNode *xml_node;
  496. int old_cert_rq_state = 0;
  497. int ret;
  498. if (*formp) {
  499. free_auth_form(*formp);
  500. *formp = NULL;
  501. }
  502. if (cert_rq) {
  503. old_cert_rq_state = cert_rq->state;
  504. *cert_rq = (struct cert_request) { 0 };
  505. }
  506. if (!response) {
  507. vpn_progress(vpninfo, PRG_DEBUG,
  508. _("Empty response from server\n"));
  509. return -EINVAL;
  510. }
  511. form = calloc(1, sizeof(*form));
  512. if (!form)
  513. return -ENOMEM;
  514. xml_doc = xmlReadMemory(response, strlen(response), "noname.xml", NULL,
  515. XML_PARSE_NOERROR|XML_PARSE_RECOVER);
  516. if (!xml_doc) {
  517. vpn_progress(vpninfo, PRG_ERR,
  518. _("Failed to parse server response\n"));
  519. vpn_progress(vpninfo, PRG_DEBUG,
  520. _("Response was:%s\n"), response);
  521. free(form);
  522. return -EINVAL;
  523. }
  524. xml_node = xmlDocGetRootElement(xml_doc);
  525. while (xml_node) {
  526. ret = 0;
  527. if (xml_node->type != XML_ELEMENT_NODE) {
  528. xml_node = xml_node->next;
  529. continue;
  530. }
  531. if (xmlnode_is_named(xml_node, "config-auth")) {
  532. /* if we do have a config-auth node, it is the root element */
  533. xml_node = xml_node->children;
  534. continue;
  535. } else if (xmlnode_is_named(xml_node, "client-cert-request")) {
  536. if (cert_rq)
  537. cert_rq->state |= CERT1_REQUESTED;
  538. else {
  539. vpn_progress(vpninfo, PRG_ERR,
  540. _("Received <client-cert-request> when not expected.\n"));
  541. ret = -EINVAL;
  542. }
  543. } else if (xmlnode_is_named(xml_node, "multiple-client-cert-request")) {
  544. if (cert_rq) {
  545. cert_rq->state |= CERT1_REQUESTED|CERT2_REQUESTED;
  546. parse_multicert_request(vpninfo, xml_node, cert_rq);
  547. } else {
  548. vpn_progress(vpninfo, PRG_ERR,
  549. _("Received <multiple-client-cert-request> when not expected.\n"));
  550. ret = -EINVAL;
  551. }
  552. } else if (xmlnode_is_named(xml_node, "cert-authenticated")) {
  553. /**
  554. * cert-authenticated indicates that the certificate for the
  555. * TLS session is valid.
  556. */
  557. if (cert_rq)
  558. cert_rq->state |= CERT1_AUTHENTICATED;
  559. } else if (xmlnode_is_named(xml_node, "auth")) {
  560. xmlnode_get_prop(xml_node, "id", &form->auth_id);
  561. ret = parse_auth_node(vpninfo, xml_node, form);
  562. } else if (xmlnode_is_named(xml_node, "opaque")) {
  563. if (vpninfo->opaque_srvdata)
  564. xmlFreeNode(vpninfo->opaque_srvdata);
  565. vpninfo->opaque_srvdata = xmlCopyNode(xml_node, 1);
  566. if (!vpninfo->opaque_srvdata)
  567. ret = -ENOMEM;
  568. } else if (xmlnode_is_named(xml_node, "host-scan")) {
  569. ret = parse_host_scan_node(vpninfo, xml_node);
  570. } else if (xmlnode_is_named(xml_node, "config")) {
  571. parse_config_node(vpninfo, xml_node);
  572. } else if (xmlnode_is_named(xml_node, "session-token")) {
  573. http_add_cookie(vpninfo, "webvpn",
  574. (const char *)xmlNodeGetContent(xml_node), 1);
  575. } else {
  576. xmlnode_get_text(xml_node, "error", &form->error);
  577. }
  578. if (ret)
  579. goto out;
  580. xml_node = xml_node->next;
  581. }
  582. if ((old_cert_rq_state & CERT2_REQUESTED) && form->error) {
  583. vpn_progress(vpninfo, PRG_ERR,
  584. _("Server reported certificate error: %s.\n"), form->error);
  585. ret = -EINVAL;
  586. goto out;
  587. }
  588. if (!form->auth_id && (!cert_rq || !cert_rq->state)) {
  589. vpn_progress(vpninfo, PRG_ERR,
  590. _("XML response has no \"auth\" node\n"));
  591. ret = -EINVAL;
  592. goto out;
  593. }
  594. *formp = form;
  595. xmlFreeDoc(xml_doc);
  596. return 0;
  597. out:
  598. xmlFreeDoc(xml_doc);
  599. free_auth_form(form);
  600. return ret;
  601. }
  602. /* Return value:
  603. * < 0, on error
  604. * = OC_FORM_RESULT_OK (0), when form parsed and POST required
  605. * = OC_FORM_RESULT_CANCELLED, when response was cancelled by user
  606. * = OC_FORM_RESULT_LOGGEDIN, when form indicates that login was already successful
  607. */
  608. static int handle_auth_form(struct openconnect_info *vpninfo, struct oc_auth_form *form,
  609. struct oc_text_buf *request_body, const char **method,
  610. const char **request_body_type)
  611. {
  612. int ret;
  613. struct oc_vpn_option *opt, *next;
  614. if (!strcmp(form->auth_id, "success"))
  615. return OC_FORM_RESULT_LOGGEDIN;
  616. if (vpninfo->nopasswd) {
  617. vpn_progress(vpninfo, PRG_ERR,
  618. _("Asked for password but '--no-passwd' set\n"));
  619. return -EPERM;
  620. }
  621. if (vpninfo->csd_token && vpninfo->csd_ticket && vpninfo->csd_starturl && vpninfo->csd_waiturl) {
  622. /* AB: remove all cookies */
  623. for (opt = vpninfo->cookies; opt; opt = next) {
  624. next = opt->next;
  625. free(opt->option);
  626. free(opt->value);
  627. free(opt);
  628. }
  629. vpninfo->cookies = NULL;
  630. return OC_FORM_RESULT_OK;
  631. }
  632. if (!form->opts) {
  633. if (form->message)
  634. vpn_progress(vpninfo, PRG_INFO, "%s\n", form->message);
  635. if (form->error) {
  636. if (!strcmp(form->error, "Certificate Validation Failure")) {
  637. /* XX: Cisco servers send this ambiguous error string when the CLIENT certificate
  638. * is absent or incorrect. We rewrite it to make this clearer, while preserving
  639. * the original error as a substring.
  640. */
  641. free(form->error);
  642. if (!(form->error = strdup(_("Client certificate missing or incorrect (Certificate Validation Failure)"))))
  643. return -ENOMEM;
  644. } else
  645. vpn_progress(vpninfo, PRG_ERR, "%s\n", form->error);
  646. }
  647. if (!strcmp(form->auth_id, "openconnect_authentication_complete"))
  648. goto justpost;
  649. return -EPERM;
  650. }
  651. ret = process_auth_form(vpninfo, form);
  652. if (ret)
  653. return ret;
  654. /* tokencode generation is deferred until after username prompts and CSD */
  655. ret = do_gen_tokencode(vpninfo, form);
  656. if (ret) {
  657. vpn_progress(vpninfo, PRG_ERR, _("Failed to generate OTP tokencode; disabling token\n"));
  658. vpninfo->token_bypassed = 1;
  659. return ret;
  660. }
  661. justpost:
  662. ret = vpninfo->xmlpost ?
  663. xmlpost_append_form_opts(vpninfo, form, request_body) :
  664. append_form_opts(vpninfo, form, request_body);
  665. if (!ret) {
  666. *method = "POST";
  667. *request_body_type = vpninfo->xmlpost ? "application/xml; charset=utf-8" : "application/x-www-form-urlencoded";
  668. }
  669. return ret;
  670. }
  671. /*
  672. * Old submission format is just an HTTP query string:
  673. *
  674. * password=12345678&username=joe
  675. *
  676. * New XML format is more complicated:
  677. *
  678. * <config-auth client="vpn" type="<!-- init or auth-reply -->">
  679. * <version who="vpn"><!-- currently just the OpenConnect version --></version>
  680. * <device-id><!-- linux, linux-64, win, ... --></device-id>
  681. * <opaque is-for="<!-- some name -->">
  682. * <!-- just copy this verbatim from whatever the gateway sent us -->
  683. * </opaque>
  684. *
  685. * For init only, add:
  686. * <group-access>https://<!-- insert hostname here --></group-access>
  687. *
  688. * For auth-reply only, add:
  689. * <auth>
  690. * <username><!-- same treatment as the old form options --></username>
  691. * <password><!-- ditto -->
  692. * </auth>
  693. * <group-select><!-- name of selected authgroup --></group-select>
  694. * <host-scan-token><!-- vpninfo->csd_ticket --></host-scan-token>
  695. */
  696. #define XCAST(x) ((const xmlChar *)(x))
  697. static xmlDocPtr xmlpost_new_query(struct openconnect_info *vpninfo, const char *type,
  698. xmlNodePtr *rootp)
  699. {
  700. xmlDocPtr doc;
  701. xmlNodePtr root, node, capabilities;
  702. doc = xmlNewDoc(XCAST("1.0"));
  703. if (!doc)
  704. return NULL;
  705. root = xmlNewNode(NULL, XCAST("config-auth"));
  706. if (!root)
  707. goto bad;
  708. xmlDocSetRootElement(doc, root);
  709. if (!xmlNewProp(root, XCAST("client"), XCAST("vpn")))
  710. goto bad;
  711. if (!xmlNewProp(root, XCAST("type"), XCAST(type)))
  712. goto bad;
  713. if (!xmlNewProp(root, XCAST("aggregate-auth-version"), XCAST("2")))
  714. goto bad;
  715. node = xmlNewTextChild(root, NULL, XCAST("version"),
  716. XCAST(vpninfo->version_string ? : openconnect_version_str));
  717. if (!node)
  718. goto bad;
  719. if (!xmlNewProp(node, XCAST("who"), XCAST("vpn")))
  720. goto bad;
  721. node = xmlNewTextChild(root, NULL, XCAST("device-id"), XCAST(vpninfo->platname));
  722. if (!node)
  723. goto bad;
  724. if (vpninfo->mobile_platform_version) {
  725. if (!xmlNewProp(node, XCAST("platform-version"), XCAST(vpninfo->mobile_platform_version)) ||
  726. !xmlNewProp(node, XCAST("device-type"), XCAST(vpninfo->mobile_device_type)) ||
  727. !xmlNewProp(node, XCAST("unique-id"), XCAST(vpninfo->mobile_device_uniqueid)))
  728. goto bad;
  729. }
  730. capabilities = xmlNewNode(NULL, XCAST("capabilities"));
  731. if (!capabilities)
  732. goto bad;
  733. capabilities = xmlAddChild(root, capabilities);
  734. if (!capabilities)
  735. goto bad;
  736. node = xmlNewTextChild(capabilities, NULL, XCAST("auth-method"), XCAST("single-sign-on"));
  737. if (!node)
  738. goto bad;
  739. node = xmlNewTextChild(capabilities, NULL, XCAST("auth-method"), XCAST("single-sign-on-v2"));
  740. if (!node)
  741. goto bad;
  742. #ifdef HAVE_HPKE_SUPPORT
  743. node = xmlNewTextChild(capabilities, NULL, XCAST("auth-method"), XCAST("single-sign-on-external-browser"));
  744. if (!node)
  745. goto bad;
  746. #endif
  747. if (vpninfo->certinfo[1].cert) {
  748. node = xmlNewTextChild(capabilities, NULL, XCAST("auth-method"), XCAST("multiple-cert"));
  749. if (!node)
  750. goto bad;
  751. }
  752. *rootp = root;
  753. return doc;
  754. bad:
  755. xmlFreeDoc(doc);
  756. return NULL;
  757. }
  758. static int xmlpost_complete(xmlDocPtr doc, struct oc_text_buf *body)
  759. {
  760. xmlChar *mem = NULL;
  761. int len, ret = 0;
  762. if (!body) {
  763. xmlFree(doc);
  764. return 0;
  765. }
  766. xmlDocDumpMemoryEnc(doc, &mem, &len, "UTF-8");
  767. if (!mem) {
  768. xmlFreeDoc(doc);
  769. return -ENOMEM;
  770. }
  771. buf_append_bytes(body, mem, len);
  772. xmlFreeDoc(doc);
  773. xmlFree(mem);
  774. return ret;
  775. }
  776. static int xmlpost_initial_req(struct openconnect_info *vpninfo,
  777. struct oc_text_buf *request_body, int cert_fail)
  778. {
  779. xmlNodePtr root, node;
  780. xmlDocPtr doc = xmlpost_new_query(vpninfo, "init", &root);
  781. char *url;
  782. if (!doc)
  783. return -ENOMEM;
  784. url = internal_get_url(vpninfo);
  785. if (!url)
  786. goto bad;
  787. node = xmlNewTextChild(root, NULL, XCAST("group-access"), XCAST(url));
  788. if (!node)
  789. goto bad;
  790. if (cert_fail) {
  791. node = xmlNewTextChild(root, NULL, XCAST("client-cert-fail"), NULL);
  792. if (!node)
  793. goto bad;
  794. }
  795. if (vpninfo->authgroup) {
  796. node = xmlNewTextChild(root, NULL, XCAST("group-select"), XCAST(vpninfo->authgroup));
  797. if (!node)
  798. goto bad;
  799. }
  800. free(url);
  801. return xmlpost_complete(doc, request_body);
  802. bad:
  803. xmlpost_complete(doc, NULL);
  804. return -ENOMEM;
  805. }
  806. static int xmlpost_append_form_opts(struct openconnect_info *vpninfo,
  807. struct oc_auth_form *form, struct oc_text_buf *body)
  808. {
  809. xmlNodePtr root, node;
  810. xmlDocPtr doc = xmlpost_new_query(vpninfo, "auth-reply", &root);
  811. struct oc_form_opt *opt;
  812. if (!doc)
  813. return -ENOMEM;
  814. if (vpninfo->opaque_srvdata) {
  815. node = xmlCopyNode(vpninfo->opaque_srvdata, 1);
  816. if (!node)
  817. goto bad;
  818. if (!xmlAddChild(root, node))
  819. goto bad;
  820. }
  821. node = xmlNewChild(root, NULL, XCAST("auth"), NULL);
  822. if (!node)
  823. goto bad;
  824. for (opt = form->opts; opt; opt = opt->next) {
  825. /* group_list: create a new <group-select> node under <config-auth> */
  826. if (!strcmp(opt->name, "group_list")) {
  827. if (!xmlNewTextChild(root, NULL, XCAST("group-select"), XCAST(opt->_value)))
  828. goto bad;
  829. continue;
  830. }
  831. /* answer,whichpin,new_password: rename to "password" */
  832. if (!strcmp(opt->name, "answer") ||
  833. !strcmp(opt->name, "whichpin") ||
  834. !strcmp(opt->name, "new_password")) {
  835. if (!xmlNewTextChild(node, NULL, XCAST("password"), XCAST(opt->_value)))
  836. goto bad;
  837. continue;
  838. }
  839. /* verify_pin,verify_password: ignore */
  840. if (!strcmp(opt->name, "verify_pin") ||
  841. !strcmp(opt->name, "verify_password")) {
  842. continue;
  843. }
  844. /* everything else: create <foo>user_input</foo> under <auth> */
  845. if (!xmlNewTextChild(node, NULL, XCAST(opt->name), XCAST(opt->_value)))
  846. goto bad;
  847. }
  848. if (vpninfo->csd_token &&
  849. !xmlNewTextChild(root, NULL, XCAST("host-scan-token"), XCAST(vpninfo->csd_token)))
  850. goto bad;
  851. return xmlpost_complete(doc, body);
  852. bad:
  853. xmlpost_complete(doc, NULL);
  854. return -ENOMEM;
  855. }
  856. /* Return value:
  857. * < 0, if unable to generate a tokencode
  858. * = 0, on success
  859. */
  860. static int cstp_can_gen_tokencode(struct openconnect_info *vpninfo,
  861. struct oc_auth_form *form,
  862. struct oc_form_opt *opt)
  863. {
  864. if (vpninfo->token_mode == OC_TOKEN_MODE_NONE ||
  865. vpninfo->token_bypassed)
  866. return -EINVAL;
  867. #ifdef HAVE_LIBSTOKEN
  868. if (vpninfo->token_mode == OC_TOKEN_MODE_STOKEN) {
  869. if (strcmp(opt->name, "password") &&
  870. strcmp(opt->name, "answer"))
  871. return -EINVAL;
  872. return can_gen_stoken_code(vpninfo, form, opt);
  873. }
  874. #endif
  875. /* Otherwise it's an OATH token of some kind. */
  876. if (!strcmp(opt->name, "secondary_password") ||
  877. (form->auth_id && !strcmp(form->auth_id, "challenge")))
  878. return can_gen_tokencode(vpninfo, form, opt);
  879. return -EINVAL;
  880. }
  881. static int fetch_config(struct openconnect_info *vpninfo)
  882. {
  883. struct oc_text_buf *buf;
  884. int result;
  885. unsigned char local_sha1_bin[SHA1_SIZE];
  886. char local_sha1_ascii[(SHA1_SIZE * 2)+1];
  887. int i;
  888. if (!vpninfo->profile_url || !vpninfo->profile_sha1 || !vpninfo->write_new_config)
  889. return -ENOENT;
  890. if (!strncasecmp(vpninfo->xmlsha1, vpninfo->profile_sha1, SHA1_SIZE * 2)) {
  891. vpn_progress(vpninfo, PRG_TRACE,
  892. _("Not downloading XML profile because SHA1 already matches\n"));
  893. return 0;
  894. }
  895. if ((result = openconnect_open_https(vpninfo))) {
  896. vpn_progress(vpninfo, PRG_ERR,
  897. _("Failed to open HTTPS connection to %s\n"),
  898. vpninfo->hostname);
  899. return result;
  900. }
  901. buf = buf_alloc();
  902. if (vpninfo->port != 443)
  903. buf_append(buf, "GET %s:%d HTTP/1.1\r\n", vpninfo->profile_url, vpninfo->port);
  904. else
  905. buf_append(buf, "GET %s HTTP/1.1\r\n", vpninfo->profile_url);
  906. cstp_common_headers(vpninfo, buf);
  907. buf_append(buf, "\r\n");
  908. if (buf_error(buf))
  909. return buf_free(buf);
  910. if (vpninfo->dump_http_traffic)
  911. dump_buf(vpninfo, '>', buf->data);
  912. if (vpninfo->ssl_write(vpninfo, buf->data, buf->pos) != buf->pos) {
  913. vpn_progress(vpninfo, PRG_ERR,
  914. _("Failed to send GET request for new config\n"));
  915. buf_free(buf);
  916. return -EIO;
  917. }
  918. result = process_http_response(vpninfo, 0, NULL, buf);
  919. if (result < 0) {
  920. /* We'll already have complained about whatever offended us */
  921. buf_free(buf);
  922. return -EINVAL;
  923. }
  924. if (result != 200) {
  925. buf_free(buf);
  926. return -EINVAL;
  927. }
  928. openconnect_sha1(local_sha1_bin, buf->data, buf->pos);
  929. for (i = 0; i < SHA1_SIZE; i++)
  930. sprintf(&local_sha1_ascii[i*2], "%02x", local_sha1_bin[i]);
  931. if (strcasecmp(vpninfo->profile_sha1, local_sha1_ascii)) {
  932. vpn_progress(vpninfo, PRG_ERR,
  933. _("Downloaded config file did not match intended SHA1\n"));
  934. buf_free(buf);
  935. return -EINVAL;
  936. }
  937. vpn_progress(vpninfo, PRG_DEBUG, _("Downloaded new XML profile\n"));
  938. result = vpninfo->write_new_config(vpninfo->cbdata, buf->data, buf->pos);
  939. buf_free(buf);
  940. return result;
  941. }
  942. int set_csd_user(struct openconnect_info *vpninfo)
  943. {
  944. #if defined(_WIN32) || defined(__native_client__)
  945. vpn_progress(vpninfo, PRG_ERR,
  946. _("Error: Running the 'Cisco Secure Desktop' trojan on this platform is not yet implemented.\n"));
  947. return -EPERM;
  948. #else
  949. setsid();
  950. if (vpninfo->uid_csd_given && vpninfo->uid_csd != getuid()) {
  951. struct passwd *pw;
  952. int err;
  953. if (setgid(vpninfo->gid_csd)) {
  954. err = errno;
  955. fprintf(stderr, _("Failed to set gid %ld: %s\n"),
  956. (long)vpninfo->uid_csd, strerror(err));
  957. return -err;
  958. }
  959. if (setgroups(1, &vpninfo->gid_csd)) {
  960. err = errno;
  961. fprintf(stderr, _("Failed to set groups to %ld: %s\n"),
  962. (long)vpninfo->uid_csd, strerror(err));
  963. return -err;
  964. }
  965. if (setuid(vpninfo->uid_csd)) {
  966. err = errno;
  967. fprintf(stderr, _("Failed to set uid %ld: %s\n"),
  968. (long)vpninfo->uid_csd, strerror(err));
  969. return -err;
  970. }
  971. if (!(pw = getpwuid(vpninfo->uid_csd))) {
  972. err = errno;
  973. fprintf(stderr, _("Invalid user uid=%ld: %s\n"),
  974. (long)vpninfo->uid_csd, strerror(err));
  975. return -err;
  976. }
  977. setenv("HOME", pw->pw_dir, 1);
  978. if (chdir(pw->pw_dir)) {
  979. err = errno;
  980. fprintf(stderr, _("Failed to change to CSD home directory '%s': %s\n"),
  981. pw->pw_dir, strerror(err));
  982. return -err;
  983. }
  984. }
  985. return 0;
  986. #endif
  987. }
  988. static int run_csd_script(struct openconnect_info *vpninfo, char *buf, int buflen)
  989. {
  990. #if defined(_WIN32) || defined(__native_client__)
  991. vpn_progress(vpninfo, PRG_ERR,
  992. _("Error: Running the 'Cisco Secure Desktop' trojan on this platform is not yet implemented.\n"));
  993. return -EPERM;
  994. #else
  995. char fname[64];
  996. int fd, ret;
  997. pid_t child;
  998. if (!vpninfo->csd_wrapper && !buflen) {
  999. vpn_progress(vpninfo, PRG_ERR,
  1000. _("Error: Server asked us to run CSD hostscan.\n"
  1001. "You need to provide a suitable --csd-wrapper argument.\n"));
  1002. return -EINVAL;
  1003. }
  1004. if (!vpninfo->uid_csd_given && !vpninfo->csd_wrapper) {
  1005. vpn_progress(vpninfo, PRG_ERR,
  1006. _("Error: Server asked us to download and run a 'Cisco Secure Desktop' trojan.\n"
  1007. "This facility is disabled by default for security reasons, so you may wish to enable it.\n"));
  1008. return -EPERM;
  1009. }
  1010. fname[0] = 0;
  1011. if (buflen) {
  1012. struct oc_vpn_option *opt;
  1013. const char *tmpdir = NULL;
  1014. /* If the caller wanted $TMPDIR set for the CSD script, that
  1015. means for us too; look through the csd_env for a TMPDIR
  1016. override. */
  1017. for (opt = vpninfo->csd_env; opt; opt = opt->next) {
  1018. if (!strcmp(opt->option, "TMPDIR")) {
  1019. tmpdir = opt->value;
  1020. break;
  1021. }
  1022. }
  1023. if (!opt)
  1024. tmpdir = getenv("TMPDIR");
  1025. if (!tmpdir && !access("/var/tmp", W_OK))
  1026. tmpdir = "/var/tmp";
  1027. if (!tmpdir)
  1028. tmpdir = "/tmp";
  1029. if (access(tmpdir, W_OK))
  1030. vpn_progress(vpninfo, PRG_ERR,
  1031. _("Temporary directory '%s' is not writable: %s\n"),
  1032. tmpdir, strerror(errno));
  1033. snprintf(fname, 64, "%s/csdXXXXXX", tmpdir);
  1034. fd = mkstemp(fname);
  1035. if (fd < 0) {
  1036. int err = -errno;
  1037. vpn_progress(vpninfo, PRG_ERR,
  1038. _("Failed to open temporary CSD script file: %s\n"),
  1039. strerror(errno));
  1040. return err;
  1041. }
  1042. ret = write(fd, (void *)buf, buflen);
  1043. if (ret != buflen) {
  1044. int err = -errno;
  1045. vpn_progress(vpninfo, PRG_ERR,
  1046. _("Failed to write temporary CSD script file: %s\n"),
  1047. strerror(errno));
  1048. return err;
  1049. }
  1050. fchmod(fd, 0755);
  1051. close(fd);
  1052. }
  1053. vpn_progress(vpninfo, PRG_INFO,
  1054. _("Trying to run CSD Trojan script '%s'.\n"),
  1055. vpninfo->csd_wrapper ?: fname);
  1056. child = fork();
  1057. if (child == -1) {
  1058. goto out;
  1059. } else if (child > 0) {
  1060. /* in parent: must reap child process */
  1061. int status;
  1062. waitpid(child, &status, 0);
  1063. if (!WIFEXITED(status)) {
  1064. vpn_progress(vpninfo, PRG_ERR,
  1065. _("CSD script '%s' exited abnormally\n"),
  1066. vpninfo->csd_wrapper ?: fname);
  1067. ret = -EINVAL;
  1068. } else {
  1069. if (WEXITSTATUS(status) != 0) {
  1070. vpn_progress(vpninfo, PRG_ERR,
  1071. _("CSD script '%s' returned non-zero status: %d\n"),
  1072. vpninfo->csd_wrapper ?: fname, WEXITSTATUS(status));
  1073. /* Some scripts do exit non-zero, and it's never mattered.
  1074. * Don't abort for now. */
  1075. vpn_progress(vpninfo, PRG_ERR,
  1076. _("Authentication may fail. If your script is not returning zero, fix it.\n"
  1077. "Future versions of openconnect will abort on this error.\n"));
  1078. } else {
  1079. vpn_progress(vpninfo, PRG_INFO,
  1080. _("CSD script '%s' completed successfully.\n"),
  1081. vpninfo->csd_wrapper ?: fname);
  1082. }
  1083. free(vpninfo->urlpath);
  1084. vpninfo->urlpath = strdup(vpninfo->csd_waiturl +
  1085. (vpninfo->csd_waiturl[0] == '/' ? 1 : 0));
  1086. vpninfo->csd_scriptname = strdup(fname);
  1087. http_add_cookie(vpninfo, "sdesktop", vpninfo->csd_token, 1);
  1088. ret = 0;
  1089. }
  1090. free(vpninfo->csd_stuburl);
  1091. vpninfo->csd_stuburl = NULL;
  1092. free(vpninfo->csd_waiturl);
  1093. vpninfo->csd_waiturl = NULL;
  1094. return ret;
  1095. } else {
  1096. /* in child: will be reaped by init */
  1097. char scertbuf[MD5_SIZE * 2 + 1];
  1098. char ccertbuf[MD5_SIZE * 2 + 1];
  1099. char *csd_argv[32];
  1100. int i = 0;
  1101. if (set_csd_user(vpninfo) < 0)
  1102. exit(1);
  1103. if (getuid() == 0 && !vpninfo->csd_wrapper) {
  1104. fprintf(stderr, _("Warning: you are running insecure CSD code with root privileges\n"
  1105. "\t Use command line option \"--csd-user\"\n"));
  1106. }
  1107. /*
  1108. * Spurious stdout output from the CSD trojan will break both
  1109. * the NM tool and the various cookieonly modes.
  1110. * Also, gnome-shell *closes* stderr so attempt to cope with that
  1111. * by opening /dev/null, because otherwise some CSD scripts fail.
  1112. * Actually, perhaps we should set up our own pipes, and report
  1113. * the trojan's output via vpn_progress().
  1114. */
  1115. if (ferror(stderr)) {
  1116. int nulfd = open("/dev/null", O_WRONLY);
  1117. if (nulfd >= 0) {
  1118. dup2(nulfd, 2);
  1119. close(nulfd);
  1120. }
  1121. }
  1122. dup2(2, 1);
  1123. if (vpninfo->csd_wrapper)
  1124. csd_argv[i++] = openconnect_utf8_to_legacy(vpninfo,
  1125. vpninfo->csd_wrapper);
  1126. csd_argv[i++] = fname;
  1127. csd_argv[i++] = (char *)"-ticket";
  1128. if (asprintf(&csd_argv[i++], "\"%s\"", vpninfo->csd_ticket) == -1)
  1129. goto out;
  1130. csd_argv[i++] = (char *)"-stub";
  1131. csd_argv[i++] = (char *)"\"0\"";
  1132. csd_argv[i++] = (char *)"-group";
  1133. if (asprintf(&csd_argv[i++], "\"%s\"", vpninfo->authgroup?:"") == -1)
  1134. goto out;
  1135. openconnect_local_cert_md5(vpninfo, ccertbuf);
  1136. scertbuf[0] = 0;
  1137. get_cert_md5_fingerprint(vpninfo, vpninfo->peer_cert, scertbuf);
  1138. csd_argv[i++] = (char *)"-certhash";
  1139. if (asprintf(&csd_argv[i++], "\"%s:%s\"", scertbuf, ccertbuf) == -1)
  1140. goto out;
  1141. csd_argv[i++] = (char *)"-url";
  1142. if (asprintf(&csd_argv[i++], "\"https://%s%s\"", openconnect_get_hostname(vpninfo), vpninfo->csd_starturl) == -1)
  1143. goto out;
  1144. csd_argv[i++] = (char *)"-langselen";
  1145. csd_argv[i++] = NULL;
  1146. if (setenv("CSD_SHA256", openconnect_get_peer_cert_hash(vpninfo)+11, 1)) /* remove initial 'pin-sha256:' */
  1147. goto out;
  1148. if (setenv("CSD_TOKEN", vpninfo->csd_token, 1))
  1149. goto out;
  1150. if (setenv("CSD_HOSTNAME", openconnect_get_hostname(vpninfo), 1))
  1151. goto out;
  1152. apply_script_env(vpninfo->csd_env);
  1153. execv(csd_argv[0], csd_argv);
  1154. out:
  1155. vpn_progress(vpninfo, PRG_ERR,
  1156. _("Failed to exec CSD script %s\n"), vpninfo->csd_wrapper ?: fname);
  1157. exit(1);
  1158. }
  1159. #endif /* !_WIN32 && !__native_client__ */
  1160. }
  1161. /* Return value:
  1162. * < 0, if the data is unrecognized
  1163. * = 0, if the page contains an XML document
  1164. * = 1, if the page is a wait/refresh HTML page
  1165. */
  1166. static int check_response_type(struct openconnect_info *vpninfo, char *form_buf)
  1167. {
  1168. if (strncmp(form_buf, "<?xml", 5)) {
  1169. /* Not XML? Perhaps it's HTML with a refresh... */
  1170. if (strcasestr(form_buf, "http-equiv=\"refresh\""))
  1171. return 1;
  1172. vpn_progress(vpninfo, PRG_ERR,
  1173. _("Unknown response from server\n"));
  1174. return -EINVAL;
  1175. }
  1176. return 0;
  1177. }
  1178. /* Return value:
  1179. * < 0, on error
  1180. * > 0, no cookie (user cancel)
  1181. * = 0, obtained cookie
  1182. */
  1183. int cstp_obtain_cookie(struct openconnect_info *vpninfo)
  1184. {
  1185. struct oc_vpn_option *opt;
  1186. char *form_buf = NULL;
  1187. struct oc_auth_form *form = NULL;
  1188. int result, buflen, tries;
  1189. struct oc_text_buf *request_body = buf_alloc();
  1190. const char *request_body_type;
  1191. const char *method = "POST";
  1192. char *orig_host = NULL, *orig_path = NULL, *form_path = NULL;
  1193. int orig_port = 0;
  1194. struct cert_request cert_rq = { 0 };
  1195. int cert_sent = !vpninfo->certinfo[0].cert;
  1196. int newgroup_attempts = 5;
  1197. if (!vpninfo->xmlpost)
  1198. goto no_xmlpost;
  1199. /*
  1200. * Step 2: Probe for XML POST compatibility
  1201. *
  1202. * This can get stuck in a redirect loop, so give up after any of:
  1203. *
  1204. * a) HTTP error (e.g. 400 Bad Request)
  1205. * b) Same-host redirect (e.g. Location: /foo/bar)
  1206. * c) Three redirects without seeing a plausible login form
  1207. */
  1208. newgroup:
  1209. if (newgroup_attempts-- <= 0) {
  1210. result = -1;
  1211. goto out;
  1212. }
  1213. buf_truncate(request_body);
  1214. result = xmlpost_initial_req(vpninfo, request_body, 0);
  1215. if (result < 0)
  1216. goto out;
  1217. free(orig_host);
  1218. free(orig_path);
  1219. orig_host = strdup(vpninfo->hostname);
  1220. orig_path = vpninfo->urlpath ? strdup(vpninfo->urlpath) : NULL;
  1221. orig_port = vpninfo->port;
  1222. for (tries = 0; ; tries++) {
  1223. /**
  1224. * Multiple certificate authentication requires an additional exchange.
  1225. * tries == 0: redirect
  1226. * tries == 1: !cert_sent + initial_req
  1227. * tries == 2: cert_sent + initial_req
  1228. * tries == 3: challenge response
  1229. */
  1230. if (tries == 3 + !!(cert_rq.state&CERT2_REQUESTED)) {
  1231. fail:
  1232. if (vpninfo->xmlpost) {
  1233. no_xmlpost:
  1234. /* Try without XML POST this time... */
  1235. tries = 0;
  1236. vpninfo->xmlpost = 0;
  1237. request_body_type = NULL;
  1238. buf_truncate(request_body);
  1239. method = "GET";
  1240. if (orig_host) {
  1241. openconnect_set_hostname(vpninfo, orig_host);
  1242. free(orig_host);
  1243. orig_host = NULL;
  1244. free(vpninfo->urlpath);
  1245. vpninfo->urlpath = orig_path;
  1246. orig_path = NULL;
  1247. vpninfo->port = orig_port;
  1248. }
  1249. openconnect_close_https(vpninfo, 0);
  1250. } else {
  1251. result = -EIO;
  1252. goto out;
  1253. }
  1254. }
  1255. request_body_type = vpninfo->xmlpost ? "application/xml; charset=utf-8" : "application/x-www-form-urlencoded";
  1256. result = do_https_request(vpninfo, method, request_body_type, request_body, &form_buf, NULL, HTTP_NO_FLAGS);
  1257. if (vpninfo->got_cancel_cmd) {
  1258. result = 1;
  1259. goto out;
  1260. }
  1261. if (result == -EINVAL)
  1262. goto fail;
  1263. if (result < 0)
  1264. goto out;
  1265. /* Some ASAs forget to send the TLS cert request on the initial connection.
  1266. * If we have a client cert, disable HTTP keepalive until we get a real
  1267. * login form (not a redirect). */
  1268. if (!cert_sent)
  1269. openconnect_close_https(vpninfo, 0);
  1270. /* XML POST does not allow local redirects, but GET does. */
  1271. if (vpninfo->xmlpost &&
  1272. vpninfo->redirect_type == REDIR_TYPE_LOCAL)
  1273. goto fail;
  1274. else if (vpninfo->redirect_type != REDIR_TYPE_NONE)
  1275. continue;
  1276. result = parse_xml_response(vpninfo, form_buf, &form, &cert_rq);
  1277. if (result < 0)
  1278. goto fail;
  1279. if ((cert_rq.state&CERT1_REQUESTED) &&
  1280. !(cert_rq.state&CERT1_AUTHENTICATED)) {
  1281. int cert_failed = 0;
  1282. free_auth_form(form);
  1283. form = NULL;
  1284. if (!cert_sent && vpninfo->certinfo[0].cert) {
  1285. /* Try again on a fresh connection. */
  1286. cert_sent = 1;
  1287. } else if (cert_sent && vpninfo->certinfo[0].cert) {
  1288. /* Try again with <client-cert-fail/> in the request */
  1289. vpn_progress(vpninfo, PRG_ERR,
  1290. _("Server requested SSL client certificate after one was provided\n"));
  1291. cert_failed = 1;
  1292. } else {
  1293. vpn_progress(vpninfo, PRG_INFO,
  1294. _("Server requested SSL client certificate; none was configured\n"));
  1295. cert_failed = 1;
  1296. }
  1297. buf_truncate(request_body);
  1298. result = xmlpost_initial_req(vpninfo, request_body, cert_failed);
  1299. if (result < 0)
  1300. goto fail;
  1301. continue;
  1302. } else if (cert_rq.state&CERT2_REQUESTED) {
  1303. free_auth_form(form); form = NULL;
  1304. buf_truncate(request_body);
  1305. /** load the second certificate */
  1306. struct cert_info *certinfo = &vpninfo->certinfo[1];
  1307. if (!certinfo->cert) {
  1308. /* This is a fail safe; we should never get here */
  1309. vpn_progress(vpninfo, PRG_ERR,
  1310. _("Multiple-certificate authentication requires a second certificate; none were configured.\n"));
  1311. result = -EINVAL;
  1312. (void) xmlpost_initial_req(vpninfo, request_body, 1);
  1313. goto out;
  1314. }
  1315. result = load_certificate(vpninfo, certinfo, MULTICERT_COMPAT);
  1316. if (result < 0) {
  1317. (void) xmlpost_initial_req(vpninfo, request_body, 1);
  1318. goto out;
  1319. }
  1320. result = prepare_multicert_response(vpninfo, cert_rq, form_buf,
  1321. request_body);
  1322. unload_certificate(certinfo, 1);
  1323. if (result < 0) {
  1324. (void) xmlpost_initial_req(vpninfo, request_body, 0);
  1325. goto fail;
  1326. }
  1327. continue;
  1328. }
  1329. if (form && form->action) {
  1330. vpninfo->redirect_url = strdup(form->action);
  1331. handle_redirect(vpninfo);
  1332. }
  1333. break;
  1334. }
  1335. if (vpninfo->xmlpost)
  1336. vpn_progress(vpninfo, PRG_INFO, _("XML POST enabled\n"));
  1337. /* Step 4: Run the CSD trojan, if applicable */
  1338. if (vpninfo->csd_starturl && vpninfo->csd_waiturl) {
  1339. buflen = 0;
  1340. if (vpninfo->urlpath) {
  1341. form_path = strdup(vpninfo->urlpath);
  1342. if (!form_path) {
  1343. result = -ENOMEM;
  1344. goto out;
  1345. }
  1346. }
  1347. /* fetch the CSD program, if available */
  1348. if (vpninfo->csd_stuburl) {
  1349. vpninfo->redirect_url = vpninfo->csd_stuburl;
  1350. vpninfo->csd_stuburl = NULL;
  1351. handle_redirect(vpninfo);
  1352. buflen = do_https_request(vpninfo, "GET", NULL, NULL, &form_buf, NULL, HTTP_NO_FLAGS);
  1353. if (buflen <= 0) {
  1354. if (vpninfo->csd_wrapper) {
  1355. vpn_progress(vpninfo, PRG_ERR,
  1356. _("Couldn't fetch CSD stub. Proceeding anyway with CSD wrapper script.\n"));
  1357. buflen = 0;
  1358. } else {
  1359. result = -EINVAL;
  1360. goto out;
  1361. }
  1362. } else
  1363. vpn_progress(vpninfo, PRG_INFO,
  1364. _("Fetched CSD stub for %s platform (size is %d bytes).\n"),
  1365. vpninfo->platname, buflen);
  1366. }
  1367. /* This is the CSD stub script, which we now need to run */
  1368. result = run_csd_script(vpninfo, form_buf, buflen);
  1369. if (result)
  1370. goto out;
  1371. /* vpninfo->urlpath now points to the wait page */
  1372. while (1) {
  1373. result = do_https_request(vpninfo, "GET", NULL, NULL, &form_buf, NULL, HTTP_NO_FLAGS);
  1374. if (result <= 0)
  1375. break;
  1376. result = check_response_type(vpninfo, form_buf);
  1377. if (result <= 0)
  1378. break;
  1379. vpn_progress(vpninfo, PRG_INFO,
  1380. _("Refreshing %s after 1 second...\n"),
  1381. vpninfo->urlpath);
  1382. sleep(1);
  1383. }
  1384. if (result < 0)
  1385. goto out;
  1386. /* refresh the form page, to see if we're authorized now */
  1387. free(vpninfo->urlpath);
  1388. vpninfo->urlpath = form_path;
  1389. form_path = NULL;
  1390. result = do_https_request(vpninfo,
  1391. vpninfo->xmlpost ? "POST" : "GET",
  1392. request_body_type, request_body, &form_buf, NULL, HTTP_REDIRECT);
  1393. if (result < 0)
  1394. goto out;
  1395. result = parse_xml_response(vpninfo, form_buf, &form, NULL);
  1396. if (result < 0)
  1397. goto out;
  1398. }
  1399. /* Step 5: Ask the user to fill in the auth form; repeat as necessary */
  1400. while (1) {
  1401. buf_truncate(request_body);
  1402. result = handle_auth_form(vpninfo, form, request_body,
  1403. &method, &request_body_type);
  1404. if (result < 0 || result == OC_FORM_RESULT_CANCELLED)
  1405. goto out;
  1406. if (result == OC_FORM_RESULT_LOGGEDIN)
  1407. break;
  1408. if (result == OC_FORM_RESULT_NEWGROUP) {
  1409. free(form_buf);
  1410. form_buf = NULL;
  1411. free_auth_form(form);
  1412. form = NULL;
  1413. goto newgroup;
  1414. }
  1415. result = do_https_request(vpninfo, method, request_body_type, request_body, &form_buf, NULL, 1);
  1416. if (result < 0)
  1417. goto out;
  1418. result = parse_xml_response(vpninfo, form_buf, &form, NULL);
  1419. if (result < 0)
  1420. goto out;
  1421. if (form->action) {
  1422. vpninfo->redirect_url = strdup(form->action);
  1423. handle_redirect(vpninfo);
  1424. }
  1425. }
  1426. /* A return value of 2 means the XML form indicated
  1427. success. We _should_ have a cookie... */
  1428. struct oc_text_buf *cookie_buf = buf_alloc();
  1429. #ifdef HAVE_HPKE_SUPPORT
  1430. if (vpninfo->strap_key) {
  1431. buf_append(cookie_buf, "openconnect_strapkey=");
  1432. append_strap_privkey(vpninfo, cookie_buf);
  1433. buf_append(cookie_buf, "; webvpn=");
  1434. }
  1435. #endif
  1436. for (opt = vpninfo->cookies; opt; opt = opt->next) {
  1437. if (!strcmp(opt->option, "webvpn")) {
  1438. buf_append(cookie_buf, "%s", opt->value);
  1439. } else if (vpninfo->write_new_config && !strcmp(opt->option, "webvpnc")) {
  1440. char *tok = opt->value;
  1441. char *bu = NULL, *fu = NULL, *sha = NULL;
  1442. do {
  1443. if (tok != opt->value)
  1444. *(tok++) = 0;
  1445. if (!strncmp(tok, "bu:", 3))
  1446. bu = tok + 3;
  1447. else if (!strncmp(tok, "fu:", 3))
  1448. fu = tok + 3;
  1449. else if (!strncmp(tok, "fh:", 3))
  1450. sha = tok + 3;
  1451. } while ((tok = strchr(tok, '&')));
  1452. if (bu && fu && sha) {
  1453. if (asprintf(&vpninfo->profile_url, "%s%s", bu, fu) == -1) {
  1454. buf_free(cookie_buf);
  1455. result = -ENOMEM;
  1456. goto out;
  1457. }
  1458. vpninfo->profile_sha1 = strdup(sha);
  1459. }
  1460. }
  1461. }
  1462. if (buf_error(cookie_buf)) {
  1463. result = buf_free(cookie_buf);
  1464. goto out;
  1465. }
  1466. free(vpninfo->cookie);
  1467. vpninfo->cookie = cookie_buf->data;
  1468. cookie_buf->data = NULL;
  1469. buf_free(cookie_buf);
  1470. result = 0;
  1471. fetch_config(vpninfo);
  1472. out:
  1473. buf_free(request_body);
  1474. free (orig_host);
  1475. free (orig_path);
  1476. free(form_path);
  1477. free(form_buf);
  1478. free_auth_form(form);
  1479. if (vpninfo->csd_scriptname) {
  1480. unlink(vpninfo->csd_scriptname);
  1481. free(vpninfo->csd_scriptname);
  1482. vpninfo->csd_scriptname = NULL;
  1483. }
  1484. return result;
  1485. }
  1486. /**
  1487. * Multiple certificate authentication
  1488. *
  1489. * Two certificates are employed: a "machine" certificate and a
  1490. * "user" certificate. The machine certificate is used to establish
  1491. * the TLS session. The user certificate is used to sign a challenge.
  1492. *
  1493. * An example XML exchange follows. For brevity, tags and attributes whose
  1494. * values are irrelevant (e.g. <opaque>) or well-understood from other auth
  1495. * types are omitted:
  1496. *
  1497. * CLIENT's initial request should include multiple-cert in capabilities:
  1498. *
  1499. * <config-auth client="vpn" type="init">
  1500. * <capabilities>
  1501. * <auth-method>multiple-cert</auth-method>
  1502. * </capabilities>
  1503. * </config-auth>
  1504. *
  1505. * SERVER's response should include <multiple-client-cert-request> with list
  1506. * of hash algorithms, and empty <cert-authenticated> tag:
  1507. *
  1508. * <config-auth client="vpn" type="auth-request">
  1509. * <multiple-client-cert-request>
  1510. * <hash-algorithm>sha256</hash-algorithm>
  1511. * <hash-algorithm>sha384</hash-algorithm>
  1512. * <hash-algorithm>sha512</hash-algorithm>
  1513. * </multiple-client-cert-request>
  1514. *
  1515. * <!-- Ensures that the client has signed this specific request in subsequent reply -->
  1516. * <random>FA4003BD87436B227...C138A08FF724F0100015B863F750914839EE79C86DFE8F0B9A0199E2</random>
  1517. *
  1518. * <!-- Appears to indicate that "machine" cert was accepted -->
  1519. * <cert-authenticated/>
  1520. * </config-auth>
  1521. *
  1522. * CLIENT's second request should include the "user" certificate (and any
  1523. * required intermediates) in PKCS7 format, along with a signature of the
  1524. * complete XML body of the server's prior response:
  1525. *
  1526. * <config-auth client="vpn" type="auth-reply">
  1527. * <auth>
  1528. * <client-cert-chain cert-store="1M">
  1529. * <client-cert-sent-via-protocol/>
  1530. * </client-cert-chain>
  1531. * <client-cert-chain cert-store="1U">
  1532. * <client-cert cert-format="pkcs7">
  1533. * <!-- PKCS7 "user" certificate and intermediate (base64-encoded) -->
  1534. * </client-cert>
  1535. * <client-cert-auth-signature hash-algorithm-chosen="sha512">
  1536. * <!-- signature on server's prior response with private key of "user" certificate -->
  1537. * </client-cert-auth-signature>
  1538. * </client-cert-chain>
  1539. * </auth>
  1540. * </config-auth>
  1541. */
  1542. static int to_base64(struct oc_text_buf **result,
  1543. const void *data, size_t data_len)
  1544. {
  1545. const uint8_t *dp = data;
  1546. struct oc_text_buf *buf;
  1547. int ret;
  1548. *result = NULL;
  1549. /**
  1550. * Line feed every 64 characters. No feed needed on last line
  1551. * */
  1552. buf = buf_alloc();
  1553. if (!buf)
  1554. return -ENOMEM;
  1555. buf_append_base64(buf, dp, data_len, 64);
  1556. ret = buf_error(buf);
  1557. if (ret < 0)
  1558. goto out;
  1559. *result = buf;
  1560. buf = NULL;
  1561. out:
  1562. buf_free(buf);
  1563. return ret;
  1564. }
  1565. /**
  1566. * parse request
  1567. */
  1568. void parse_multicert_request(struct openconnect_info *vpninfo,
  1569. xmlNodePtr node, struct cert_request *cert_rq)
  1570. {
  1571. xmlNodePtr child;
  1572. char *content;
  1573. openconnect_hash_type hash;
  1574. unsigned int oldhashes = 0;
  1575. /* node is a multiple-client-cert-request element */
  1576. for (child = node->children; child; child = child->next) {
  1577. if (child->type != XML_ELEMENT_NODE)
  1578. continue;
  1579. if (xmlStrcmp(child->name, XCAST("hash-algorithm")) != 0)
  1580. continue;
  1581. content = (char *)xmlNodeGetContent(child);
  1582. if (content == NULL)
  1583. continue;
  1584. hash = multicert_hash_get_id(content);
  1585. /* hash was not found */
  1586. if (hash == OPENCONNECT_HASH_UNKNOWN) {
  1587. vpn_progress(vpninfo, PRG_INFO,
  1588. _("Unsupported hash algorithm '%s' requested.\n"),
  1589. (char *) content);
  1590. goto next;
  1591. }
  1592. oldhashes = cert_rq->hashes;
  1593. cert_rq->hashes |= MULTICERT_HASH_FLAG(hash);
  1594. if (oldhashes == cert_rq->hashes)
  1595. vpn_progress(vpninfo, PRG_INFO,
  1596. _("Duplicate hash algorithm '%s' requested.\n"),
  1597. (char *) content);
  1598. next:
  1599. xmlFree(content);
  1600. }
  1601. }
  1602. #define BUF_DATA(bp) ((bp)->data)
  1603. #define BUF_SIZE(bp) ((bp)->pos)
  1604. static int post_multicert_response(struct openconnect_info *vpninfo, const xmlChar *cert,
  1605. openconnect_hash_type hash, const xmlChar *signature,
  1606. struct oc_text_buf *body)
  1607. {
  1608. static const xmlChar *strnull = XCAST("(null)");
  1609. const xmlChar *hashname;
  1610. xmlDocPtr doc;
  1611. xmlNodePtr root, auth, node, chain;
  1612. doc = xmlpost_new_query(vpninfo, "auth-reply", &root);
  1613. if (!doc)
  1614. goto bad;
  1615. node = xmlNewChild(root, NULL, XCAST("session-token"), NULL);
  1616. if (!node)
  1617. goto bad;
  1618. node = xmlNewChild(root, NULL, XCAST("session-id"), NULL);
  1619. if (!node)
  1620. goto bad;
  1621. if (vpninfo->opaque_srvdata != NULL) {
  1622. node = xmlCopyNode(vpninfo->opaque_srvdata, 1);
  1623. if (!node || !xmlAddChild(root, node))
  1624. goto bad;
  1625. }
  1626. // key1 ownership is proved by TLS session
  1627. auth = xmlNewChild(root, NULL, XCAST("auth"), NULL);
  1628. if (!auth)
  1629. goto bad;
  1630. chain = xmlNewChild(auth, NULL, XCAST("client-cert-chain"), NULL);
  1631. if (!chain || !xmlNewProp(chain, XCAST("cert-store"), XCAST("1M")))
  1632. goto bad;
  1633. if (!xmlNewChild(chain, NULL, XCAST("client-cert-sent-via-protocol"),
  1634. NULL))
  1635. goto bad;
  1636. // key2 ownership is proved by signing the challenge
  1637. chain = xmlNewChild(auth, NULL, XCAST("client-cert-chain"), NULL);
  1638. if (!chain || !xmlNewProp(chain, XCAST("cert-store"), XCAST("1U")))
  1639. goto bad;
  1640. node = xmlNewTextChild(chain, NULL, XCAST("client-cert"), cert ? cert : strnull);
  1641. if (!node || !xmlNewProp(node, XCAST("cert-format"), XCAST("pkcs7")))
  1642. goto bad;
  1643. hashname = XCAST(multicert_hash_get_name(hash));
  1644. node = xmlNewTextChild(chain, NULL,
  1645. XCAST("client-cert-auth-signature"),
  1646. signature ? signature : strnull);
  1647. if (!node || !xmlNewProp(node, XCAST("hash-algorithm-chosen"), hashname ? hashname : strnull))
  1648. goto bad;
  1649. return xmlpost_complete(doc, body);
  1650. bad:
  1651. xmlpost_complete(doc, NULL);
  1652. return -ENOMEM;
  1653. }
  1654. int prepare_multicert_response(struct openconnect_info *vpninfo,
  1655. struct cert_request cert_rq, const char *challenge,
  1656. struct oc_text_buf *body)
  1657. {
  1658. struct cert_info *certinfo = &vpninfo->certinfo[1];
  1659. struct oc_text_buf *certdata = NULL, *certtext = NULL;
  1660. struct oc_text_buf *signdata = NULL, *signtext = NULL;
  1661. openconnect_hash_type hash;
  1662. int ret;
  1663. if (!cert_rq.hashes) {
  1664. vpn_progress(vpninfo, PRG_ERR,
  1665. _("Multiple-certificate authentication signature hash algorithm negotiation failed.\n"));
  1666. ret = -EIO;
  1667. goto done;
  1668. }
  1669. ret = export_certificate_pkcs7(vpninfo, certinfo, CERT_FORMAT_ASN1, &certdata);
  1670. if (ret >= 0)
  1671. ret = to_base64(&certtext,
  1672. BUF_DATA(certdata),
  1673. BUF_SIZE(certdata));
  1674. if (ret < 0) {
  1675. vpn_progress(vpninfo, PRG_ERR,
  1676. _("Error exporting multiple-certificate signer's certificate chain.\n"));
  1677. goto done;
  1678. }
  1679. ret = multicert_sign_data(vpninfo, certinfo, cert_rq.hashes,
  1680. challenge, strlen(challenge),
  1681. &signdata);
  1682. if (ret < 0)
  1683. goto done;
  1684. hash = ret;
  1685. if ((ret = to_base64(&signtext, BUF_DATA(signdata), BUF_SIZE(signdata))) < 0) {
  1686. vpn_progress(vpninfo, PRG_ERR,
  1687. _("Error encoding the challenge response.\n"));
  1688. goto done;
  1689. }
  1690. ret = post_multicert_response(vpninfo, XCAST(BUF_DATA(certtext)),
  1691. hash, XCAST(BUF_DATA(signtext)),
  1692. body);
  1693. done:
  1694. buf_free(certdata); buf_free(certtext);
  1695. buf_free(signdata); buf_free(signtext);
  1696. return ret;
  1697. }