spp_client.c 26 KB

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