spp_client.c 26 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004
  1. /*
  2. * Hotspot 2.0 SPP client
  3. * Copyright (c) 2012-2014, Qualcomm Atheros, Inc.
  4. *
  5. * This software may be distributed under the terms of the BSD license.
  6. * See README for more details.
  7. */
  8. #include "includes.h"
  9. #include <sys/stat.h>
  10. #include "common.h"
  11. #include "browser.h"
  12. #include "wpa_ctrl.h"
  13. #include "wpa_helpers.h"
  14. #include "xml-utils.h"
  15. #include "http-utils.h"
  16. #include "utils/base64.h"
  17. #include "crypto/crypto.h"
  18. #include "crypto/sha256.h"
  19. #include "osu_client.h"
  20. extern const char *spp_xsd_fname;
  21. static int hs20_spp_update_response(struct hs20_osu_client *ctx,
  22. const char *session_id,
  23. const char *spp_status,
  24. const char *error_code);
  25. static void hs20_policy_update_complete(
  26. struct hs20_osu_client *ctx, const char *pps_fname);
  27. static char * get_spp_attr_value(struct xml_node_ctx *ctx, xml_node_t *node,
  28. char *attr_name)
  29. {
  30. return xml_node_get_attr_value_ns(ctx, node, SPP_NS_URI, attr_name);
  31. }
  32. static int hs20_spp_validate(struct hs20_osu_client *ctx, xml_node_t *node,
  33. const char *expected_name)
  34. {
  35. struct xml_node_ctx *xctx = ctx->xml;
  36. const char *name;
  37. char *err;
  38. int ret;
  39. if (!xml_node_is_element(xctx, node))
  40. return -1;
  41. name = xml_node_get_localname(xctx, node);
  42. if (name == NULL)
  43. return -1;
  44. if (strcmp(expected_name, name) != 0) {
  45. wpa_printf(MSG_INFO, "Unexpected SOAP method name '%s' (expected '%s')",
  46. name, expected_name);
  47. write_summary(ctx, "Unexpected SOAP method name '%s' (expected '%s')",
  48. name, expected_name);
  49. return -1;
  50. }
  51. ret = xml_validate(xctx, node, spp_xsd_fname, &err);
  52. if (ret < 0) {
  53. wpa_printf(MSG_INFO, "XML schema validation error(s)\n%s", err);
  54. write_summary(ctx, "SPP XML schema validation failed");
  55. os_free(err);
  56. }
  57. return ret;
  58. }
  59. static void add_mo_container(struct xml_node_ctx *ctx, xml_namespace_t *ns,
  60. xml_node_t *parent, const char *urn,
  61. const char *fname)
  62. {
  63. xml_node_t *node;
  64. xml_node_t *fnode, *tnds;
  65. char *str;
  66. errno = 0;
  67. fnode = node_from_file(ctx, fname);
  68. if (!fnode) {
  69. wpa_printf(MSG_ERROR,
  70. "Failed to create XML node from file: %s, possible error: %s",
  71. fname, strerror(errno));
  72. return;
  73. }
  74. tnds = mo_to_tnds(ctx, fnode, 0, urn, "syncml:dmddf1.2");
  75. xml_node_free(ctx, fnode);
  76. if (!tnds)
  77. return;
  78. str = xml_node_to_str(ctx, tnds);
  79. xml_node_free(ctx, tnds);
  80. if (str == NULL)
  81. return;
  82. node = xml_node_create_text(ctx, parent, ns, "moContainer", str);
  83. if (node)
  84. xml_node_add_attr(ctx, node, ns, "moURN", urn);
  85. os_free(str);
  86. }
  87. static xml_node_t * build_spp_post_dev_data(struct hs20_osu_client *ctx,
  88. xml_namespace_t **ret_ns,
  89. const char *session_id,
  90. const char *reason)
  91. {
  92. xml_namespace_t *ns;
  93. xml_node_t *spp_node;
  94. write_summary(ctx, "Building sppPostDevData requestReason='%s'",
  95. reason);
  96. spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
  97. "sppPostDevData");
  98. if (spp_node == NULL)
  99. return NULL;
  100. if (ret_ns)
  101. *ret_ns = ns;
  102. xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
  103. xml_node_add_attr(ctx->xml, spp_node, NULL, "requestReason", reason);
  104. if (session_id)
  105. xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID",
  106. session_id);
  107. xml_node_add_attr(ctx->xml, spp_node, NULL, "redirectURI",
  108. "http://localhost:12345/");
  109. xml_node_create_text(ctx->xml, spp_node, ns, "supportedSPPVersions",
  110. "1.0");
  111. xml_node_create_text(ctx->xml, spp_node, ns, "supportedMOList",
  112. URN_HS20_PPS " " URN_OMA_DM_DEVINFO " "
  113. URN_OMA_DM_DEVDETAIL " " URN_HS20_DEVDETAIL_EXT);
  114. add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVINFO,
  115. "devinfo.xml");
  116. add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVDETAIL,
  117. "devdetail.xml");
  118. return spp_node;
  119. }
  120. static int process_update_node(struct hs20_osu_client *ctx, xml_node_t *pps,
  121. xml_node_t *update)
  122. {
  123. xml_node_t *node, *parent, *tnds, *unode;
  124. char *str;
  125. const char *name;
  126. char *uri, *pos;
  127. char *cdata, *cdata_end;
  128. size_t fqdn_len;
  129. wpa_printf(MSG_INFO, "Processing updateNode");
  130. debug_dump_node(ctx, "updateNode", update);
  131. uri = get_spp_attr_value(ctx->xml, update, "managementTreeURI");
  132. if (uri == NULL) {
  133. wpa_printf(MSG_INFO, "No managementTreeURI present");
  134. return -1;
  135. }
  136. wpa_printf(MSG_INFO, "managementTreeUri: '%s'", uri);
  137. name = os_strrchr(uri, '/');
  138. if (name == NULL) {
  139. wpa_printf(MSG_INFO, "Unexpected URI");
  140. xml_node_get_attr_value_free(ctx->xml, uri);
  141. return -1;
  142. }
  143. name++;
  144. wpa_printf(MSG_INFO, "Update interior node: '%s'", name);
  145. str = xml_node_get_text(ctx->xml, update);
  146. if (str == NULL) {
  147. wpa_printf(MSG_INFO, "Could not extract MO text");
  148. xml_node_get_attr_value_free(ctx->xml, uri);
  149. return -1;
  150. }
  151. wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text: '%s'", str);
  152. cdata = strstr(str, "<![CDATA[");
  153. cdata_end = strstr(str, "]]>");
  154. if (cdata && cdata_end && cdata_end > cdata &&
  155. cdata < strstr(str, "MgmtTree") &&
  156. cdata_end > strstr(str, "/MgmtTree")) {
  157. char *tmp;
  158. wpa_printf(MSG_DEBUG, "[hs20] Removing extra CDATA container");
  159. tmp = strdup(cdata + 9);
  160. if (tmp) {
  161. cdata_end = strstr(tmp, "]]>");
  162. if (cdata_end)
  163. *cdata_end = '\0';
  164. wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text with CDATA container removed: '%s'",
  165. tmp);
  166. tnds = xml_node_from_buf(ctx->xml, tmp);
  167. free(tmp);
  168. } else
  169. tnds = NULL;
  170. } else
  171. tnds = xml_node_from_buf(ctx->xml, str);
  172. xml_node_get_text_free(ctx->xml, str);
  173. if (tnds == NULL) {
  174. wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer text");
  175. xml_node_get_attr_value_free(ctx->xml, uri);
  176. return -1;
  177. }
  178. unode = tnds_to_mo(ctx->xml, tnds);
  179. xml_node_free(ctx->xml, tnds);
  180. if (unode == NULL) {
  181. wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer TNDS text");
  182. xml_node_get_attr_value_free(ctx->xml, uri);
  183. return -1;
  184. }
  185. debug_dump_node(ctx, "Parsed TNDS", unode);
  186. if (get_node_uri(ctx->xml, unode, name) == NULL) {
  187. wpa_printf(MSG_INFO, "[hs20] %s node not found", name);
  188. xml_node_free(ctx->xml, unode);
  189. xml_node_get_attr_value_free(ctx->xml, uri);
  190. return -1;
  191. }
  192. if (os_strncasecmp(uri, "./Wi-Fi/", 8) != 0) {
  193. wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi");
  194. xml_node_free(ctx->xml, unode);
  195. xml_node_get_attr_value_free(ctx->xml, uri);
  196. return -1;
  197. }
  198. pos = uri + 8;
  199. if (ctx->fqdn == NULL) {
  200. wpa_printf(MSG_INFO, "FQDN not known");
  201. xml_node_free(ctx->xml, unode);
  202. xml_node_get_attr_value_free(ctx->xml, uri);
  203. return -1;
  204. }
  205. fqdn_len = os_strlen(ctx->fqdn);
  206. if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
  207. pos[fqdn_len] != '/') {
  208. wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s",
  209. ctx->fqdn);
  210. xml_node_free(ctx->xml, unode);
  211. xml_node_get_attr_value_free(ctx->xml, uri);
  212. return -1;
  213. }
  214. pos += fqdn_len + 1;
  215. if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
  216. wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s/PerProviderSubscription",
  217. ctx->fqdn);
  218. xml_node_free(ctx->xml, unode);
  219. xml_node_get_attr_value_free(ctx->xml, uri);
  220. return -1;
  221. }
  222. pos += 24;
  223. wpa_printf(MSG_INFO, "Update command for PPS node %s", pos);
  224. node = get_node(ctx->xml, pps, pos);
  225. if (node) {
  226. parent = xml_node_get_parent(ctx->xml, node);
  227. xml_node_detach(ctx->xml, node);
  228. wpa_printf(MSG_INFO, "Replace '%s' node", name);
  229. } else {
  230. char *pos2;
  231. pos2 = os_strrchr(pos, '/');
  232. if (pos2 == NULL) {
  233. parent = pps;
  234. } else {
  235. *pos2 = '\0';
  236. parent = get_node(ctx->xml, pps, pos);
  237. }
  238. if (parent == NULL) {
  239. wpa_printf(MSG_INFO, "Could not find parent %s", pos);
  240. xml_node_free(ctx->xml, unode);
  241. xml_node_get_attr_value_free(ctx->xml, uri);
  242. return -1;
  243. }
  244. wpa_printf(MSG_INFO, "Add '%s' node", name);
  245. }
  246. xml_node_add_child(ctx->xml, parent, unode);
  247. xml_node_get_attr_value_free(ctx->xml, uri);
  248. return 0;
  249. }
  250. static int update_pps(struct hs20_osu_client *ctx, xml_node_t *update,
  251. const char *pps_fname, xml_node_t *pps)
  252. {
  253. wpa_printf(MSG_INFO, "Updating PPS based on updateNode element(s)");
  254. xml_node_for_each_sibling(ctx->xml, update) {
  255. xml_node_for_each_check(ctx->xml, update);
  256. if (process_update_node(ctx, pps, update) < 0)
  257. return -1;
  258. }
  259. return update_pps_file(ctx, pps_fname, pps);
  260. }
  261. static void hs20_sub_rem_complete(struct hs20_osu_client *ctx,
  262. const char *pps_fname)
  263. {
  264. /*
  265. * Update wpa_supplicant credentials and reconnect using updated
  266. * information.
  267. */
  268. wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
  269. cmd_set_pps(ctx, pps_fname);
  270. if (ctx->no_reconnect)
  271. return;
  272. wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
  273. if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
  274. wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect");
  275. }
  276. static xml_node_t * hs20_spp_upload_mo(struct hs20_osu_client *ctx,
  277. xml_node_t *cmd,
  278. const char *session_id,
  279. const char *pps_fname)
  280. {
  281. xml_namespace_t *ns;
  282. xml_node_t *node, *ret_node;
  283. char *urn;
  284. urn = get_spp_attr_value(ctx->xml, cmd, "moURN");
  285. if (!urn) {
  286. wpa_printf(MSG_INFO, "No URN included");
  287. return NULL;
  288. }
  289. wpa_printf(MSG_INFO, "Upload MO request - URN=%s", urn);
  290. if (strcasecmp(urn, URN_HS20_PPS) != 0) {
  291. wpa_printf(MSG_INFO, "Unsupported moURN");
  292. xml_node_get_attr_value_free(ctx->xml, urn);
  293. return NULL;
  294. }
  295. xml_node_get_attr_value_free(ctx->xml, urn);
  296. if (!pps_fname) {
  297. wpa_printf(MSG_INFO, "PPS file name no known");
  298. return NULL;
  299. }
  300. node = build_spp_post_dev_data(ctx, &ns, session_id,
  301. "MO upload");
  302. if (node == NULL)
  303. return NULL;
  304. add_mo_container(ctx->xml, ns, node, URN_HS20_PPS, pps_fname);
  305. ret_node = soap_send_receive(ctx->http, node);
  306. if (ret_node == NULL)
  307. return NULL;
  308. debug_dump_node(ctx, "Received response to MO upload", ret_node);
  309. if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
  310. wpa_printf(MSG_INFO, "SPP validation failed");
  311. xml_node_free(ctx->xml, ret_node);
  312. return NULL;
  313. }
  314. return ret_node;
  315. }
  316. static int hs20_add_mo(struct hs20_osu_client *ctx, xml_node_t *add_mo,
  317. char *fname, size_t fname_len)
  318. {
  319. char *uri, *urn;
  320. int ret;
  321. debug_dump_node(ctx, "Received addMO", add_mo);
  322. urn = get_spp_attr_value(ctx->xml, add_mo, "moURN");
  323. if (urn == NULL) {
  324. wpa_printf(MSG_INFO, "[hs20] No moURN in addMO");
  325. return -1;
  326. }
  327. wpa_printf(MSG_INFO, "addMO - moURN: '%s'", urn);
  328. if (strcasecmp(urn, URN_HS20_PPS) != 0) {
  329. wpa_printf(MSG_INFO, "[hs20] Unsupported MO in addMO");
  330. xml_node_get_attr_value_free(ctx->xml, urn);
  331. return -1;
  332. }
  333. xml_node_get_attr_value_free(ctx->xml, urn);
  334. uri = get_spp_attr_value(ctx->xml, add_mo, "managementTreeURI");
  335. if (uri == NULL) {
  336. wpa_printf(MSG_INFO, "[hs20] No managementTreeURI in addMO");
  337. return -1;
  338. }
  339. wpa_printf(MSG_INFO, "addMO - managementTreeURI: '%s'", uri);
  340. ret = hs20_add_pps_mo(ctx, uri, add_mo, fname, fname_len);
  341. xml_node_get_attr_value_free(ctx->xml, uri);
  342. return ret;
  343. }
  344. static int process_spp_user_input_response(struct hs20_osu_client *ctx,
  345. const char *session_id,
  346. xml_node_t *add_mo)
  347. {
  348. int ret;
  349. char fname[300];
  350. debug_dump_node(ctx, "addMO", add_mo);
  351. wpa_printf(MSG_INFO, "Subscription registration completed");
  352. if (hs20_add_mo(ctx, add_mo, fname, sizeof(fname)) < 0) {
  353. wpa_printf(MSG_INFO, "Could not add MO");
  354. ret = hs20_spp_update_response(
  355. ctx, session_id,
  356. "Error occurred",
  357. "MO addition or update failed");
  358. return 0;
  359. }
  360. ret = hs20_spp_update_response(ctx, session_id, "OK", NULL);
  361. if (ret == 0)
  362. hs20_sub_rem_complete(ctx, fname);
  363. return 0;
  364. }
  365. static xml_node_t * hs20_spp_user_input_completed(struct hs20_osu_client *ctx,
  366. const char *session_id)
  367. {
  368. xml_node_t *node, *ret_node;
  369. node = build_spp_post_dev_data(ctx, NULL, session_id,
  370. "User input completed");
  371. if (node == NULL)
  372. return NULL;
  373. ret_node = soap_send_receive(ctx->http, node);
  374. if (!ret_node) {
  375. if (soap_reinit_client(ctx->http) < 0)
  376. return NULL;
  377. wpa_printf(MSG_INFO, "Try to finish with re-opened connection");
  378. node = build_spp_post_dev_data(ctx, NULL, session_id,
  379. "User input completed");
  380. if (node == NULL)
  381. return NULL;
  382. ret_node = soap_send_receive(ctx->http, node);
  383. if (ret_node == NULL)
  384. return NULL;
  385. wpa_printf(MSG_INFO, "Continue with new connection");
  386. }
  387. if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
  388. wpa_printf(MSG_INFO, "SPP validation failed");
  389. xml_node_free(ctx->xml, ret_node);
  390. return NULL;
  391. }
  392. return ret_node;
  393. }
  394. static xml_node_t * hs20_spp_get_certificate(struct hs20_osu_client *ctx,
  395. xml_node_t *cmd,
  396. const char *session_id,
  397. const char *pps_fname)
  398. {
  399. xml_namespace_t *ns;
  400. xml_node_t *node, *ret_node;
  401. int res;
  402. wpa_printf(MSG_INFO, "Client certificate enrollment");
  403. res = osu_get_certificate(ctx, cmd);
  404. if (res < 0)
  405. wpa_printf(MSG_INFO, "EST simpleEnroll failed");
  406. node = build_spp_post_dev_data(ctx, &ns, session_id,
  407. res == 0 ?
  408. "Certificate enrollment completed" :
  409. "Certificate enrollment failed");
  410. if (node == NULL)
  411. return NULL;
  412. ret_node = soap_send_receive(ctx->http, node);
  413. if (ret_node == NULL)
  414. return NULL;
  415. debug_dump_node(ctx, "Received response to certificate enrollment "
  416. "completed", ret_node);
  417. if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
  418. wpa_printf(MSG_INFO, "SPP validation failed");
  419. xml_node_free(ctx->xml, ret_node);
  420. return NULL;
  421. }
  422. return ret_node;
  423. }
  424. static int hs20_spp_exec(struct hs20_osu_client *ctx, xml_node_t *exec,
  425. const char *session_id, const char *pps_fname,
  426. xml_node_t *pps, xml_node_t **ret_node)
  427. {
  428. xml_node_t *cmd;
  429. const char *name;
  430. char *uri;
  431. char *id = strdup(session_id);
  432. if (id == NULL)
  433. return -1;
  434. *ret_node = NULL;
  435. debug_dump_node(ctx, "exec", exec);
  436. xml_node_for_each_child(ctx->xml, cmd, exec) {
  437. xml_node_for_each_check(ctx->xml, cmd);
  438. break;
  439. }
  440. if (!cmd) {
  441. wpa_printf(MSG_INFO, "exec command element not found (cmd=%p)",
  442. cmd);
  443. free(id);
  444. return -1;
  445. }
  446. name = xml_node_get_localname(ctx->xml, cmd);
  447. if (strcasecmp(name, "launchBrowserToURI") == 0) {
  448. int res;
  449. uri = xml_node_get_text(ctx->xml, cmd);
  450. if (!uri) {
  451. wpa_printf(MSG_INFO, "No URI found");
  452. free(id);
  453. return -1;
  454. }
  455. wpa_printf(MSG_INFO, "Launch browser to URI '%s'", uri);
  456. write_summary(ctx, "Launch browser to URI '%s'", uri);
  457. res = hs20_web_browser(uri);
  458. xml_node_get_text_free(ctx->xml, uri);
  459. if (res > 0) {
  460. wpa_printf(MSG_INFO, "User response in browser completed successfully - sessionid='%s'",
  461. id);
  462. write_summary(ctx, "User response in browser completed successfully");
  463. *ret_node = hs20_spp_user_input_completed(ctx, id);
  464. free(id);
  465. return *ret_node ? 0 : -1;
  466. } else {
  467. wpa_printf(MSG_INFO, "Failed to receive user response");
  468. write_summary(ctx, "Failed to receive user response");
  469. hs20_spp_update_response(
  470. ctx, id, "Error occurred", "Other");
  471. free(id);
  472. return -1;
  473. }
  474. return 0;
  475. }
  476. if (strcasecmp(name, "uploadMO") == 0) {
  477. if (pps_fname == NULL)
  478. return -1;
  479. *ret_node = hs20_spp_upload_mo(ctx, cmd, id,
  480. pps_fname);
  481. free(id);
  482. return *ret_node ? 0 : -1;
  483. }
  484. if (strcasecmp(name, "getCertificate") == 0) {
  485. *ret_node = hs20_spp_get_certificate(ctx, cmd, id,
  486. pps_fname);
  487. free(id);
  488. return *ret_node ? 0 : -1;
  489. }
  490. wpa_printf(MSG_INFO, "Unsupported exec command: '%s'", name);
  491. free(id);
  492. return -1;
  493. }
  494. enum spp_post_dev_data_use {
  495. SPP_SUBSCRIPTION_REMEDIATION,
  496. SPP_POLICY_UPDATE,
  497. SPP_SUBSCRIPTION_REGISTRATION,
  498. };
  499. static void process_spp_post_dev_data_response(
  500. struct hs20_osu_client *ctx,
  501. enum spp_post_dev_data_use use, xml_node_t *node,
  502. const char *pps_fname, xml_node_t *pps)
  503. {
  504. xml_node_t *child;
  505. char *status = NULL;
  506. xml_node_t *update = NULL, *exec = NULL, *add_mo = NULL, *no_mo = NULL;
  507. char *session_id = NULL;
  508. debug_dump_node(ctx, "sppPostDevDataResponse node", node);
  509. status = get_spp_attr_value(ctx->xml, node, "sppStatus");
  510. if (status == NULL) {
  511. wpa_printf(MSG_INFO, "No sppStatus attribute");
  512. goto out;
  513. }
  514. write_summary(ctx, "Received sppPostDevDataResponse sppStatus='%s'",
  515. status);
  516. session_id = get_spp_attr_value(ctx->xml, node, "sessionID");
  517. if (session_id == NULL) {
  518. wpa_printf(MSG_INFO, "No sessionID attribute");
  519. goto out;
  520. }
  521. wpa_printf(MSG_INFO, "[hs20] sppPostDevDataResponse - sppStatus: '%s' sessionID: '%s'",
  522. status, session_id);
  523. xml_node_for_each_child(ctx->xml, child, node) {
  524. const char *name;
  525. xml_node_for_each_check(ctx->xml, child);
  526. debug_dump_node(ctx, "child", child);
  527. name = xml_node_get_localname(ctx->xml, child);
  528. wpa_printf(MSG_INFO, "localname: '%s'", name);
  529. if (!update && strcasecmp(name, "updateNode") == 0)
  530. update = child;
  531. if (!exec && strcasecmp(name, "exec") == 0)
  532. exec = child;
  533. if (!add_mo && strcasecmp(name, "addMO") == 0)
  534. add_mo = child;
  535. if (!no_mo && strcasecmp(name, "noMOUpdate") == 0)
  536. no_mo = child;
  537. }
  538. if (use == SPP_SUBSCRIPTION_REMEDIATION &&
  539. strcasecmp(status,
  540. "Remediation complete, request sppUpdateResponse") == 0)
  541. {
  542. int res, ret;
  543. if (!update && !no_mo) {
  544. wpa_printf(MSG_INFO, "No updateNode or noMOUpdate element");
  545. goto out;
  546. }
  547. wpa_printf(MSG_INFO, "Subscription remediation completed");
  548. res = update_pps(ctx, update, pps_fname, pps);
  549. if (res < 0)
  550. wpa_printf(MSG_INFO, "Failed to update PPS MO");
  551. ret = hs20_spp_update_response(
  552. ctx, session_id,
  553. res < 0 ? "Error occurred" : "OK",
  554. res < 0 ? "MO addition or update failed" : NULL);
  555. if (res == 0 && ret == 0)
  556. hs20_sub_rem_complete(ctx, pps_fname);
  557. goto out;
  558. }
  559. if (use == SPP_SUBSCRIPTION_REMEDIATION &&
  560. strcasecmp(status, "Exchange complete, release TLS connection") ==
  561. 0) {
  562. if (!no_mo) {
  563. wpa_printf(MSG_INFO, "No noMOUpdate element");
  564. goto out;
  565. }
  566. wpa_printf(MSG_INFO, "Subscription remediation completed (no MO update)");
  567. goto out;
  568. }
  569. if (use == SPP_POLICY_UPDATE &&
  570. strcasecmp(status, "Update complete, request sppUpdateResponse") ==
  571. 0) {
  572. int res, ret;
  573. wpa_printf(MSG_INFO, "Policy update received - update PPS");
  574. res = update_pps(ctx, update, pps_fname, pps);
  575. ret = hs20_spp_update_response(
  576. ctx, session_id,
  577. res < 0 ? "Error occurred" : "OK",
  578. res < 0 ? "MO addition or update failed" : NULL);
  579. if (res == 0 && ret == 0)
  580. hs20_policy_update_complete(ctx, pps_fname);
  581. goto out;
  582. }
  583. if (use == SPP_SUBSCRIPTION_REGISTRATION &&
  584. strcasecmp(status, "Provisioning complete, request "
  585. "sppUpdateResponse") == 0) {
  586. if (!add_mo) {
  587. wpa_printf(MSG_INFO, "No addMO element - not sure what to do next");
  588. goto out;
  589. }
  590. process_spp_user_input_response(ctx, session_id, add_mo);
  591. node = NULL;
  592. goto out;
  593. }
  594. if (strcasecmp(status, "No update available at this time") == 0) {
  595. wpa_printf(MSG_INFO, "No update available at this time");
  596. goto out;
  597. }
  598. if (strcasecmp(status, "OK") == 0) {
  599. int res;
  600. xml_node_t *ret;
  601. if (!exec) {
  602. wpa_printf(MSG_INFO, "No exec element - not sure what to do next");
  603. goto out;
  604. }
  605. res = hs20_spp_exec(ctx, exec, session_id,
  606. pps_fname, pps, &ret);
  607. /* xml_node_free(ctx->xml, node); */
  608. node = NULL;
  609. if (res == 0 && ret)
  610. process_spp_post_dev_data_response(ctx, use,
  611. ret, pps_fname, pps);
  612. goto out;
  613. }
  614. if (strcasecmp(status, "Error occurred") == 0) {
  615. xml_node_t *err;
  616. char *code = NULL;
  617. err = get_node(ctx->xml, node, "sppError");
  618. if (err)
  619. code = xml_node_get_attr_value(ctx->xml, err,
  620. "errorCode");
  621. wpa_printf(MSG_INFO, "Error occurred - errorCode=%s",
  622. code ? code : "N/A");
  623. xml_node_get_attr_value_free(ctx->xml, code);
  624. goto out;
  625. }
  626. wpa_printf(MSG_INFO,
  627. "[hs20] Unsupported sppPostDevDataResponse sppStatus '%s'",
  628. status);
  629. out:
  630. xml_node_get_attr_value_free(ctx->xml, status);
  631. xml_node_get_attr_value_free(ctx->xml, session_id);
  632. xml_node_free(ctx->xml, node);
  633. }
  634. static int spp_post_dev_data(struct hs20_osu_client *ctx,
  635. enum spp_post_dev_data_use use,
  636. const char *reason,
  637. const char *pps_fname, xml_node_t *pps)
  638. {
  639. xml_node_t *payload;
  640. xml_node_t *ret_node;
  641. payload = build_spp_post_dev_data(ctx, NULL, NULL, reason);
  642. if (payload == NULL)
  643. return -1;
  644. ret_node = soap_send_receive(ctx->http, payload);
  645. if (!ret_node) {
  646. const char *err = http_get_err(ctx->http);
  647. if (err) {
  648. wpa_printf(MSG_INFO, "HTTP error: %s", err);
  649. write_result(ctx, "HTTP error: %s", err);
  650. } else {
  651. write_summary(ctx, "Failed to send SOAP message");
  652. }
  653. return -1;
  654. }
  655. if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
  656. wpa_printf(MSG_INFO, "SPP validation failed");
  657. xml_node_free(ctx->xml, ret_node);
  658. return -1;
  659. }
  660. process_spp_post_dev_data_response(ctx, use, ret_node,
  661. pps_fname, pps);
  662. return 0;
  663. }
  664. void spp_sub_rem(struct hs20_osu_client *ctx, const char *address,
  665. const char *pps_fname,
  666. const char *client_cert, const char *client_key,
  667. const char *cred_username, const char *cred_password,
  668. xml_node_t *pps)
  669. {
  670. wpa_printf(MSG_INFO, "SPP subscription remediation");
  671. write_summary(ctx, "SPP subscription remediation");
  672. os_free(ctx->server_url);
  673. ctx->server_url = os_strdup(address);
  674. if (soap_init_client(ctx->http, address, ctx->ca_fname,
  675. cred_username, cred_password, client_cert,
  676. client_key) == 0) {
  677. spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REMEDIATION,
  678. "Subscription remediation", pps_fname, pps);
  679. }
  680. }
  681. static void hs20_policy_update_complete(struct hs20_osu_client *ctx,
  682. const char *pps_fname)
  683. {
  684. wpa_printf(MSG_INFO, "Policy update completed");
  685. /*
  686. * Update wpa_supplicant credentials and reconnect using updated
  687. * information.
  688. */
  689. wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
  690. cmd_set_pps(ctx, pps_fname);
  691. wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
  692. if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
  693. wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect");
  694. }
  695. static int process_spp_exchange_complete(struct hs20_osu_client *ctx,
  696. xml_node_t *node)
  697. {
  698. char *status, *session_id;
  699. debug_dump_node(ctx, "sppExchangeComplete", node);
  700. status = get_spp_attr_value(ctx->xml, node, "sppStatus");
  701. if (status == NULL) {
  702. wpa_printf(MSG_INFO, "No sppStatus attribute");
  703. return -1;
  704. }
  705. write_summary(ctx, "Received sppExchangeComplete sppStatus='%s'",
  706. status);
  707. session_id = get_spp_attr_value(ctx->xml, node, "sessionID");
  708. if (session_id == NULL) {
  709. wpa_printf(MSG_INFO, "No sessionID attribute");
  710. xml_node_get_attr_value_free(ctx->xml, status);
  711. return -1;
  712. }
  713. wpa_printf(MSG_INFO, "[hs20] sppStatus: '%s' sessionID: '%s'",
  714. status, session_id);
  715. xml_node_get_attr_value_free(ctx->xml, session_id);
  716. if (strcasecmp(status, "Exchange complete, release TLS connection") ==
  717. 0) {
  718. xml_node_get_attr_value_free(ctx->xml, status);
  719. return 0;
  720. }
  721. wpa_printf(MSG_INFO, "Unexpected sppStatus '%s'", status);
  722. write_summary(ctx, "Unexpected sppStatus '%s'", status);
  723. xml_node_get_attr_value_free(ctx->xml, status);
  724. return -1;
  725. }
  726. static xml_node_t * build_spp_update_response(struct hs20_osu_client *ctx,
  727. const char *session_id,
  728. const char *spp_status,
  729. const char *error_code)
  730. {
  731. xml_namespace_t *ns;
  732. xml_node_t *spp_node, *node;
  733. spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
  734. "sppUpdateResponse");
  735. if (spp_node == NULL)
  736. return NULL;
  737. xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
  738. xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id);
  739. xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", spp_status);
  740. if (error_code) {
  741. node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
  742. if (node)
  743. xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
  744. error_code);
  745. }
  746. return spp_node;
  747. }
  748. static int hs20_spp_update_response(struct hs20_osu_client *ctx,
  749. const char *session_id,
  750. const char *spp_status,
  751. const char *error_code)
  752. {
  753. xml_node_t *node, *ret_node;
  754. int ret;
  755. write_summary(ctx, "Building sppUpdateResponse sppStatus='%s' error_code='%s'",
  756. spp_status, error_code);
  757. node = build_spp_update_response(ctx, session_id, spp_status,
  758. error_code);
  759. if (node == NULL)
  760. return -1;
  761. ret_node = soap_send_receive(ctx->http, node);
  762. if (!ret_node) {
  763. if (soap_reinit_client(ctx->http) < 0)
  764. return -1;
  765. wpa_printf(MSG_INFO, "Try to finish with re-opened connection");
  766. node = build_spp_update_response(ctx, session_id, spp_status,
  767. error_code);
  768. if (node == NULL)
  769. return -1;
  770. ret_node = soap_send_receive(ctx->http, node);
  771. if (ret_node == NULL)
  772. return -1;
  773. wpa_printf(MSG_INFO, "Continue with new connection");
  774. }
  775. if (hs20_spp_validate(ctx, ret_node, "sppExchangeComplete") < 0) {
  776. wpa_printf(MSG_INFO, "SPP validation failed");
  777. xml_node_free(ctx->xml, ret_node);
  778. return -1;
  779. }
  780. ret = process_spp_exchange_complete(ctx, ret_node);
  781. xml_node_free(ctx->xml, ret_node);
  782. return ret;
  783. }
  784. void spp_pol_upd(struct hs20_osu_client *ctx, const char *address,
  785. const char *pps_fname,
  786. const char *client_cert, const char *client_key,
  787. const char *cred_username, const char *cred_password,
  788. xml_node_t *pps)
  789. {
  790. wpa_printf(MSG_INFO, "SPP policy update");
  791. write_summary(ctx, "SPP policy update");
  792. os_free(ctx->server_url);
  793. ctx->server_url = os_strdup(address);
  794. if (soap_init_client(ctx->http, address, ctx->ca_fname, cred_username,
  795. cred_password, client_cert, client_key) == 0) {
  796. spp_post_dev_data(ctx, SPP_POLICY_UPDATE, "Policy update",
  797. pps_fname, pps);
  798. }
  799. }
  800. int cmd_prov(struct hs20_osu_client *ctx, const char *url)
  801. {
  802. unlink("Cert/est_cert.der");
  803. unlink("Cert/est_cert.pem");
  804. if (url == NULL) {
  805. wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
  806. return -1;
  807. }
  808. wpa_printf(MSG_INFO,
  809. "Credential provisioning requested - URL: %s ca_fname: %s",
  810. url, ctx->ca_fname ? ctx->ca_fname : "N/A");
  811. os_free(ctx->server_url);
  812. ctx->server_url = os_strdup(url);
  813. if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
  814. NULL) < 0)
  815. return -1;
  816. spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
  817. "Subscription registration", NULL, NULL);
  818. return ctx->pps_cred_set ? 0 : -1;
  819. }
  820. int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url)
  821. {
  822. if (url == NULL) {
  823. wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
  824. return -1;
  825. }
  826. wpa_printf(MSG_INFO, "SIM provisioning requested");
  827. os_free(ctx->server_url);
  828. ctx->server_url = os_strdup(url);
  829. wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning");
  830. if (wait_ip_addr(ctx->ifname, 15) < 0) {
  831. wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
  832. }
  833. if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
  834. NULL) < 0)
  835. return -1;
  836. spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
  837. "Subscription provisioning", NULL, NULL);
  838. return ctx->pps_cred_set ? 0 : -1;
  839. }