auth.c 55 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974
  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), NULL, 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-v2"));
  737. if (!node)
  738. goto bad;
  739. #ifdef HAVE_HPKE_SUPPORT
  740. node = xmlNewTextChild(capabilities, NULL, XCAST("auth-method"), XCAST("single-sign-on-external-browser"));
  741. if (!node)
  742. goto bad;
  743. #endif
  744. if (vpninfo->certinfo[1].cert) {
  745. node = xmlNewTextChild(capabilities, NULL, XCAST("auth-method"), XCAST("multiple-cert"));
  746. if (!node)
  747. goto bad;
  748. }
  749. *rootp = root;
  750. return doc;
  751. bad:
  752. xmlFreeDoc(doc);
  753. return NULL;
  754. }
  755. static int xmlpost_complete(xmlDocPtr doc, struct oc_text_buf *body)
  756. {
  757. xmlChar *mem = NULL;
  758. int len, ret = 0;
  759. if (!body) {
  760. xmlFree(doc);
  761. return 0;
  762. }
  763. xmlDocDumpMemoryEnc(doc, &mem, &len, "UTF-8");
  764. if (!mem) {
  765. xmlFreeDoc(doc);
  766. return -ENOMEM;
  767. }
  768. buf_append_bytes(body, mem, len);
  769. xmlFreeDoc(doc);
  770. xmlFree(mem);
  771. return ret;
  772. }
  773. static int xmlpost_initial_req(struct openconnect_info *vpninfo,
  774. struct oc_text_buf *request_body, int cert_fail)
  775. {
  776. xmlNodePtr root, node;
  777. xmlDocPtr doc = xmlpost_new_query(vpninfo, "init", &root);
  778. char *url;
  779. if (!doc)
  780. return -ENOMEM;
  781. url = internal_get_url(vpninfo);
  782. if (!url)
  783. goto bad;
  784. node = xmlNewTextChild(root, NULL, XCAST("group-access"), XCAST(url));
  785. if (!node)
  786. goto bad;
  787. if (cert_fail) {
  788. node = xmlNewTextChild(root, NULL, XCAST("client-cert-fail"), NULL);
  789. if (!node)
  790. goto bad;
  791. }
  792. if (vpninfo->authgroup) {
  793. node = xmlNewTextChild(root, NULL, XCAST("group-select"), XCAST(vpninfo->authgroup));
  794. if (!node)
  795. goto bad;
  796. }
  797. free(url);
  798. return xmlpost_complete(doc, request_body);
  799. bad:
  800. xmlpost_complete(doc, NULL);
  801. return -ENOMEM;
  802. }
  803. static int xmlpost_append_form_opts(struct openconnect_info *vpninfo,
  804. struct oc_auth_form *form, struct oc_text_buf *body)
  805. {
  806. xmlNodePtr root, node;
  807. xmlDocPtr doc = xmlpost_new_query(vpninfo, "auth-reply", &root);
  808. struct oc_form_opt *opt;
  809. if (!doc)
  810. return -ENOMEM;
  811. if (vpninfo->opaque_srvdata) {
  812. node = xmlCopyNode(vpninfo->opaque_srvdata, 1);
  813. if (!node)
  814. goto bad;
  815. if (!xmlAddChild(root, node))
  816. goto bad;
  817. }
  818. node = xmlNewChild(root, NULL, XCAST("auth"), NULL);
  819. if (!node)
  820. goto bad;
  821. for (opt = form->opts; opt; opt = opt->next) {
  822. /* group_list: create a new <group-select> node under <config-auth> */
  823. if (!strcmp(opt->name, "group_list")) {
  824. if (!xmlNewTextChild(root, NULL, XCAST("group-select"), XCAST(opt->_value)))
  825. goto bad;
  826. continue;
  827. }
  828. /* answer,whichpin,new_password: rename to "password" */
  829. if (!strcmp(opt->name, "answer") ||
  830. !strcmp(opt->name, "whichpin") ||
  831. !strcmp(opt->name, "new_password")) {
  832. if (!xmlNewTextChild(node, NULL, XCAST("password"), XCAST(opt->_value)))
  833. goto bad;
  834. continue;
  835. }
  836. /* verify_pin,verify_password: ignore */
  837. if (!strcmp(opt->name, "verify_pin") ||
  838. !strcmp(opt->name, "verify_password")) {
  839. continue;
  840. }
  841. /* everything else: create <foo>user_input</foo> under <auth> */
  842. if (!xmlNewTextChild(node, NULL, XCAST(opt->name), XCAST(opt->_value)))
  843. goto bad;
  844. }
  845. if (vpninfo->csd_token &&
  846. !xmlNewTextChild(root, NULL, XCAST("host-scan-token"), XCAST(vpninfo->csd_token)))
  847. goto bad;
  848. return xmlpost_complete(doc, body);
  849. bad:
  850. xmlpost_complete(doc, NULL);
  851. return -ENOMEM;
  852. }
  853. /* Return value:
  854. * < 0, if unable to generate a tokencode
  855. * = 0, on success
  856. */
  857. static int cstp_can_gen_tokencode(struct openconnect_info *vpninfo,
  858. struct oc_auth_form *form,
  859. struct oc_form_opt *opt)
  860. {
  861. if (vpninfo->token_mode == OC_TOKEN_MODE_NONE ||
  862. vpninfo->token_bypassed)
  863. return -EINVAL;
  864. #ifdef HAVE_LIBSTOKEN
  865. if (vpninfo->token_mode == OC_TOKEN_MODE_STOKEN) {
  866. if (strcmp(opt->name, "password") &&
  867. strcmp(opt->name, "answer"))
  868. return -EINVAL;
  869. return can_gen_stoken_code(vpninfo, form, opt);
  870. }
  871. #endif
  872. /* Otherwise it's an OATH token of some kind. */
  873. if (!strcmp(opt->name, "secondary_password") ||
  874. (form->auth_id && !strcmp(form->auth_id, "challenge")))
  875. return can_gen_tokencode(vpninfo, form, opt);
  876. return -EINVAL;
  877. }
  878. static int fetch_config(struct openconnect_info *vpninfo)
  879. {
  880. struct oc_text_buf *buf;
  881. int result;
  882. unsigned char local_sha1_bin[SHA1_SIZE];
  883. char local_sha1_ascii[(SHA1_SIZE * 2)+1];
  884. int i;
  885. if (!vpninfo->profile_url || !vpninfo->profile_sha1 || !vpninfo->write_new_config)
  886. return -ENOENT;
  887. if (!strncasecmp(vpninfo->xmlsha1, vpninfo->profile_sha1, SHA1_SIZE * 2)) {
  888. vpn_progress(vpninfo, PRG_TRACE,
  889. _("Not downloading XML profile because SHA1 already matches\n"));
  890. return 0;
  891. }
  892. if ((result = openconnect_open_https(vpninfo))) {
  893. vpn_progress(vpninfo, PRG_ERR,
  894. _("Failed to open HTTPS connection to %s\n"),
  895. vpninfo->hostname);
  896. return result;
  897. }
  898. buf = buf_alloc();
  899. if (vpninfo->port != 443)
  900. buf_append(buf, "GET %s:%d HTTP/1.1\r\n", vpninfo->profile_url, vpninfo->port);
  901. else
  902. buf_append(buf, "GET %s HTTP/1.1\r\n", vpninfo->profile_url);
  903. cstp_common_headers(vpninfo, buf);
  904. buf_append(buf, "\r\n");
  905. if (buf_error(buf))
  906. return buf_free(buf);
  907. if (vpninfo->dump_http_traffic)
  908. dump_buf(vpninfo, '>', buf->data);
  909. if (vpninfo->ssl_write(vpninfo, buf->data, buf->pos) != buf->pos) {
  910. vpn_progress(vpninfo, PRG_ERR,
  911. _("Failed to send GET request for new config\n"));
  912. buf_free(buf);
  913. return -EIO;
  914. }
  915. result = process_http_response(vpninfo, 0, NULL, buf);
  916. if (result < 0) {
  917. /* We'll already have complained about whatever offended us */
  918. buf_free(buf);
  919. return -EINVAL;
  920. }
  921. if (result != 200) {
  922. buf_free(buf);
  923. return -EINVAL;
  924. }
  925. openconnect_sha1(local_sha1_bin, buf->data, buf->pos);
  926. for (i = 0; i < SHA1_SIZE; i++)
  927. sprintf(&local_sha1_ascii[i*2], "%02x", local_sha1_bin[i]);
  928. if (strcasecmp(vpninfo->profile_sha1, local_sha1_ascii)) {
  929. vpn_progress(vpninfo, PRG_ERR,
  930. _("Downloaded config file did not match intended SHA1\n"));
  931. buf_free(buf);
  932. return -EINVAL;
  933. }
  934. vpn_progress(vpninfo, PRG_DEBUG, _("Downloaded new XML profile\n"));
  935. result = vpninfo->write_new_config(vpninfo->cbdata, buf->data, buf->pos);
  936. buf_free(buf);
  937. return result;
  938. }
  939. int set_csd_user(struct openconnect_info *vpninfo)
  940. {
  941. #if defined(_WIN32) || defined(__native_client__)
  942. vpn_progress(vpninfo, PRG_ERR,
  943. _("Error: Running the 'Cisco Secure Desktop' trojan on this platform is not yet implemented.\n"));
  944. return -EPERM;
  945. #else
  946. setsid();
  947. if (vpninfo->uid_csd_given && vpninfo->uid_csd != getuid()) {
  948. struct passwd *pw;
  949. int err;
  950. if (setgid(vpninfo->gid_csd)) {
  951. err = errno;
  952. fprintf(stderr, _("Failed to set gid %ld: %s\n"),
  953. (long)vpninfo->uid_csd, strerror(err));
  954. return -err;
  955. }
  956. if (setgroups(1, &vpninfo->gid_csd)) {
  957. err = errno;
  958. fprintf(stderr, _("Failed to set groups to %ld: %s\n"),
  959. (long)vpninfo->uid_csd, strerror(err));
  960. return -err;
  961. }
  962. if (setuid(vpninfo->uid_csd)) {
  963. err = errno;
  964. fprintf(stderr, _("Failed to set uid %ld: %s\n"),
  965. (long)vpninfo->uid_csd, strerror(err));
  966. return -err;
  967. }
  968. if (!(pw = getpwuid(vpninfo->uid_csd))) {
  969. err = errno;
  970. fprintf(stderr, _("Invalid user uid=%ld: %s\n"),
  971. (long)vpninfo->uid_csd, strerror(err));
  972. return -err;
  973. }
  974. setenv("HOME", pw->pw_dir, 1);
  975. if (chdir(pw->pw_dir)) {
  976. err = errno;
  977. fprintf(stderr, _("Failed to change to CSD home directory '%s': %s\n"),
  978. pw->pw_dir, strerror(err));
  979. return -err;
  980. }
  981. }
  982. return 0;
  983. #endif
  984. }
  985. static int run_csd_script(struct openconnect_info *vpninfo, char *buf, int buflen)
  986. {
  987. #if defined(_WIN32) || defined(__native_client__)
  988. vpn_progress(vpninfo, PRG_ERR,
  989. _("Error: Running the 'Cisco Secure Desktop' trojan on this platform is not yet implemented.\n"));
  990. return -EPERM;
  991. #else
  992. char fname[64];
  993. int fd, ret;
  994. pid_t child;
  995. if (!vpninfo->csd_wrapper && !buflen) {
  996. vpn_progress(vpninfo, PRG_ERR,
  997. _("Error: Server asked us to run CSD hostscan.\n"
  998. "You need to provide a suitable --csd-wrapper argument.\n"));
  999. return -EINVAL;
  1000. }
  1001. if (!vpninfo->uid_csd_given && !vpninfo->csd_wrapper) {
  1002. vpn_progress(vpninfo, PRG_ERR,
  1003. _("Error: Server asked us to download and run a 'Cisco Secure Desktop' trojan.\n"
  1004. "This facility is disabled by default for security reasons, so you may wish to enable it.\n"));
  1005. return -EPERM;
  1006. }
  1007. fname[0] = 0;
  1008. if (buflen) {
  1009. struct oc_vpn_option *opt;
  1010. const char *tmpdir = NULL;
  1011. /* If the caller wanted $TMPDIR set for the CSD script, that
  1012. means for us too; look through the csd_env for a TMPDIR
  1013. override. */
  1014. for (opt = vpninfo->csd_env; opt; opt = opt->next) {
  1015. if (!strcmp(opt->option, "TMPDIR")) {
  1016. tmpdir = opt->value;
  1017. break;
  1018. }
  1019. }
  1020. if (!opt)
  1021. tmpdir = getenv("TMPDIR");
  1022. if (!tmpdir && !access("/var/tmp", W_OK))
  1023. tmpdir = "/var/tmp";
  1024. if (!tmpdir)
  1025. tmpdir = "/tmp";
  1026. if (access(tmpdir, W_OK))
  1027. vpn_progress(vpninfo, PRG_ERR,
  1028. _("Temporary directory '%s' is not writable: %s\n"),
  1029. tmpdir, strerror(errno));
  1030. snprintf(fname, 64, "%s/csdXXXXXX", tmpdir);
  1031. fd = mkstemp(fname);
  1032. if (fd < 0) {
  1033. int err = -errno;
  1034. vpn_progress(vpninfo, PRG_ERR,
  1035. _("Failed to open temporary CSD script file: %s\n"),
  1036. strerror(errno));
  1037. return err;
  1038. }
  1039. ret = write(fd, (void *)buf, buflen);
  1040. if (ret != buflen) {
  1041. int err = -errno;
  1042. vpn_progress(vpninfo, PRG_ERR,
  1043. _("Failed to write temporary CSD script file: %s\n"),
  1044. strerror(errno));
  1045. return err;
  1046. }
  1047. fchmod(fd, 0755);
  1048. close(fd);
  1049. }
  1050. vpn_progress(vpninfo, PRG_INFO,
  1051. _("Trying to run CSD Trojan script '%s'.\n"),
  1052. vpninfo->csd_wrapper ?: fname);
  1053. child = fork();
  1054. if (child == -1) {
  1055. goto out;
  1056. } else if (child > 0) {
  1057. /* in parent: must reap child process */
  1058. int status;
  1059. waitpid(child, &status, 0);
  1060. if (!WIFEXITED(status)) {
  1061. vpn_progress(vpninfo, PRG_ERR,
  1062. _("CSD script '%s' exited abnormally\n"),
  1063. vpninfo->csd_wrapper ?: fname);
  1064. ret = -EINVAL;
  1065. } else {
  1066. if (WEXITSTATUS(status) != 0) {
  1067. vpn_progress(vpninfo, PRG_ERR,
  1068. _("CSD script '%s' returned non-zero status: %d\n"),
  1069. vpninfo->csd_wrapper ?: fname, WEXITSTATUS(status));
  1070. /* Some scripts do exit non-zero, and it's never mattered.
  1071. * Don't abort for now. */
  1072. vpn_progress(vpninfo, PRG_ERR,
  1073. _("Authentication may fail. If your script is not returning zero, fix it.\n"
  1074. "Future versions of openconnect will abort on this error.\n"));
  1075. } else {
  1076. vpn_progress(vpninfo, PRG_INFO,
  1077. _("CSD script '%s' completed successfully.\n"),
  1078. vpninfo->csd_wrapper ?: fname);
  1079. }
  1080. free(vpninfo->urlpath);
  1081. vpninfo->urlpath = strdup(vpninfo->csd_waiturl +
  1082. (vpninfo->csd_waiturl[0] == '/' ? 1 : 0));
  1083. vpninfo->csd_scriptname = strdup(fname);
  1084. http_add_cookie(vpninfo, "sdesktop", vpninfo->csd_token, 1);
  1085. ret = 0;
  1086. }
  1087. free(vpninfo->csd_stuburl);
  1088. vpninfo->csd_stuburl = NULL;
  1089. free(vpninfo->csd_waiturl);
  1090. vpninfo->csd_waiturl = NULL;
  1091. return ret;
  1092. } else {
  1093. /* in child: will be reaped by init */
  1094. char scertbuf[MD5_SIZE * 2 + 1];
  1095. char ccertbuf[MD5_SIZE * 2 + 1];
  1096. char *csd_argv[32];
  1097. int i = 0;
  1098. if (set_csd_user(vpninfo) < 0)
  1099. exit(1);
  1100. if (getuid() == 0 && !vpninfo->csd_wrapper) {
  1101. fprintf(stderr, _("Warning: you are running insecure CSD code with root privileges\n"
  1102. "\t Use command line option \"--csd-user\"\n"));
  1103. }
  1104. /*
  1105. * Spurious stdout output from the CSD trojan will break both
  1106. * the NM tool and the various cookieonly modes.
  1107. * Also, gnome-shell *closes* stderr so attempt to cope with that
  1108. * by opening /dev/null, because otherwise some CSD scripts fail.
  1109. * Actually, perhaps we should set up our own pipes, and report
  1110. * the trojan's output via vpn_progress().
  1111. */
  1112. if (ferror(stderr)) {
  1113. int nulfd = open("/dev/null", O_WRONLY);
  1114. if (nulfd >= 0) {
  1115. dup2(nulfd, 2);
  1116. close(nulfd);
  1117. }
  1118. }
  1119. dup2(2, 1);
  1120. if (vpninfo->csd_wrapper)
  1121. csd_argv[i++] = openconnect_utf8_to_legacy(vpninfo,
  1122. vpninfo->csd_wrapper);
  1123. csd_argv[i++] = fname;
  1124. csd_argv[i++] = (char *)"-ticket";
  1125. if (asprintf(&csd_argv[i++], "\"%s\"", vpninfo->csd_ticket) == -1)
  1126. goto out;
  1127. csd_argv[i++] = (char *)"-stub";
  1128. csd_argv[i++] = (char *)"\"0\"";
  1129. csd_argv[i++] = (char *)"-group";
  1130. if (asprintf(&csd_argv[i++], "\"%s\"", vpninfo->authgroup?:"") == -1)
  1131. goto out;
  1132. openconnect_local_cert_md5(vpninfo, ccertbuf);
  1133. scertbuf[0] = 0;
  1134. get_cert_md5_fingerprint(vpninfo, vpninfo->peer_cert, scertbuf);
  1135. csd_argv[i++] = (char *)"-certhash";
  1136. if (asprintf(&csd_argv[i++], "\"%s:%s\"", scertbuf, ccertbuf) == -1)
  1137. goto out;
  1138. csd_argv[i++] = (char *)"-url";
  1139. if (asprintf(&csd_argv[i++], "\"https://%s%s\"", openconnect_get_hostname(vpninfo), vpninfo->csd_starturl) == -1)
  1140. goto out;
  1141. csd_argv[i++] = (char *)"-langselen";
  1142. csd_argv[i++] = NULL;
  1143. if (setenv("CSD_SHA256", openconnect_get_peer_cert_hash(vpninfo)+11, 1)) /* remove initial 'pin-sha256:' */
  1144. goto out;
  1145. if (setenv("CSD_TOKEN", vpninfo->csd_token, 1))
  1146. goto out;
  1147. if (setenv("CSD_HOSTNAME", openconnect_get_hostname(vpninfo), 1))
  1148. goto out;
  1149. apply_script_env(vpninfo->csd_env);
  1150. execv(csd_argv[0], csd_argv);
  1151. out:
  1152. vpn_progress(vpninfo, PRG_ERR,
  1153. _("Failed to exec CSD script %s\n"), vpninfo->csd_wrapper ?: fname);
  1154. exit(1);
  1155. }
  1156. #endif /* !_WIN32 && !__native_client__ */
  1157. }
  1158. /* Return value:
  1159. * < 0, if the data is unrecognized
  1160. * = 0, if the page contains an XML document
  1161. * = 1, if the page is a wait/refresh HTML page
  1162. */
  1163. static int check_response_type(struct openconnect_info *vpninfo, char *form_buf)
  1164. {
  1165. if (strncmp(form_buf, "<?xml", 5)) {
  1166. /* Not XML? Perhaps it's HTML with a refresh... */
  1167. if (strcasestr(form_buf, "http-equiv=\"refresh\""))
  1168. return 1;
  1169. vpn_progress(vpninfo, PRG_ERR,
  1170. _("Unknown response from server\n"));
  1171. return -EINVAL;
  1172. }
  1173. return 0;
  1174. }
  1175. /* Return value:
  1176. * < 0, on error
  1177. * > 0, no cookie (user cancel)
  1178. * = 0, obtained cookie
  1179. */
  1180. int cstp_obtain_cookie(struct openconnect_info *vpninfo)
  1181. {
  1182. struct oc_vpn_option *opt;
  1183. char *form_buf = NULL;
  1184. struct oc_auth_form *form = NULL;
  1185. int result, buflen, tries;
  1186. struct oc_text_buf *request_body = buf_alloc();
  1187. const char *request_body_type;
  1188. const char *method = "POST";
  1189. char *orig_host = NULL, *orig_path = NULL, *form_path = NULL;
  1190. int orig_port = 0;
  1191. struct cert_request cert_rq = { 0 };
  1192. int cert_sent = !vpninfo->certinfo[0].cert;
  1193. int newgroup_attempts = 5;
  1194. if (!vpninfo->xmlpost)
  1195. goto no_xmlpost;
  1196. /*
  1197. * Step 2: Probe for XML POST compatibility
  1198. *
  1199. * This can get stuck in a redirect loop, so give up after any of:
  1200. *
  1201. * a) HTTP error (e.g. 400 Bad Request)
  1202. * b) Same-host redirect (e.g. Location: /foo/bar)
  1203. * c) Three redirects without seeing a plausible login form
  1204. */
  1205. newgroup:
  1206. if (newgroup_attempts-- <= 0) {
  1207. result = -1;
  1208. goto out;
  1209. }
  1210. buf_truncate(request_body);
  1211. result = xmlpost_initial_req(vpninfo, request_body, 0);
  1212. if (result < 0)
  1213. goto out;
  1214. free(orig_host);
  1215. free(orig_path);
  1216. orig_host = strdup(vpninfo->hostname);
  1217. orig_path = vpninfo->urlpath ? strdup(vpninfo->urlpath) : NULL;
  1218. orig_port = vpninfo->port;
  1219. for (tries = 0; ; tries++) {
  1220. /**
  1221. * Multiple certificate authentication requires an additional exchange.
  1222. * tries == 0: redirect
  1223. * tries == 1: !cert_sent + initial_req
  1224. * tries == 2: cert_sent + initial_req
  1225. * tries == 3: challenge response
  1226. */
  1227. if (tries == 3 + !!(cert_rq.state&CERT2_REQUESTED)) {
  1228. fail:
  1229. if (vpninfo->xmlpost) {
  1230. no_xmlpost:
  1231. /* Try without XML POST this time... */
  1232. tries = 0;
  1233. vpninfo->xmlpost = 0;
  1234. request_body_type = NULL;
  1235. buf_truncate(request_body);
  1236. method = "GET";
  1237. if (orig_host) {
  1238. openconnect_set_hostname(vpninfo, orig_host);
  1239. free(orig_host);
  1240. orig_host = NULL;
  1241. free(vpninfo->urlpath);
  1242. vpninfo->urlpath = orig_path;
  1243. orig_path = NULL;
  1244. vpninfo->port = orig_port;
  1245. }
  1246. openconnect_close_https(vpninfo, 0);
  1247. } else {
  1248. result = -EIO;
  1249. goto out;
  1250. }
  1251. }
  1252. request_body_type = vpninfo->xmlpost ? "application/xml; charset=utf-8" : "application/x-www-form-urlencoded";
  1253. result = do_https_request(vpninfo, method, request_body_type, request_body, &form_buf, NULL, HTTP_NO_FLAGS);
  1254. if (vpninfo->got_cancel_cmd) {
  1255. result = 1;
  1256. goto out;
  1257. }
  1258. if (result == -EINVAL)
  1259. goto fail;
  1260. if (result < 0)
  1261. goto out;
  1262. /* Some ASAs forget to send the TLS cert request on the initial connection.
  1263. * If we have a client cert, disable HTTP keepalive until we get a real
  1264. * login form (not a redirect). */
  1265. if (!cert_sent)
  1266. openconnect_close_https(vpninfo, 0);
  1267. /* XML POST does not allow local redirects, but GET does. */
  1268. if (vpninfo->xmlpost &&
  1269. vpninfo->redirect_type == REDIR_TYPE_LOCAL)
  1270. goto fail;
  1271. else if (vpninfo->redirect_type != REDIR_TYPE_NONE)
  1272. continue;
  1273. result = parse_xml_response(vpninfo, form_buf, &form, &cert_rq);
  1274. if (result < 0)
  1275. goto fail;
  1276. if ((cert_rq.state&CERT1_REQUESTED) &&
  1277. !(cert_rq.state&CERT1_AUTHENTICATED)) {
  1278. int cert_failed = 0;
  1279. free_auth_form(form);
  1280. form = NULL;
  1281. if (!cert_sent && vpninfo->certinfo[0].cert) {
  1282. /* Try again on a fresh connection. */
  1283. cert_sent = 1;
  1284. } else if (cert_sent && vpninfo->certinfo[0].cert) {
  1285. /* Try again with <client-cert-fail/> in the request */
  1286. vpn_progress(vpninfo, PRG_ERR,
  1287. _("Server requested SSL client certificate after one was provided\n"));
  1288. cert_failed = 1;
  1289. } else {
  1290. vpn_progress(vpninfo, PRG_INFO,
  1291. _("Server requested SSL client certificate; none was configured\n"));
  1292. cert_failed = 1;
  1293. }
  1294. buf_truncate(request_body);
  1295. result = xmlpost_initial_req(vpninfo, request_body, cert_failed);
  1296. if (result < 0)
  1297. goto fail;
  1298. continue;
  1299. } else if (cert_rq.state&CERT2_REQUESTED) {
  1300. free_auth_form(form); form = NULL;
  1301. buf_truncate(request_body);
  1302. /** load the second certificate */
  1303. struct cert_info *certinfo = &vpninfo->certinfo[1];
  1304. if (!certinfo->cert) {
  1305. /* This is a fail safe; we should never get here */
  1306. vpn_progress(vpninfo, PRG_ERR,
  1307. _("Multiple-certificate authentication requires a second certificate; none were configured.\n"));
  1308. result = -EINVAL;
  1309. (void) xmlpost_initial_req(vpninfo, request_body, 1);
  1310. goto out;
  1311. }
  1312. result = load_certificate(vpninfo, certinfo, MULTICERT_COMPAT);
  1313. if (result < 0) {
  1314. (void) xmlpost_initial_req(vpninfo, request_body, 1);
  1315. goto out;
  1316. }
  1317. result = prepare_multicert_response(vpninfo, cert_rq, form_buf,
  1318. request_body);
  1319. unload_certificate(certinfo, 1);
  1320. if (result < 0) {
  1321. (void) xmlpost_initial_req(vpninfo, request_body, 0);
  1322. goto fail;
  1323. }
  1324. continue;
  1325. }
  1326. if (form && form->action) {
  1327. vpninfo->redirect_url = strdup(form->action);
  1328. handle_redirect(vpninfo);
  1329. }
  1330. break;
  1331. }
  1332. if (vpninfo->xmlpost)
  1333. vpn_progress(vpninfo, PRG_INFO, _("XML POST enabled\n"));
  1334. /* Step 4: Run the CSD trojan, if applicable */
  1335. if (vpninfo->csd_starturl && vpninfo->csd_waiturl) {
  1336. buflen = 0;
  1337. if (vpninfo->urlpath) {
  1338. form_path = strdup(vpninfo->urlpath);
  1339. if (!form_path) {
  1340. result = -ENOMEM;
  1341. goto out;
  1342. }
  1343. }
  1344. /* fetch the CSD program, if available */
  1345. if (vpninfo->csd_stuburl) {
  1346. vpninfo->redirect_url = vpninfo->csd_stuburl;
  1347. vpninfo->csd_stuburl = NULL;
  1348. handle_redirect(vpninfo);
  1349. buflen = do_https_request(vpninfo, "GET", NULL, NULL, &form_buf, NULL, HTTP_NO_FLAGS);
  1350. if (buflen <= 0) {
  1351. if (vpninfo->csd_wrapper) {
  1352. vpn_progress(vpninfo, PRG_ERR,
  1353. _("Couldn't fetch CSD stub. Proceeding anyway with CSD wrapper script.\n"));
  1354. buflen = 0;
  1355. } else {
  1356. result = -EINVAL;
  1357. goto out;
  1358. }
  1359. } else
  1360. vpn_progress(vpninfo, PRG_INFO,
  1361. _("Fetched CSD stub for %s platform (size is %d bytes).\n"),
  1362. vpninfo->platname, buflen);
  1363. }
  1364. /* This is the CSD stub script, which we now need to run */
  1365. result = run_csd_script(vpninfo, form_buf, buflen);
  1366. if (result)
  1367. goto out;
  1368. /* vpninfo->urlpath now points to the wait page */
  1369. while (1) {
  1370. result = do_https_request(vpninfo, "GET", NULL, NULL, &form_buf, NULL, HTTP_NO_FLAGS);
  1371. if (result <= 0)
  1372. break;
  1373. result = check_response_type(vpninfo, form_buf);
  1374. if (result <= 0)
  1375. break;
  1376. vpn_progress(vpninfo, PRG_INFO,
  1377. _("Refreshing %s after 1 second...\n"),
  1378. vpninfo->urlpath);
  1379. sleep(1);
  1380. }
  1381. if (result < 0)
  1382. goto out;
  1383. /* refresh the form page, to see if we're authorized now */
  1384. free(vpninfo->urlpath);
  1385. vpninfo->urlpath = form_path;
  1386. form_path = NULL;
  1387. result = do_https_request(vpninfo,
  1388. vpninfo->xmlpost ? "POST" : "GET",
  1389. request_body_type, request_body, &form_buf, NULL, HTTP_REDIRECT);
  1390. if (result < 0)
  1391. goto out;
  1392. result = parse_xml_response(vpninfo, form_buf, &form, NULL);
  1393. if (result < 0)
  1394. goto out;
  1395. }
  1396. /* Step 5: Ask the user to fill in the auth form; repeat as necessary */
  1397. while (1) {
  1398. buf_truncate(request_body);
  1399. result = handle_auth_form(vpninfo, form, request_body,
  1400. &method, &request_body_type);
  1401. if (result < 0 || result == OC_FORM_RESULT_CANCELLED)
  1402. goto out;
  1403. if (result == OC_FORM_RESULT_LOGGEDIN)
  1404. break;
  1405. if (result == OC_FORM_RESULT_NEWGROUP) {
  1406. free(form_buf);
  1407. form_buf = NULL;
  1408. free_auth_form(form);
  1409. form = NULL;
  1410. goto newgroup;
  1411. }
  1412. result = do_https_request(vpninfo, method, request_body_type, request_body, &form_buf, NULL, 1);
  1413. if (result < 0)
  1414. goto out;
  1415. result = parse_xml_response(vpninfo, form_buf, &form, NULL);
  1416. if (result < 0)
  1417. goto out;
  1418. if (form->action) {
  1419. vpninfo->redirect_url = strdup(form->action);
  1420. handle_redirect(vpninfo);
  1421. }
  1422. }
  1423. /* A return value of 2 means the XML form indicated
  1424. success. We _should_ have a cookie... */
  1425. struct oc_text_buf *cookie_buf = buf_alloc();
  1426. #ifdef HAVE_HPKE_SUPPORT
  1427. if (vpninfo->strap_key) {
  1428. buf_append(cookie_buf, "openconnect_strapkey=");
  1429. append_strap_privkey(vpninfo, cookie_buf);
  1430. buf_append(cookie_buf, "; webvpn=");
  1431. }
  1432. #endif
  1433. for (opt = vpninfo->cookies; opt; opt = opt->next) {
  1434. if (!strcmp(opt->option, "webvpn")) {
  1435. buf_append(cookie_buf, "%s", opt->value);
  1436. } else if (vpninfo->write_new_config && !strcmp(opt->option, "webvpnc")) {
  1437. char *tok = opt->value;
  1438. char *bu = NULL, *fu = NULL, *sha = NULL;
  1439. do {
  1440. if (tok != opt->value)
  1441. *(tok++) = 0;
  1442. if (!strncmp(tok, "bu:", 3))
  1443. bu = tok + 3;
  1444. else if (!strncmp(tok, "fu:", 3))
  1445. fu = tok + 3;
  1446. else if (!strncmp(tok, "fh:", 3))
  1447. sha = tok + 3;
  1448. } while ((tok = strchr(tok, '&')));
  1449. if (bu && fu && sha) {
  1450. if (asprintf(&vpninfo->profile_url, "%s%s", bu, fu) == -1) {
  1451. buf_free(cookie_buf);
  1452. result = -ENOMEM;
  1453. goto out;
  1454. }
  1455. vpninfo->profile_sha1 = strdup(sha);
  1456. }
  1457. }
  1458. }
  1459. if (buf_error(cookie_buf)) {
  1460. result = buf_free(cookie_buf);
  1461. goto out;
  1462. }
  1463. free(vpninfo->cookie);
  1464. vpninfo->cookie = cookie_buf->data;
  1465. cookie_buf->data = NULL;
  1466. buf_free(cookie_buf);
  1467. result = 0;
  1468. fetch_config(vpninfo);
  1469. out:
  1470. buf_free(request_body);
  1471. free (orig_host);
  1472. free (orig_path);
  1473. free(form_path);
  1474. free(form_buf);
  1475. free_auth_form(form);
  1476. if (vpninfo->csd_scriptname) {
  1477. unlink(vpninfo->csd_scriptname);
  1478. free(vpninfo->csd_scriptname);
  1479. vpninfo->csd_scriptname = NULL;
  1480. }
  1481. return result;
  1482. }
  1483. /**
  1484. * Multiple certificate authentication
  1485. *
  1486. * Two certificates are employed: a "machine" certificate and a
  1487. * "user" certificate. The machine certificate is used to establish
  1488. * the TLS session. The user certificate is used to sign a challenge.
  1489. *
  1490. * An example XML exchange follows. For brevity, tags and attributes whose
  1491. * values are irrelevant (e.g. <opaque>) or well-understood from other auth
  1492. * types are omitted:
  1493. *
  1494. * CLIENT's initial request should include multiple-cert in capabilities:
  1495. *
  1496. * <config-auth client="vpn" type="init">
  1497. * <capabilities>
  1498. * <auth-method>multiple-cert</auth-method>
  1499. * </capabilities>
  1500. * </config-auth>
  1501. *
  1502. * SERVER's response should include <multiple-client-cert-request> with list
  1503. * of hash algorithms, and empty <cert-authenticated> tag:
  1504. *
  1505. * <config-auth client="vpn" type="auth-request">
  1506. * <multiple-client-cert-request>
  1507. * <hash-algorithm>sha256</hash-algorithm>
  1508. * <hash-algorithm>sha384</hash-algorithm>
  1509. * <hash-algorithm>sha512</hash-algorithm>
  1510. * </multiple-client-cert-request>
  1511. *
  1512. * <!-- Ensures that the client has signed this specific request in subsequent reply -->
  1513. * <random>FA4003BD87436B227...C138A08FF724F0100015B863F750914839EE79C86DFE8F0B9A0199E2</random>
  1514. *
  1515. * <!-- Appears to indicate that "machine" cert was accepted -->
  1516. * <cert-authenticated/>
  1517. * </config-auth>
  1518. *
  1519. * CLIENT's second request should include the "user" certificate (and any
  1520. * required intermediates) in PKCS7 format, along with a signature of the
  1521. * complete XML body of the server's prior response:
  1522. *
  1523. * <config-auth client="vpn" type="auth-reply">
  1524. * <auth>
  1525. * <client-cert-chain cert-store="1M">
  1526. * <client-cert-sent-via-protocol/>
  1527. * </client-cert-chain>
  1528. * <client-cert-chain cert-store="1U">
  1529. * <client-cert cert-format="pkcs7">
  1530. * <!-- PKCS7 "user" certificate and intermediate (base64-encoded) -->
  1531. * </client-cert>
  1532. * <client-cert-auth-signature hash-algorithm-chosen="sha512">
  1533. * <!-- signature on server's prior response with private key of "user" certificate -->
  1534. * </client-cert-auth-signature>
  1535. * </client-cert-chain>
  1536. * </auth>
  1537. * </config-auth>
  1538. */
  1539. static int to_base64(struct oc_text_buf **result,
  1540. const void *data, size_t data_len)
  1541. {
  1542. const uint8_t *dp = data;
  1543. struct oc_text_buf *buf;
  1544. int ret;
  1545. *result = NULL;
  1546. /**
  1547. * Line feed every 64 characters. No feed needed on last line
  1548. * */
  1549. buf = buf_alloc();
  1550. if (!buf)
  1551. return -ENOMEM;
  1552. buf_append_base64(buf, dp, data_len, 64);
  1553. ret = buf_error(buf);
  1554. if (ret < 0)
  1555. goto out;
  1556. *result = buf;
  1557. buf = NULL;
  1558. out:
  1559. buf_free(buf);
  1560. return ret;
  1561. }
  1562. /**
  1563. * parse request
  1564. */
  1565. void parse_multicert_request(struct openconnect_info *vpninfo,
  1566. xmlNodePtr node, struct cert_request *cert_rq)
  1567. {
  1568. xmlNodePtr child;
  1569. char *content;
  1570. openconnect_hash_type hash;
  1571. unsigned int oldhashes = 0;
  1572. /* node is a multiple-client-cert-request element */
  1573. for (child = node->children; child; child = child->next) {
  1574. if (child->type != XML_ELEMENT_NODE)
  1575. continue;
  1576. if (xmlStrcmp(child->name, XCAST("hash-algorithm")) != 0)
  1577. continue;
  1578. content = (char *)xmlNodeGetContent(child);
  1579. if (content == NULL)
  1580. continue;
  1581. hash = multicert_hash_get_id(content);
  1582. /* hash was not found */
  1583. if (hash == OPENCONNECT_HASH_UNKNOWN) {
  1584. vpn_progress(vpninfo, PRG_INFO,
  1585. _("Unsupported hash algorithm '%s' requested.\n"),
  1586. (char *) content);
  1587. goto next;
  1588. }
  1589. oldhashes = cert_rq->hashes;
  1590. cert_rq->hashes |= MULTICERT_HASH_FLAG(hash);
  1591. if (oldhashes == cert_rq->hashes)
  1592. vpn_progress(vpninfo, PRG_INFO,
  1593. _("Duplicate hash algorithm '%s' requested.\n"),
  1594. (char *) content);
  1595. next:
  1596. xmlFree(content);
  1597. }
  1598. }
  1599. #define BUF_DATA(bp) ((bp)->data)
  1600. #define BUF_SIZE(bp) ((bp)->pos)
  1601. static int post_multicert_response(struct openconnect_info *vpninfo, const xmlChar *cert,
  1602. openconnect_hash_type hash, const xmlChar *signature,
  1603. struct oc_text_buf *body)
  1604. {
  1605. static const xmlChar *strnull = XCAST("(null)");
  1606. const xmlChar *hashname;
  1607. xmlDocPtr doc;
  1608. xmlNodePtr root, auth, node, chain;
  1609. doc = xmlpost_new_query(vpninfo, "auth-reply", &root);
  1610. if (!doc)
  1611. goto bad;
  1612. node = xmlNewChild(root, NULL, XCAST("session-token"), NULL);
  1613. if (!node)
  1614. goto bad;
  1615. node = xmlNewChild(root, NULL, XCAST("session-id"), NULL);
  1616. if (!node)
  1617. goto bad;
  1618. if (vpninfo->opaque_srvdata != NULL) {
  1619. node = xmlCopyNode(vpninfo->opaque_srvdata, 1);
  1620. if (!node || !xmlAddChild(root, node))
  1621. goto bad;
  1622. }
  1623. // key1 ownership is proved by TLS session
  1624. auth = xmlNewChild(root, NULL, XCAST("auth"), NULL);
  1625. if (!auth)
  1626. goto bad;
  1627. chain = xmlNewChild(auth, NULL, XCAST("client-cert-chain"), NULL);
  1628. if (!chain || !xmlNewProp(chain, XCAST("cert-store"), XCAST("1M")))
  1629. goto bad;
  1630. if (!xmlNewChild(chain, NULL, XCAST("client-cert-sent-via-protocol"),
  1631. NULL))
  1632. goto bad;
  1633. // key2 ownership is proved by signing the challenge
  1634. chain = xmlNewChild(auth, NULL, XCAST("client-cert-chain"), NULL);
  1635. if (!chain || !xmlNewProp(chain, XCAST("cert-store"), XCAST("1U")))
  1636. goto bad;
  1637. node = xmlNewTextChild(chain, NULL, XCAST("client-cert"), cert ? cert : strnull);
  1638. if (!node || !xmlNewProp(node, XCAST("cert-format"), XCAST("pkcs7")))
  1639. goto bad;
  1640. hashname = XCAST(multicert_hash_get_name(hash));
  1641. node = xmlNewTextChild(chain, NULL,
  1642. XCAST("client-cert-auth-signature"),
  1643. signature ? signature : strnull);
  1644. if (!node || !xmlNewProp(node, XCAST("hash-algorithm-chosen"), hashname ? hashname : strnull))
  1645. goto bad;
  1646. return xmlpost_complete(doc, body);
  1647. bad:
  1648. xmlpost_complete(doc, NULL);
  1649. return -ENOMEM;
  1650. }
  1651. int prepare_multicert_response(struct openconnect_info *vpninfo,
  1652. struct cert_request cert_rq, const char *challenge,
  1653. struct oc_text_buf *body)
  1654. {
  1655. struct cert_info *certinfo = &vpninfo->certinfo[1];
  1656. struct oc_text_buf *certdata = NULL, *certtext = NULL;
  1657. struct oc_text_buf *signdata = NULL, *signtext = NULL;
  1658. openconnect_hash_type hash;
  1659. int ret;
  1660. if (!cert_rq.hashes) {
  1661. vpn_progress(vpninfo, PRG_ERR,
  1662. _("Multiple-certificate authentication signature hash algorithm negotiation failed.\n"));
  1663. ret = -EIO;
  1664. goto done;
  1665. }
  1666. ret = export_certificate_pkcs7(vpninfo, certinfo, CERT_FORMAT_ASN1, &certdata);
  1667. if (ret >= 0)
  1668. ret = to_base64(&certtext,
  1669. BUF_DATA(certdata),
  1670. BUF_SIZE(certdata));
  1671. if (ret < 0) {
  1672. vpn_progress(vpninfo, PRG_ERR,
  1673. _("Error exporting multiple-certificate signer's certificate chain.\n"));
  1674. goto done;
  1675. }
  1676. ret = multicert_sign_data(vpninfo, certinfo, cert_rq.hashes,
  1677. challenge, strlen(challenge),
  1678. &signdata);
  1679. if (ret < 0)
  1680. goto done;
  1681. hash = ret;
  1682. if ((ret = to_base64(&signtext, BUF_DATA(signdata), BUF_SIZE(signdata))) < 0) {
  1683. vpn_progress(vpninfo, PRG_ERR,
  1684. _("Error encoding the challenge response.\n"));
  1685. goto done;
  1686. }
  1687. ret = post_multicert_response(vpninfo, XCAST(BUF_DATA(certtext)),
  1688. hash, XCAST(BUF_DATA(signtext)),
  1689. body);
  1690. done:
  1691. buf_free(certdata); buf_free(certtext);
  1692. buf_free(signdata); buf_free(signtext);
  1693. return ret;
  1694. }