blob: e5af4c255c6904a1d37634ebd420a101f62dec19 [file] [log] [blame]
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001/*
2 * Hotspot 2.0 SPP server
3 * Copyright (c) 2012-2013, 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
9#include <stdlib.h>
10#include <stdio.h>
11#include <string.h>
12#include <ctype.h>
13#include <time.h>
14#include <errno.h>
15#include <sqlite3.h>
16
17#include "common.h"
18#include "base64.h"
19#include "md5_i.h"
20#include "xml-utils.h"
21#include "spp_server.h"
22
23
24#define SPP_NS_URI "http://www.wi-fi.org/specifications/hotspot2dot0/v1.0/spp"
25
26#define URN_OMA_DM_DEVINFO "urn:oma:mo:oma-dm-devinfo:1.0"
27#define URN_OMA_DM_DEVDETAIL "urn:oma:mo:oma-dm-devdetail:1.0"
28#define URN_OMA_DM_DMACC "urn:oma:mo:oma-dm-dmacc:1.0"
29#define URN_HS20_PPS "urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0"
30
31
32/* TODO: timeout to expire sessions */
33
34enum hs20_session_operation {
35 NO_OPERATION,
36 UPDATE_PASSWORD,
37 CONTINUE_SUBSCRIPTION_REMEDIATION,
38 CONTINUE_POLICY_UPDATE,
39 USER_REMEDIATION,
40 SUBSCRIPTION_REGISTRATION,
41 POLICY_REMEDIATION,
42 POLICY_UPDATE,
43 FREE_REMEDIATION,
44};
45
46
47static char * db_get_session_val(struct hs20_svc *ctx, const char *user,
48 const char *realm, const char *session_id,
49 const char *field);
50static char * db_get_osu_config_val(struct hs20_svc *ctx, const char *realm,
51 const char *field);
52static xml_node_t * build_policy(struct hs20_svc *ctx, const char *user,
53 const char *realm, int use_dmacc);
54
55
56static int db_add_session(struct hs20_svc *ctx,
57 const char *user, const char *realm,
58 const char *sessionid, const char *pw,
59 const char *redirect_uri,
Hai Shalom39ba6fc2019-01-22 12:40:38 -080060 enum hs20_session_operation operation,
61 const u8 *mac_addr)
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -070062{
63 char *sql;
64 int ret = 0;
Hai Shalom39ba6fc2019-01-22 12:40:38 -080065 char addr[20];
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -070066
Hai Shalom39ba6fc2019-01-22 12:40:38 -080067 if (mac_addr)
68 snprintf(addr, sizeof(addr), MACSTR, MAC2STR(mac_addr));
69 else
70 addr[0] = '\0';
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -070071 sql = sqlite3_mprintf("INSERT INTO sessions(timestamp,id,user,realm,"
Hai Shalom39ba6fc2019-01-22 12:40:38 -080072 "operation,password,redirect_uri,mac_addr,test) "
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -070073 "VALUES "
74 "(strftime('%%Y-%%m-%%d %%H:%%M:%%f','now'),"
Hai Shalom39ba6fc2019-01-22 12:40:38 -080075 "%Q,%Q,%Q,%d,%Q,%Q,%Q,%Q)",
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -070076 sessionid, user ? user : "", realm ? realm : "",
77 operation, pw ? pw : "",
Hai Shalom39ba6fc2019-01-22 12:40:38 -080078 redirect_uri ? redirect_uri : "",
79 addr, ctx->test);
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -070080 if (sql == NULL)
81 return -1;
82 debug_print(ctx, 1, "DB: %s", sql);
83 if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
84 debug_print(ctx, 1, "Failed to add session entry into sqlite "
85 "database: %s", sqlite3_errmsg(ctx->db));
86 ret = -1;
87 }
88 sqlite3_free(sql);
89 return ret;
90}
91
92
93static void db_update_session_password(struct hs20_svc *ctx, const char *user,
94 const char *realm, const char *sessionid,
95 const char *pw)
96{
97 char *sql;
98
99 sql = sqlite3_mprintf("UPDATE sessions SET password=%Q WHERE id=%Q AND "
100 "user=%Q AND realm=%Q",
101 pw, sessionid, user, realm);
102 if (sql == NULL)
103 return;
104 debug_print(ctx, 1, "DB: %s", sql);
105 if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
106 debug_print(ctx, 1, "Failed to update session password: %s",
107 sqlite3_errmsg(ctx->db));
108 }
109 sqlite3_free(sql);
110}
111
112
Dmitry Shmidt216983b2015-02-06 10:50:36 -0800113static void db_update_session_machine_managed(struct hs20_svc *ctx,
114 const char *user,
115 const char *realm,
116 const char *sessionid,
117 const int pw_mm)
118{
119 char *sql;
120
121 sql = sqlite3_mprintf("UPDATE sessions SET machine_managed=%Q WHERE id=%Q AND user=%Q AND realm=%Q",
122 pw_mm ? "1" : "0", sessionid, user, realm);
123 if (sql == NULL)
124 return;
125 debug_print(ctx, 1, "DB: %s", sql);
126 if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
127 debug_print(ctx, 1,
128 "Failed to update session machine_managed: %s",
129 sqlite3_errmsg(ctx->db));
130 }
131 sqlite3_free(sql);
132}
133
134
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -0700135static void db_add_session_pps(struct hs20_svc *ctx, const char *user,
136 const char *realm, const char *sessionid,
137 xml_node_t *node)
138{
139 char *str;
140 char *sql;
141
142 str = xml_node_to_str(ctx->xml, node);
143 if (str == NULL)
144 return;
145 sql = sqlite3_mprintf("UPDATE sessions SET pps=%Q WHERE id=%Q AND "
146 "user=%Q AND realm=%Q",
147 str, sessionid, user, realm);
148 free(str);
149 if (sql == NULL)
150 return;
151 debug_print(ctx, 1, "DB: %s", sql);
152 if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
153 debug_print(ctx, 1, "Failed to add session pps: %s",
154 sqlite3_errmsg(ctx->db));
155 }
156 sqlite3_free(sql);
157}
158
159
160static void db_add_session_devinfo(struct hs20_svc *ctx, const char *sessionid,
161 xml_node_t *node)
162{
163 char *str;
164 char *sql;
165
166 str = xml_node_to_str(ctx->xml, node);
167 if (str == NULL)
168 return;
169 sql = sqlite3_mprintf("UPDATE sessions SET devinfo=%Q WHERE id=%Q",
170 str, sessionid);
171 free(str);
172 if (sql == NULL)
173 return;
174 debug_print(ctx, 1, "DB: %s", sql);
175 if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
176 debug_print(ctx, 1, "Failed to add session devinfo: %s",
177 sqlite3_errmsg(ctx->db));
178 }
179 sqlite3_free(sql);
180}
181
182
183static void db_add_session_devdetail(struct hs20_svc *ctx,
184 const char *sessionid,
185 xml_node_t *node)
186{
187 char *str;
188 char *sql;
189
190 str = xml_node_to_str(ctx->xml, node);
191 if (str == NULL)
192 return;
193 sql = sqlite3_mprintf("UPDATE sessions SET devdetail=%Q WHERE id=%Q",
194 str, sessionid);
195 free(str);
196 if (sql == NULL)
197 return;
198 debug_print(ctx, 1, "DB: %s", sql);
199 if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
200 debug_print(ctx, 1, "Failed to add session devdetail: %s",
201 sqlite3_errmsg(ctx->db));
202 }
203 sqlite3_free(sql);
204}
205
206
207static void db_remove_session(struct hs20_svc *ctx,
208 const char *user, const char *realm,
209 const char *sessionid)
210{
211 char *sql;
212
213 if (user == NULL || realm == NULL) {
214 sql = sqlite3_mprintf("DELETE FROM sessions WHERE "
215 "id=%Q", sessionid);
216 } else {
217 sql = sqlite3_mprintf("DELETE FROM sessions WHERE "
218 "user=%Q AND realm=%Q AND id=%Q",
219 user, realm, sessionid);
220 }
221 if (sql == NULL)
222 return;
223 debug_print(ctx, 1, "DB: %s", sql);
224 if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
225 debug_print(ctx, 1, "Failed to delete session entry from "
226 "sqlite database: %s", sqlite3_errmsg(ctx->db));
227 }
228 sqlite3_free(sql);
229}
230
231
232static void hs20_eventlog(struct hs20_svc *ctx,
233 const char *user, const char *realm,
234 const char *sessionid, const char *notes,
235 const char *dump)
236{
237 char *sql;
238 char *user_buf = NULL, *realm_buf = NULL;
239
240 debug_print(ctx, 1, "eventlog: %s", notes);
241
242 if (user == NULL) {
243 user_buf = db_get_session_val(ctx, NULL, NULL, sessionid,
244 "user");
245 user = user_buf;
246 realm_buf = db_get_session_val(ctx, NULL, NULL, sessionid,
247 "realm");
248 realm = realm_buf;
249 }
250
251 sql = sqlite3_mprintf("INSERT INTO eventlog"
252 "(user,realm,sessionid,timestamp,notes,dump,addr)"
253 " VALUES (%Q,%Q,%Q,"
254 "strftime('%%Y-%%m-%%d %%H:%%M:%%f','now'),"
255 "%Q,%Q,%Q)",
256 user, realm, sessionid, notes,
257 dump ? dump : "", ctx->addr ? ctx->addr : "");
258 free(user_buf);
259 free(realm_buf);
260 if (sql == NULL)
261 return;
262 if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
263 debug_print(ctx, 1, "Failed to add eventlog entry into sqlite "
264 "database: %s", sqlite3_errmsg(ctx->db));
265 }
266 sqlite3_free(sql);
267}
268
269
270static void hs20_eventlog_node(struct hs20_svc *ctx,
271 const char *user, const char *realm,
272 const char *sessionid, const char *notes,
273 xml_node_t *node)
274{
275 char *str;
276
277 if (node)
278 str = xml_node_to_str(ctx->xml, node);
279 else
280 str = NULL;
281 hs20_eventlog(ctx, user, realm, sessionid, notes, str);
282 free(str);
283}
284
285
286static void db_update_mo_str(struct hs20_svc *ctx, const char *user,
287 const char *realm, const char *name,
288 const char *str)
289{
290 char *sql;
291 if (user == NULL || realm == NULL || name == NULL)
292 return;
293 sql = sqlite3_mprintf("UPDATE users SET %s=%Q "
294 "WHERE identity=%Q AND realm=%Q AND phase2=1",
295 name, str, user, realm);
296 if (sql == NULL)
297 return;
298 debug_print(ctx, 1, "DB: %s", sql);
299 if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
300 debug_print(ctx, 1, "Failed to update user MO entry in sqlite "
301 "database: %s", sqlite3_errmsg(ctx->db));
302 }
303 sqlite3_free(sql);
304}
305
306
307static void db_update_mo(struct hs20_svc *ctx, const char *user,
308 const char *realm, const char *name, xml_node_t *mo)
309{
310 char *str;
311
312 str = xml_node_to_str(ctx->xml, mo);
313 if (str == NULL)
314 return;
315
316 db_update_mo_str(ctx, user, realm, name, str);
317 free(str);
318}
319
320
321static void add_text_node(struct hs20_svc *ctx, xml_node_t *parent,
322 const char *name, const char *value)
323{
324 xml_node_create_text(ctx->xml, parent, NULL, name, value ? value : "");
325}
326
327
328static void add_text_node_conf(struct hs20_svc *ctx, const char *realm,
329 xml_node_t *parent, const char *name,
330 const char *field)
331{
332 char *val;
333 val = db_get_osu_config_val(ctx, realm, field);
334 xml_node_create_text(ctx->xml, parent, NULL, name, val ? val : "");
335 os_free(val);
336}
337
338
Hai Shalom39ba6fc2019-01-22 12:40:38 -0800339static void add_text_node_conf_corrupt(struct hs20_svc *ctx, const char *realm,
340 xml_node_t *parent, const char *name,
341 const char *field)
342{
343 char *val;
344
345 val = db_get_osu_config_val(ctx, realm, field);
346 if (val) {
347 size_t len;
348
349 len = os_strlen(val);
350 if (len > 0) {
351 if (val[len - 1] == '0')
352 val[len - 1] = '1';
353 else
354 val[len - 1] = '0';
355 }
356 }
357 xml_node_create_text(ctx->xml, parent, NULL, name, val ? val : "");
358 os_free(val);
359}
360
361
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -0700362static int new_password(char *buf, int buflen)
363{
364 int i;
365
366 if (buflen < 1)
367 return -1;
368 buf[buflen - 1] = '\0';
369 if (os_get_random((unsigned char *) buf, buflen - 1) < 0)
370 return -1;
371
372 for (i = 0; i < buflen - 1; i++) {
373 unsigned char val = buf[i];
374 val %= 2 * 26 + 10;
375 if (val < 26)
376 buf[i] = 'a' + val;
377 else if (val < 2 * 26)
378 buf[i] = 'A' + val - 26;
379 else
380 buf[i] = '0' + val - 2 * 26;
381 }
382
383 return 0;
384}
385
386
387struct get_db_field_data {
388 const char *field;
389 char *value;
390};
391
392
393static int get_db_field(void *ctx, int argc, char *argv[], char *col[])
394{
395 struct get_db_field_data *data = ctx;
396 int i;
397
398 for (i = 0; i < argc; i++) {
399 if (os_strcmp(col[i], data->field) == 0 && argv[i]) {
400 os_free(data->value);
401 data->value = os_strdup(argv[i]);
402 break;
403 }
404 }
405
406 return 0;
407}
408
409
410static char * db_get_val(struct hs20_svc *ctx, const char *user,
411 const char *realm, const char *field, int dmacc)
412{
413 char *cmd;
414 struct get_db_field_data data;
415
416 cmd = sqlite3_mprintf("SELECT %s FROM users WHERE "
417 "%s=%Q AND realm=%Q AND phase2=1",
418 field, dmacc ? "osu_user" : "identity",
419 user, realm);
420 if (cmd == NULL)
421 return NULL;
422 memset(&data, 0, sizeof(data));
423 data.field = field;
424 if (sqlite3_exec(ctx->db, cmd, get_db_field, &data, NULL) != SQLITE_OK)
425 {
426 debug_print(ctx, 1, "Could not find user '%s'", user);
427 sqlite3_free(cmd);
428 return NULL;
429 }
430 sqlite3_free(cmd);
431
432 debug_print(ctx, 1, "DB: user='%s' realm='%s' field='%s' dmacc=%d --> "
433 "value='%s'", user, realm, field, dmacc, data.value);
434
435 return data.value;
436}
437
438
439static int db_update_val(struct hs20_svc *ctx, const char *user,
440 const char *realm, const char *field,
441 const char *val, int dmacc)
442{
443 char *cmd;
444 int ret;
445
446 cmd = sqlite3_mprintf("UPDATE users SET %s=%Q WHERE "
447 "%s=%Q AND realm=%Q AND phase2=1",
448 field, val, dmacc ? "osu_user" : "identity", user,
449 realm);
450 if (cmd == NULL)
451 return -1;
452 debug_print(ctx, 1, "DB: %s", cmd);
453 if (sqlite3_exec(ctx->db, cmd, NULL, NULL, NULL) != SQLITE_OK) {
454 debug_print(ctx, 1,
455 "Failed to update user in sqlite database: %s",
456 sqlite3_errmsg(ctx->db));
457 ret = -1;
458 } else {
459 debug_print(ctx, 1,
460 "DB: user='%s' realm='%s' field='%s' set to '%s'",
461 user, realm, field, val);
462 ret = 0;
463 }
464 sqlite3_free(cmd);
465
466 return ret;
467}
468
469
470static char * db_get_session_val(struct hs20_svc *ctx, const char *user,
471 const char *realm, const char *session_id,
472 const char *field)
473{
474 char *cmd;
475 struct get_db_field_data data;
476
477 if (user == NULL || realm == NULL) {
478 cmd = sqlite3_mprintf("SELECT %s FROM sessions WHERE "
479 "id=%Q", field, session_id);
480 } else {
481 cmd = sqlite3_mprintf("SELECT %s FROM sessions WHERE "
482 "user=%Q AND realm=%Q AND id=%Q",
483 field, user, realm, session_id);
484 }
485 if (cmd == NULL)
486 return NULL;
487 debug_print(ctx, 1, "DB: %s", cmd);
488 memset(&data, 0, sizeof(data));
489 data.field = field;
490 if (sqlite3_exec(ctx->db, cmd, get_db_field, &data, NULL) != SQLITE_OK)
491 {
492 debug_print(ctx, 1, "DB: Could not find session %s: %s",
493 session_id, sqlite3_errmsg(ctx->db));
494 sqlite3_free(cmd);
495 return NULL;
496 }
497 sqlite3_free(cmd);
498
499 debug_print(ctx, 1, "DB: return '%s'", data.value);
500 return data.value;
501}
502
503
504static int update_password(struct hs20_svc *ctx, const char *user,
505 const char *realm, const char *pw, int dmacc)
506{
507 char *cmd;
508
509 cmd = sqlite3_mprintf("UPDATE users SET password=%Q, "
510 "remediation='' "
511 "WHERE %s=%Q AND phase2=1",
512 pw, dmacc ? "osu_user" : "identity",
513 user);
514 if (cmd == NULL)
515 return -1;
516 debug_print(ctx, 1, "DB: %s", cmd);
517 if (sqlite3_exec(ctx->db, cmd, NULL, NULL, NULL) != SQLITE_OK) {
518 debug_print(ctx, 1, "Failed to update database for user '%s'",
519 user);
520 }
521 sqlite3_free(cmd);
522
523 return 0;
524}
525
526
527static int add_eap_ttls(struct hs20_svc *ctx, xml_node_t *parent)
528{
529 xml_node_t *node;
530
531 node = xml_node_create(ctx->xml, parent, NULL, "EAPMethod");
532 if (node == NULL)
533 return -1;
534
535 add_text_node(ctx, node, "EAPType", "21");
536 add_text_node(ctx, node, "InnerMethod", "MS-CHAP-V2");
537
538 return 0;
539}
540
541
542static xml_node_t * build_username_password(struct hs20_svc *ctx,
543 xml_node_t *parent,
544 const char *user, const char *pw)
545{
546 xml_node_t *node;
547 char *b64;
548
549 node = xml_node_create(ctx->xml, parent, NULL, "UsernamePassword");
550 if (node == NULL)
551 return NULL;
552
553 add_text_node(ctx, node, "Username", user);
554
555 b64 = (char *) base64_encode((unsigned char *) pw, strlen(pw), NULL);
556 if (b64 == NULL)
557 return NULL;
558 add_text_node(ctx, node, "Password", b64);
559 free(b64);
560
561 return node;
562}
563
564
565static int add_username_password(struct hs20_svc *ctx, xml_node_t *cred,
Hai Shalom39ba6fc2019-01-22 12:40:38 -0800566 const char *user, const char *pw,
567 int machine_managed)
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -0700568{
569 xml_node_t *node;
570
571 node = build_username_password(ctx, cred, user, pw);
572 if (node == NULL)
573 return -1;
574
Hai Shalom39ba6fc2019-01-22 12:40:38 -0800575 add_text_node(ctx, node, "MachineManaged",
576 machine_managed ? "TRUE" : "FALSE");
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -0700577 add_text_node(ctx, node, "SoftTokenApp", "");
578 add_eap_ttls(ctx, node);
579
580 return 0;
581}
582
583
584static void add_creation_date(struct hs20_svc *ctx, xml_node_t *cred)
585{
586 char str[30];
587 time_t now;
588 struct tm tm;
589
590 time(&now);
591 gmtime_r(&now, &tm);
592 snprintf(str, sizeof(str), "%04u-%02u-%02uT%02u:%02u:%02uZ",
593 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
594 tm.tm_hour, tm.tm_min, tm.tm_sec);
595 xml_node_create_text(ctx->xml, cred, NULL, "CreationDate", str);
596}
597
598
599static xml_node_t * build_credential_pw(struct hs20_svc *ctx,
600 const char *user, const char *realm,
Hai Shalom39ba6fc2019-01-22 12:40:38 -0800601 const char *pw, int machine_managed)
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -0700602{
603 xml_node_t *cred;
604
605 cred = xml_node_create_root(ctx->xml, NULL, NULL, NULL, "Credential");
606 if (cred == NULL) {
607 debug_print(ctx, 1, "Failed to create Credential node");
608 return NULL;
609 }
610 add_creation_date(ctx, cred);
Hai Shalom39ba6fc2019-01-22 12:40:38 -0800611 if (add_username_password(ctx, cred, user, pw, machine_managed) < 0) {
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -0700612 xml_node_free(ctx->xml, cred);
613 return NULL;
614 }
615 add_text_node(ctx, cred, "Realm", realm);
616
617 return cred;
618}
619
620
621static xml_node_t * build_credential(struct hs20_svc *ctx,
622 const char *user, const char *realm,
623 char *new_pw, size_t new_pw_len)
624{
625 if (new_password(new_pw, new_pw_len) < 0)
626 return NULL;
627 debug_print(ctx, 1, "Update password to '%s'", new_pw);
Hai Shalom39ba6fc2019-01-22 12:40:38 -0800628 return build_credential_pw(ctx, user, realm, new_pw, 1);
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -0700629}
630
631
632static xml_node_t * build_credential_cert(struct hs20_svc *ctx,
633 const char *user, const char *realm,
634 const char *cert_fingerprint)
635{
636 xml_node_t *cred, *cert;
637
638 cred = xml_node_create_root(ctx->xml, NULL, NULL, NULL, "Credential");
639 if (cred == NULL) {
640 debug_print(ctx, 1, "Failed to create Credential node");
641 return NULL;
642 }
643 add_creation_date(ctx, cred);
644 cert = xml_node_create(ctx->xml, cred, NULL, "DigitalCertificate");
645 add_text_node(ctx, cert, "CertificateType", "x509v3");
646 add_text_node(ctx, cert, "CertSHA256Fingerprint", cert_fingerprint);
647 add_text_node(ctx, cred, "Realm", realm);
648
649 return cred;
650}
651
652
653static xml_node_t * build_post_dev_data_response(struct hs20_svc *ctx,
654 xml_namespace_t **ret_ns,
655 const char *session_id,
656 const char *status,
657 const char *error_code)
658{
659 xml_node_t *spp_node = NULL;
660 xml_namespace_t *ns;
661
662 spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
663 "sppPostDevDataResponse");
664 if (spp_node == NULL)
665 return NULL;
666 if (ret_ns)
667 *ret_ns = ns;
668
669 xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
670 xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id);
671 xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", status);
672
673 if (error_code) {
674 xml_node_t *node;
675 node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
676 if (node)
677 xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
678 error_code);
679 }
680
681 return spp_node;
682}
683
684
685static int add_update_node(struct hs20_svc *ctx, xml_node_t *spp_node,
686 xml_namespace_t *ns, const char *uri,
687 xml_node_t *upd_node)
688{
689 xml_node_t *node, *tnds;
690 char *str;
691
692 tnds = mo_to_tnds(ctx->xml, upd_node, 0, NULL, NULL);
693 if (!tnds)
694 return -1;
695
696 str = xml_node_to_str(ctx->xml, tnds);
697 xml_node_free(ctx->xml, tnds);
698 if (str == NULL)
699 return -1;
700 node = xml_node_create_text(ctx->xml, spp_node, ns, "updateNode", str);
701 free(str);
702
703 xml_node_add_attr(ctx->xml, node, ns, "managementTreeURI", uri);
704
705 return 0;
706}
707
708
709static xml_node_t * build_sub_rem_resp(struct hs20_svc *ctx,
710 const char *user, const char *realm,
711 const char *session_id,
712 int machine_rem, int dmacc)
713{
714 xml_namespace_t *ns;
715 xml_node_t *spp_node, *cred;
716 char buf[400];
717 char new_pw[33];
718 char *real_user = NULL;
719 char *status;
720 char *cert;
721
722 if (dmacc) {
723 real_user = db_get_val(ctx, user, realm, "identity", dmacc);
724 if (real_user == NULL) {
725 debug_print(ctx, 1, "Could not find user identity for "
726 "dmacc user '%s'", user);
727 return NULL;
728 }
729 }
730
731 cert = db_get_val(ctx, user, realm, "cert", dmacc);
732 if (cert && cert[0] == '\0')
733 cert = NULL;
734 if (cert) {
735 cred = build_credential_cert(ctx, real_user ? real_user : user,
736 realm, cert);
737 } else {
Hai Shalom39ba6fc2019-01-22 12:40:38 -0800738 char *pw;
739
740 pw = db_get_session_val(ctx, user, realm, session_id,
741 "password");
742 if (pw && pw[0]) {
743 debug_print(ctx, 1, "New password from the user: '%s'",
744 pw);
745 snprintf(new_pw, sizeof(new_pw), "%s", pw);
746 free(pw);
747 cred = build_credential_pw(ctx,
748 real_user ? real_user : user,
749 realm, new_pw, 0);
750 } else {
751 cred = build_credential(ctx,
752 real_user ? real_user : user,
753 realm, new_pw, sizeof(new_pw));
754 }
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -0700755 }
756 free(real_user);
757 if (!cred) {
758 debug_print(ctx, 1, "Could not build credential");
759 return NULL;
760 }
761
762 status = "Remediation complete, request sppUpdateResponse";
763 spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
764 NULL);
765 if (spp_node == NULL) {
766 debug_print(ctx, 1, "Could not build sppPostDevDataResponse");
767 return NULL;
768 }
769
770 snprintf(buf, sizeof(buf),
Hai Shalom39ba6fc2019-01-22 12:40:38 -0800771 "./Wi-Fi/%s/PerProviderSubscription/Cred01/Credential",
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -0700772 realm);
773
774 if (add_update_node(ctx, spp_node, ns, buf, cred) < 0) {
775 debug_print(ctx, 1, "Could not add update node");
776 xml_node_free(ctx->xml, spp_node);
777 return NULL;
778 }
779
780 hs20_eventlog_node(ctx, user, realm, session_id,
781 machine_rem ? "machine remediation" :
782 "user remediation", cred);
783 xml_node_free(ctx->xml, cred);
784
785 if (cert) {
786 debug_print(ctx, 1, "Certificate credential - no need for DB "
787 "password update on success notification");
788 } else {
789 debug_print(ctx, 1, "Request DB password update on success "
790 "notification");
791 db_add_session(ctx, user, realm, session_id, new_pw, NULL,
Hai Shalom39ba6fc2019-01-22 12:40:38 -0800792 UPDATE_PASSWORD, NULL);
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -0700793 }
794
795 return spp_node;
796}
797
798
799static xml_node_t * machine_remediation(struct hs20_svc *ctx,
800 const char *user,
801 const char *realm,
802 const char *session_id, int dmacc)
803{
804 return build_sub_rem_resp(ctx, user, realm, session_id, 1, dmacc);
805}
806
807
808static xml_node_t * policy_remediation(struct hs20_svc *ctx,
809 const char *user, const char *realm,
810 const char *session_id, int dmacc)
811{
812 xml_namespace_t *ns;
813 xml_node_t *spp_node, *policy;
814 char buf[400];
815 const char *status;
816
817 hs20_eventlog(ctx, user, realm, session_id,
818 "requires policy remediation", NULL);
819
820 db_add_session(ctx, user, realm, session_id, NULL, NULL,
Hai Shalom39ba6fc2019-01-22 12:40:38 -0800821 POLICY_REMEDIATION, NULL);
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -0700822
823 policy = build_policy(ctx, user, realm, dmacc);
824 if (!policy) {
825 return build_post_dev_data_response(
826 ctx, NULL, session_id,
827 "No update available at this time", NULL);
828 }
829
830 status = "Remediation complete, request sppUpdateResponse";
831 spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
832 NULL);
833 if (spp_node == NULL)
834 return NULL;
835
836 snprintf(buf, sizeof(buf),
Hai Shalom39ba6fc2019-01-22 12:40:38 -0800837 "./Wi-Fi/%s/PerProviderSubscription/Cred01/Policy",
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -0700838 realm);
839
840 if (add_update_node(ctx, spp_node, ns, buf, policy) < 0) {
841 xml_node_free(ctx->xml, spp_node);
842 xml_node_free(ctx->xml, policy);
843 return NULL;
844 }
845
846 hs20_eventlog_node(ctx, user, realm, session_id,
847 "policy update (sub rem)", policy);
848 xml_node_free(ctx->xml, policy);
849
850 return spp_node;
851}
852
853
854static xml_node_t * browser_remediation(struct hs20_svc *ctx,
855 const char *session_id,
856 const char *redirect_uri,
857 const char *uri)
858{
859 xml_namespace_t *ns;
860 xml_node_t *spp_node, *exec_node;
861
862 if (redirect_uri == NULL) {
863 debug_print(ctx, 1, "Missing redirectURI attribute for user "
864 "remediation");
865 return NULL;
866 }
867 debug_print(ctx, 1, "redirectURI %s", redirect_uri);
868
869 spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
870 NULL);
871 if (spp_node == NULL)
872 return NULL;
873
874 exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
875 xml_node_create_text(ctx->xml, exec_node, ns, "launchBrowserToURI",
876 uri);
877 return spp_node;
878}
879
880
881static xml_node_t * user_remediation(struct hs20_svc *ctx, const char *user,
882 const char *realm, const char *session_id,
883 const char *redirect_uri)
884{
885 char uri[300], *val;
886
887 hs20_eventlog(ctx, user, realm, session_id,
888 "requires user remediation", NULL);
889 val = db_get_osu_config_val(ctx, realm, "remediation_url");
890 if (val == NULL)
891 return NULL;
892
893 db_add_session(ctx, user, realm, session_id, NULL, redirect_uri,
Hai Shalom39ba6fc2019-01-22 12:40:38 -0800894 USER_REMEDIATION, NULL);
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -0700895
896 snprintf(uri, sizeof(uri), "%s%s", val, session_id);
897 os_free(val);
898 return browser_remediation(ctx, session_id, redirect_uri, uri);
899}
900
901
902static xml_node_t * free_remediation(struct hs20_svc *ctx,
903 const char *user, const char *realm,
904 const char *session_id,
905 const char *redirect_uri)
906{
907 char uri[300], *val;
908
909 hs20_eventlog(ctx, user, realm, session_id,
910 "requires free/public account remediation", NULL);
911 val = db_get_osu_config_val(ctx, realm, "free_remediation_url");
912 if (val == NULL)
913 return NULL;
914
915 db_add_session(ctx, user, realm, session_id, NULL, redirect_uri,
Hai Shalom39ba6fc2019-01-22 12:40:38 -0800916 FREE_REMEDIATION, NULL);
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -0700917
918 snprintf(uri, sizeof(uri), "%s%s", val, session_id);
919 os_free(val);
920 return browser_remediation(ctx, session_id, redirect_uri, uri);
921}
922
923
924static xml_node_t * no_sub_rem(struct hs20_svc *ctx,
925 const char *user, const char *realm,
926 const char *session_id)
927{
928 const char *status;
929
930 hs20_eventlog(ctx, user, realm, session_id,
931 "no subscription mediation available", NULL);
932
933 status = "No update available at this time";
934 return build_post_dev_data_response(ctx, NULL, session_id, status,
935 NULL);
936}
937
938
939static xml_node_t * hs20_subscription_remediation(struct hs20_svc *ctx,
940 const char *user,
941 const char *realm,
942 const char *session_id,
943 int dmacc,
944 const char *redirect_uri)
945{
946 char *type, *identity;
947 xml_node_t *ret;
948 char *free_account;
949
950 identity = db_get_val(ctx, user, realm, "identity", dmacc);
951 if (identity == NULL || strlen(identity) == 0) {
952 hs20_eventlog(ctx, user, realm, session_id,
953 "user not found in database for remediation",
954 NULL);
955 os_free(identity);
956 return build_post_dev_data_response(ctx, NULL, session_id,
957 "Error occurred",
958 "Not found");
959 }
960 os_free(identity);
961
962 free_account = db_get_osu_config_val(ctx, realm, "free_account");
963 if (free_account && strcmp(free_account, user) == 0) {
964 free(free_account);
965 return no_sub_rem(ctx, user, realm, session_id);
966 }
967 free(free_account);
968
969 type = db_get_val(ctx, user, realm, "remediation", dmacc);
970 if (type && strcmp(type, "free") != 0) {
971 char *val;
972 int shared = 0;
973 val = db_get_val(ctx, user, realm, "shared", dmacc);
974 if (val)
975 shared = atoi(val);
976 free(val);
977 if (shared) {
978 free(type);
979 return no_sub_rem(ctx, user, realm, session_id);
980 }
981 }
982 if (type && strcmp(type, "user") == 0)
983 ret = user_remediation(ctx, user, realm, session_id,
984 redirect_uri);
985 else if (type && strcmp(type, "free") == 0)
986 ret = free_remediation(ctx, user, realm, session_id,
987 redirect_uri);
988 else if (type && strcmp(type, "policy") == 0)
989 ret = policy_remediation(ctx, user, realm, session_id, dmacc);
Hai Shalom39ba6fc2019-01-22 12:40:38 -0800990 else if (type && strcmp(type, "machine") == 0)
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -0700991 ret = machine_remediation(ctx, user, realm, session_id, dmacc);
Hai Shalom39ba6fc2019-01-22 12:40:38 -0800992 else
993 ret = no_sub_rem(ctx, user, realm, session_id);
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -0700994 free(type);
995
996 return ret;
997}
998
999
1000static xml_node_t * build_policy(struct hs20_svc *ctx, const char *user,
1001 const char *realm, int use_dmacc)
1002{
1003 char *policy_id;
1004 char fname[200];
1005 xml_node_t *policy, *node;
1006
1007 policy_id = db_get_val(ctx, user, realm, "policy", use_dmacc);
1008 if (policy_id == NULL || strlen(policy_id) == 0) {
1009 free(policy_id);
1010 policy_id = strdup("default");
1011 if (policy_id == NULL)
1012 return NULL;
1013 }
1014
1015 snprintf(fname, sizeof(fname), "%s/spp/policy/%s.xml",
1016 ctx->root_dir, policy_id);
1017 free(policy_id);
1018 debug_print(ctx, 1, "Use policy file %s", fname);
1019
1020 policy = node_from_file(ctx->xml, fname);
1021 if (policy == NULL)
1022 return NULL;
1023
1024 node = get_node_uri(ctx->xml, policy, "Policy/PolicyUpdate/URI");
1025 if (node) {
1026 char *url;
1027 url = db_get_osu_config_val(ctx, realm, "policy_url");
1028 if (url == NULL) {
1029 xml_node_free(ctx->xml, policy);
1030 return NULL;
1031 }
1032 xml_node_set_text(ctx->xml, node, url);
1033 free(url);
1034 }
1035
1036 node = get_node_uri(ctx->xml, policy, "Policy/PolicyUpdate");
1037 if (node && use_dmacc) {
1038 char *pw;
1039 pw = db_get_val(ctx, user, realm, "osu_password", use_dmacc);
1040 if (pw == NULL ||
1041 build_username_password(ctx, node, user, pw) == NULL) {
1042 debug_print(ctx, 1, "Failed to add Policy/PolicyUpdate/"
1043 "UsernamePassword");
1044 free(pw);
1045 xml_node_free(ctx->xml, policy);
1046 return NULL;
1047 }
1048 free(pw);
1049 }
1050
1051 return policy;
1052}
1053
1054
1055static xml_node_t * hs20_policy_update(struct hs20_svc *ctx,
1056 const char *user, const char *realm,
1057 const char *session_id, int dmacc)
1058{
1059 xml_namespace_t *ns;
1060 xml_node_t *spp_node;
1061 xml_node_t *policy;
1062 char buf[400];
1063 const char *status;
1064 char *identity;
1065
1066 identity = db_get_val(ctx, user, realm, "identity", dmacc);
1067 if (identity == NULL || strlen(identity) == 0) {
1068 hs20_eventlog(ctx, user, realm, session_id,
1069 "user not found in database for policy update",
1070 NULL);
1071 os_free(identity);
1072 return build_post_dev_data_response(ctx, NULL, session_id,
1073 "Error occurred",
1074 "Not found");
1075 }
1076 os_free(identity);
1077
1078 policy = build_policy(ctx, user, realm, dmacc);
1079 if (!policy) {
1080 return build_post_dev_data_response(
1081 ctx, NULL, session_id,
1082 "No update available at this time", NULL);
1083 }
1084
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001085 db_add_session(ctx, user, realm, session_id, NULL, NULL, POLICY_UPDATE,
1086 NULL);
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001087
1088 status = "Update complete, request sppUpdateResponse";
1089 spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
1090 NULL);
1091 if (spp_node == NULL)
1092 return NULL;
1093
1094 snprintf(buf, sizeof(buf),
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001095 "./Wi-Fi/%s/PerProviderSubscription/Cred01/Policy",
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001096 realm);
1097
1098 if (add_update_node(ctx, spp_node, ns, buf, policy) < 0) {
1099 xml_node_free(ctx->xml, spp_node);
1100 xml_node_free(ctx->xml, policy);
1101 return NULL;
1102 }
1103
1104 hs20_eventlog_node(ctx, user, realm, session_id, "policy update",
1105 policy);
1106 xml_node_free(ctx->xml, policy);
1107
1108 return spp_node;
1109}
1110
1111
1112static xml_node_t * spp_get_mo(struct hs20_svc *ctx, xml_node_t *node,
1113 const char *urn, int *valid, char **ret_err)
1114{
1115 xml_node_t *child, *tnds, *mo;
1116 const char *name;
1117 char *mo_urn;
1118 char *str;
1119 char fname[200];
1120
1121 *valid = -1;
1122 if (ret_err)
1123 *ret_err = NULL;
1124
1125 xml_node_for_each_child(ctx->xml, child, node) {
1126 xml_node_for_each_check(ctx->xml, child);
1127 name = xml_node_get_localname(ctx->xml, child);
1128 if (strcmp(name, "moContainer") != 0)
1129 continue;
1130 mo_urn = xml_node_get_attr_value_ns(ctx->xml, child, SPP_NS_URI,
1131 "moURN");
1132 if (strcasecmp(urn, mo_urn) == 0) {
1133 xml_node_get_attr_value_free(ctx->xml, mo_urn);
1134 break;
1135 }
1136 xml_node_get_attr_value_free(ctx->xml, mo_urn);
1137 }
1138
1139 if (child == NULL)
1140 return NULL;
1141
1142 debug_print(ctx, 1, "moContainer text for %s", urn);
1143 debug_dump_node(ctx, "moContainer", child);
1144
1145 str = xml_node_get_text(ctx->xml, child);
1146 debug_print(ctx, 1, "moContainer payload: '%s'", str);
1147 tnds = xml_node_from_buf(ctx->xml, str);
1148 xml_node_get_text_free(ctx->xml, str);
1149 if (tnds == NULL) {
1150 debug_print(ctx, 1, "could not parse moContainer text");
1151 return NULL;
1152 }
1153
1154 snprintf(fname, sizeof(fname), "%s/spp/dm_ddf-v1_2.dtd", ctx->root_dir);
1155 if (xml_validate_dtd(ctx->xml, tnds, fname, ret_err) == 0)
1156 *valid = 1;
1157 else if (ret_err && *ret_err &&
1158 os_strcmp(*ret_err, "No declaration for attribute xmlns of element MgmtTree\n") == 0) {
1159 free(*ret_err);
1160 debug_print(ctx, 1, "Ignore OMA-DM DDF DTD validation error for MgmtTree namespace declaration with xmlns attribute");
1161 *ret_err = NULL;
1162 *valid = 1;
1163 } else
1164 *valid = 0;
1165
1166 mo = tnds_to_mo(ctx->xml, tnds);
1167 xml_node_free(ctx->xml, tnds);
1168 if (mo == NULL) {
1169 debug_print(ctx, 1, "invalid moContainer for %s", urn);
1170 }
1171
1172 return mo;
1173}
1174
1175
1176static xml_node_t * spp_exec_upload_mo(struct hs20_svc *ctx,
1177 const char *session_id, const char *urn)
1178{
1179 xml_namespace_t *ns;
1180 xml_node_t *spp_node, *node, *exec_node;
1181
1182 spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
1183 NULL);
1184 if (spp_node == NULL)
1185 return NULL;
1186
1187 exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
1188
1189 node = xml_node_create(ctx->xml, exec_node, ns, "uploadMO");
1190 xml_node_add_attr(ctx->xml, node, ns, "moURN", urn);
1191
1192 return spp_node;
1193}
1194
1195
1196static xml_node_t * hs20_subscription_registration(struct hs20_svc *ctx,
1197 const char *realm,
1198 const char *session_id,
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001199 const char *redirect_uri,
1200 const u8 *mac_addr)
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001201{
1202 xml_namespace_t *ns;
1203 xml_node_t *spp_node, *exec_node;
1204 char uri[300], *val;
1205
1206 if (db_add_session(ctx, NULL, realm, session_id, NULL, redirect_uri,
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001207 SUBSCRIPTION_REGISTRATION, mac_addr) < 0)
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001208 return NULL;
1209 val = db_get_osu_config_val(ctx, realm, "signup_url");
1210 if (val == NULL)
1211 return NULL;
1212
1213 spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
1214 NULL);
1215 if (spp_node == NULL)
1216 return NULL;
1217
1218 exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
1219
1220 snprintf(uri, sizeof(uri), "%s%s", val, session_id);
1221 os_free(val);
1222 xml_node_create_text(ctx->xml, exec_node, ns, "launchBrowserToURI",
1223 uri);
1224 return spp_node;
1225}
1226
1227
1228static xml_node_t * hs20_user_input_remediation(struct hs20_svc *ctx,
1229 const char *user,
1230 const char *realm, int dmacc,
1231 const char *session_id)
1232{
1233 return build_sub_rem_resp(ctx, user, realm, session_id, 0, dmacc);
1234}
1235
1236
1237static char * db_get_osu_config_val(struct hs20_svc *ctx, const char *realm,
1238 const char *field)
1239{
1240 char *cmd;
1241 struct get_db_field_data data;
1242
1243 cmd = sqlite3_mprintf("SELECT value FROM osu_config WHERE realm=%Q AND "
1244 "field=%Q", realm, field);
1245 if (cmd == NULL)
1246 return NULL;
1247 debug_print(ctx, 1, "DB: %s", cmd);
1248 memset(&data, 0, sizeof(data));
1249 data.field = "value";
1250 if (sqlite3_exec(ctx->db, cmd, get_db_field, &data, NULL) != SQLITE_OK)
1251 {
1252 debug_print(ctx, 1, "DB: Could not find osu_config %s: %s",
1253 realm, sqlite3_errmsg(ctx->db));
1254 sqlite3_free(cmd);
1255 return NULL;
1256 }
1257 sqlite3_free(cmd);
1258
1259 debug_print(ctx, 1, "DB: return '%s'", data.value);
1260 return data.value;
1261}
1262
1263
1264static xml_node_t * build_pps(struct hs20_svc *ctx,
1265 const char *user, const char *realm,
1266 const char *pw, const char *cert,
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001267 int machine_managed, const char *test)
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001268{
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001269 xml_node_t *pps, *c, *trust, *aaa, *aaa1, *upd, *homesp, *p;
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001270 xml_node_t *cred, *eap, *userpw;
1271
1272 pps = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
1273 "PerProviderSubscription");
1274 if (pps == NULL)
1275 return NULL;
1276
1277 add_text_node(ctx, pps, "UpdateIdentifier", "1");
1278
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001279 c = xml_node_create(ctx->xml, pps, NULL, "Cred01");
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001280
1281 add_text_node(ctx, c, "CredentialPriority", "1");
1282
1283 aaa = xml_node_create(ctx->xml, c, NULL, "AAAServerTrustRoot");
1284 aaa1 = xml_node_create(ctx->xml, aaa, NULL, "AAA1");
1285 add_text_node_conf(ctx, realm, aaa1, "CertURL",
1286 "aaa_trust_root_cert_url");
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001287 if (test && os_strcmp(test, "corrupt_aaa_hash") == 0) {
1288 debug_print(ctx, 1,
1289 "TEST: Corrupt PPS/Cred*/AAAServerTrustRoot/Root*/CertSHA256FingerPrint");
1290 add_text_node_conf_corrupt(ctx, realm, aaa1,
1291 "CertSHA256Fingerprint",
1292 "aaa_trust_root_cert_fingerprint");
1293 } else {
1294 add_text_node_conf(ctx, realm, aaa1, "CertSHA256Fingerprint",
1295 "aaa_trust_root_cert_fingerprint");
1296 }
1297
1298 if (test && os_strcmp(test, "corrupt_polupd_hash") == 0) {
1299 debug_print(ctx, 1,
1300 "TEST: Corrupt PPS/Cred*/Policy/PolicyUpdate/Trustroot/CertSHA256FingerPrint");
1301 p = xml_node_create(ctx->xml, c, NULL, "Policy");
1302 upd = xml_node_create(ctx->xml, p, NULL, "PolicyUpdate");
1303 add_text_node(ctx, upd, "UpdateInterval", "30");
1304 add_text_node(ctx, upd, "UpdateMethod", "SPP-ClientInitiated");
1305 add_text_node(ctx, upd, "Restriction", "Unrestricted");
1306 add_text_node_conf(ctx, realm, upd, "URI", "policy_url");
1307 trust = xml_node_create(ctx->xml, upd, NULL, "TrustRoot");
1308 add_text_node_conf(ctx, realm, trust, "CertURL",
1309 "policy_trust_root_cert_url");
1310 add_text_node_conf_corrupt(ctx, realm, trust,
1311 "CertSHA256Fingerprint",
1312 "policy_trust_root_cert_fingerprint");
1313 }
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001314
1315 upd = xml_node_create(ctx->xml, c, NULL, "SubscriptionUpdate");
1316 add_text_node(ctx, upd, "UpdateInterval", "4294967295");
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001317 add_text_node(ctx, upd, "UpdateMethod", "SPP-ClientInitiated");
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001318 add_text_node(ctx, upd, "Restriction", "HomeSP");
1319 add_text_node_conf(ctx, realm, upd, "URI", "spp_http_auth_url");
1320 trust = xml_node_create(ctx->xml, upd, NULL, "TrustRoot");
1321 add_text_node_conf(ctx, realm, trust, "CertURL", "trust_root_cert_url");
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001322 if (test && os_strcmp(test, "corrupt_subrem_hash") == 0) {
1323 debug_print(ctx, 1,
1324 "TEST: Corrupt PPS/Cred*/SubscriptionUpdate/Trustroot/CertSHA256FingerPrint");
1325 add_text_node_conf_corrupt(ctx, realm, trust,
1326 "CertSHA256Fingerprint",
1327 "trust_root_cert_fingerprint");
1328 } else {
1329 add_text_node_conf(ctx, realm, trust, "CertSHA256Fingerprint",
1330 "trust_root_cert_fingerprint");
1331 }
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001332
1333 homesp = xml_node_create(ctx->xml, c, NULL, "HomeSP");
1334 add_text_node_conf(ctx, realm, homesp, "FriendlyName", "friendly_name");
1335 add_text_node_conf(ctx, realm, homesp, "FQDN", "fqdn");
1336
1337 xml_node_create(ctx->xml, c, NULL, "SubscriptionParameters");
1338
1339 cred = xml_node_create(ctx->xml, c, NULL, "Credential");
1340 add_creation_date(ctx, cred);
1341 if (cert) {
1342 xml_node_t *dc;
1343 dc = xml_node_create(ctx->xml, cred, NULL,
1344 "DigitalCertificate");
1345 add_text_node(ctx, dc, "CertificateType", "x509v3");
1346 add_text_node(ctx, dc, "CertSHA256Fingerprint", cert);
1347 } else {
1348 userpw = build_username_password(ctx, cred, user, pw);
1349 add_text_node(ctx, userpw, "MachineManaged",
1350 machine_managed ? "TRUE" : "FALSE");
1351 eap = xml_node_create(ctx->xml, userpw, NULL, "EAPMethod");
1352 add_text_node(ctx, eap, "EAPType", "21");
1353 add_text_node(ctx, eap, "InnerMethod", "MS-CHAP-V2");
1354 }
1355 add_text_node(ctx, cred, "Realm", realm);
1356
1357 return pps;
1358}
1359
1360
1361static xml_node_t * spp_exec_get_certificate(struct hs20_svc *ctx,
1362 const char *session_id,
1363 const char *user,
1364 const char *realm)
1365{
1366 xml_namespace_t *ns;
1367 xml_node_t *spp_node, *enroll, *exec_node;
1368 char *val;
1369 char password[11];
1370 char *b64;
1371
1372 if (new_password(password, sizeof(password)) < 0)
1373 return NULL;
1374
1375 spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
1376 NULL);
1377 if (spp_node == NULL)
1378 return NULL;
1379
1380 exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
1381
1382 enroll = xml_node_create(ctx->xml, exec_node, ns, "getCertificate");
1383 xml_node_add_attr(ctx->xml, enroll, NULL, "enrollmentProtocol", "EST");
1384
1385 val = db_get_osu_config_val(ctx, realm, "est_url");
1386 xml_node_create_text(ctx->xml, enroll, ns, "enrollmentServerURI",
1387 val ? val : "");
1388 os_free(val);
1389 xml_node_create_text(ctx->xml, enroll, ns, "estUserID", user);
1390
1391 b64 = (char *) base64_encode((unsigned char *) password,
1392 strlen(password), NULL);
1393 if (b64 == NULL) {
1394 xml_node_free(ctx->xml, spp_node);
1395 return NULL;
1396 }
1397 xml_node_create_text(ctx->xml, enroll, ns, "estPassword", b64);
1398 free(b64);
1399
1400 db_update_session_password(ctx, user, realm, session_id, password);
1401
1402 return spp_node;
1403}
1404
1405
1406static xml_node_t * hs20_user_input_registration(struct hs20_svc *ctx,
1407 const char *session_id,
1408 int enrollment_done)
1409{
1410 xml_namespace_t *ns;
1411 xml_node_t *spp_node, *node = NULL;
1412 xml_node_t *pps, *tnds;
1413 char buf[400];
1414 char *str;
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001415 char *user, *realm, *pw, *type, *mm, *test;
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001416 const char *status;
1417 int cert = 0;
1418 int machine_managed = 0;
1419 char *fingerprint;
1420
1421 user = db_get_session_val(ctx, NULL, NULL, session_id, "user");
1422 realm = db_get_session_val(ctx, NULL, NULL, session_id, "realm");
1423 pw = db_get_session_val(ctx, NULL, NULL, session_id, "password");
1424
1425 if (!user || !realm || !pw) {
1426 debug_print(ctx, 1, "Could not find session info from DB for "
1427 "the new subscription");
1428 free(user);
1429 free(realm);
1430 free(pw);
1431 return NULL;
1432 }
1433
1434 mm = db_get_session_val(ctx, NULL, NULL, session_id, "machine_managed");
1435 if (mm && atoi(mm))
1436 machine_managed = 1;
1437 free(mm);
1438
1439 type = db_get_session_val(ctx, NULL, NULL, session_id, "type");
1440 if (type && strcmp(type, "cert") == 0)
1441 cert = 1;
1442 free(type);
1443
1444 if (cert && !enrollment_done) {
1445 xml_node_t *ret;
1446 hs20_eventlog(ctx, user, realm, session_id,
1447 "request client certificate enrollment", NULL);
1448 ret = spp_exec_get_certificate(ctx, session_id, user, realm);
1449 free(user);
1450 free(realm);
1451 free(pw);
1452 return ret;
1453 }
1454
1455 if (!cert && strlen(pw) == 0) {
1456 machine_managed = 1;
1457 free(pw);
1458 pw = malloc(11);
1459 if (pw == NULL || new_password(pw, 11) < 0) {
1460 free(user);
1461 free(realm);
1462 free(pw);
1463 return NULL;
1464 }
1465 }
1466
1467 status = "Provisioning complete, request sppUpdateResponse";
1468 spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
1469 NULL);
1470 if (spp_node == NULL)
1471 return NULL;
1472
1473 fingerprint = db_get_session_val(ctx, NULL, NULL, session_id, "cert");
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001474 test = db_get_session_val(ctx, NULL, NULL, session_id, "test");
1475 if (test)
1476 debug_print(ctx, 1, "TEST: Requested special behavior: %s",
1477 test);
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001478 pps = build_pps(ctx, user, realm, pw,
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001479 fingerprint ? fingerprint : NULL, machine_managed,
1480 test);
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001481 free(fingerprint);
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001482 free(test);
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001483 if (!pps) {
1484 xml_node_free(ctx->xml, spp_node);
1485 free(user);
1486 free(realm);
1487 free(pw);
1488 return NULL;
1489 }
1490
1491 debug_print(ctx, 1, "Request DB subscription registration on success "
1492 "notification");
Dmitry Shmidt216983b2015-02-06 10:50:36 -08001493 if (machine_managed) {
1494 db_update_session_password(ctx, user, realm, session_id, pw);
1495 db_update_session_machine_managed(ctx, user, realm, session_id,
1496 machine_managed);
1497 }
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001498 db_add_session_pps(ctx, user, realm, session_id, pps);
1499
1500 hs20_eventlog_node(ctx, user, realm, session_id,
1501 "new subscription", pps);
1502 free(user);
1503 free(pw);
1504
1505 tnds = mo_to_tnds(ctx->xml, pps, 0, URN_HS20_PPS, NULL);
1506 xml_node_free(ctx->xml, pps);
1507 if (!tnds) {
1508 xml_node_free(ctx->xml, spp_node);
1509 free(realm);
1510 return NULL;
1511 }
1512
1513 str = xml_node_to_str(ctx->xml, tnds);
1514 xml_node_free(ctx->xml, tnds);
1515 if (str == NULL) {
1516 xml_node_free(ctx->xml, spp_node);
1517 free(realm);
1518 return NULL;
1519 }
1520
1521 node = xml_node_create_text(ctx->xml, spp_node, ns, "addMO", str);
1522 free(str);
1523 snprintf(buf, sizeof(buf), "./Wi-Fi/%s/PerProviderSubscription", realm);
1524 free(realm);
1525 xml_node_add_attr(ctx->xml, node, ns, "managementTreeURI", buf);
1526 xml_node_add_attr(ctx->xml, node, ns, "moURN", URN_HS20_PPS);
1527
1528 return spp_node;
1529}
1530
1531
1532static xml_node_t * hs20_user_input_free_remediation(struct hs20_svc *ctx,
1533 const char *user,
1534 const char *realm,
1535 const char *session_id)
1536{
1537 xml_namespace_t *ns;
1538 xml_node_t *spp_node;
1539 xml_node_t *cred;
1540 char buf[400];
1541 char *status;
1542 char *free_account, *pw;
1543
1544 free_account = db_get_osu_config_val(ctx, realm, "free_account");
1545 if (free_account == NULL)
1546 return NULL;
1547 pw = db_get_val(ctx, free_account, realm, "password", 0);
1548 if (pw == NULL) {
1549 free(free_account);
1550 return NULL;
1551 }
1552
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001553 cred = build_credential_pw(ctx, free_account, realm, pw, 1);
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001554 free(free_account);
1555 free(pw);
1556 if (!cred) {
1557 xml_node_free(ctx->xml, cred);
1558 return NULL;
1559 }
1560
1561 status = "Remediation complete, request sppUpdateResponse";
1562 spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
1563 NULL);
1564 if (spp_node == NULL)
1565 return NULL;
1566
1567 snprintf(buf, sizeof(buf),
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001568 "./Wi-Fi/%s/PerProviderSubscription/Cred01/Credential",
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001569 realm);
1570
1571 if (add_update_node(ctx, spp_node, ns, buf, cred) < 0) {
1572 xml_node_free(ctx->xml, spp_node);
1573 return NULL;
1574 }
1575
1576 hs20_eventlog_node(ctx, user, realm, session_id,
1577 "free/public remediation", cred);
1578 xml_node_free(ctx->xml, cred);
1579
1580 return spp_node;
1581}
1582
1583
1584static xml_node_t * hs20_user_input_complete(struct hs20_svc *ctx,
1585 const char *user,
1586 const char *realm, int dmacc,
1587 const char *session_id)
1588{
1589 char *val;
1590 enum hs20_session_operation oper;
1591
1592 val = db_get_session_val(ctx, user, realm, session_id, "operation");
1593 if (val == NULL) {
1594 debug_print(ctx, 1, "No session %s found to continue",
1595 session_id);
1596 return NULL;
1597 }
1598 oper = atoi(val);
1599 free(val);
1600
1601 if (oper == USER_REMEDIATION) {
1602 return hs20_user_input_remediation(ctx, user, realm, dmacc,
1603 session_id);
1604 }
1605
1606 if (oper == FREE_REMEDIATION) {
1607 return hs20_user_input_free_remediation(ctx, user, realm,
1608 session_id);
1609 }
1610
1611 if (oper == SUBSCRIPTION_REGISTRATION) {
1612 return hs20_user_input_registration(ctx, session_id, 0);
1613 }
1614
1615 debug_print(ctx, 1, "User session %s not in state for user input "
1616 "completion", session_id);
1617 return NULL;
1618}
1619
1620
1621static xml_node_t * hs20_cert_enroll_completed(struct hs20_svc *ctx,
1622 const char *user,
1623 const char *realm, int dmacc,
1624 const char *session_id)
1625{
1626 char *val;
1627 enum hs20_session_operation oper;
1628
1629 val = db_get_session_val(ctx, user, realm, session_id, "operation");
1630 if (val == NULL) {
1631 debug_print(ctx, 1, "No session %s found to continue",
1632 session_id);
1633 return NULL;
1634 }
1635 oper = atoi(val);
1636 free(val);
1637
1638 if (oper == SUBSCRIPTION_REGISTRATION)
1639 return hs20_user_input_registration(ctx, session_id, 1);
1640
1641 debug_print(ctx, 1, "User session %s not in state for certificate "
1642 "enrollment completion", session_id);
1643 return NULL;
1644}
1645
1646
1647static xml_node_t * hs20_cert_enroll_failed(struct hs20_svc *ctx,
1648 const char *user,
1649 const char *realm, int dmacc,
1650 const char *session_id)
1651{
1652 char *val;
1653 enum hs20_session_operation oper;
1654 xml_node_t *spp_node, *node;
1655 char *status;
1656 xml_namespace_t *ns;
1657
1658 val = db_get_session_val(ctx, user, realm, session_id, "operation");
1659 if (val == NULL) {
1660 debug_print(ctx, 1, "No session %s found to continue",
1661 session_id);
1662 return NULL;
1663 }
1664 oper = atoi(val);
1665 free(val);
1666
1667 if (oper != SUBSCRIPTION_REGISTRATION) {
1668 debug_print(ctx, 1, "User session %s not in state for "
1669 "enrollment failure", session_id);
1670 return NULL;
1671 }
1672
1673 status = "Error occurred";
1674 spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
1675 NULL);
1676 if (spp_node == NULL)
1677 return NULL;
1678 node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
1679 xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
1680 "Credentials cannot be provisioned at this time");
1681 db_remove_session(ctx, user, realm, session_id);
1682
1683 return spp_node;
1684}
1685
1686
1687static xml_node_t * hs20_spp_post_dev_data(struct hs20_svc *ctx,
1688 xml_node_t *node,
1689 const char *user,
1690 const char *realm,
1691 const char *session_id,
1692 int dmacc)
1693{
1694 const char *req_reason;
1695 char *redirect_uri = NULL;
1696 char *req_reason_buf = NULL;
1697 char str[200];
1698 xml_node_t *ret = NULL, *devinfo = NULL, *devdetail = NULL;
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001699 xml_node_t *mo, *macaddr;
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001700 char *version;
1701 int valid;
1702 char *supp, *pos;
1703 char *err;
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001704 u8 wifi_mac_addr[ETH_ALEN];
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001705
1706 version = xml_node_get_attr_value_ns(ctx->xml, node, SPP_NS_URI,
1707 "sppVersion");
1708 if (version == NULL || strstr(version, "1.0") == NULL) {
1709 ret = build_post_dev_data_response(
1710 ctx, NULL, session_id, "Error occurred",
1711 "SPP version not supported");
1712 hs20_eventlog_node(ctx, user, realm, session_id,
1713 "Unsupported sppVersion", ret);
1714 xml_node_get_attr_value_free(ctx->xml, version);
1715 return ret;
1716 }
1717 xml_node_get_attr_value_free(ctx->xml, version);
1718
1719 mo = get_node(ctx->xml, node, "supportedMOList");
1720 if (mo == NULL) {
1721 ret = build_post_dev_data_response(
1722 ctx, NULL, session_id, "Error occurred",
1723 "Other");
1724 hs20_eventlog_node(ctx, user, realm, session_id,
1725 "No supportedMOList element", ret);
1726 return ret;
1727 }
1728 supp = xml_node_get_text(ctx->xml, mo);
1729 for (pos = supp; pos && *pos; pos++)
1730 *pos = tolower(*pos);
1731 if (supp == NULL ||
1732 strstr(supp, URN_OMA_DM_DEVINFO) == NULL ||
1733 strstr(supp, URN_OMA_DM_DEVDETAIL) == NULL ||
1734 strstr(supp, URN_HS20_PPS) == NULL) {
1735 xml_node_get_text_free(ctx->xml, supp);
1736 ret = build_post_dev_data_response(
1737 ctx, NULL, session_id, "Error occurred",
1738 "One or more mandatory MOs not supported");
1739 hs20_eventlog_node(ctx, user, realm, session_id,
1740 "Unsupported MOs", ret);
1741 return ret;
1742 }
1743 xml_node_get_text_free(ctx->xml, supp);
1744
1745 req_reason_buf = xml_node_get_attr_value(ctx->xml, node,
1746 "requestReason");
1747 if (req_reason_buf == NULL) {
1748 debug_print(ctx, 1, "No requestReason attribute");
1749 return NULL;
1750 }
1751 req_reason = req_reason_buf;
1752
1753 redirect_uri = xml_node_get_attr_value(ctx->xml, node, "redirectURI");
1754
1755 debug_print(ctx, 1, "requestReason: %s sessionID: %s redirectURI: %s",
1756 req_reason, session_id, redirect_uri);
1757 snprintf(str, sizeof(str), "sppPostDevData: requestReason=%s",
1758 req_reason);
1759 hs20_eventlog(ctx, user, realm, session_id, str, NULL);
1760
1761 devinfo = spp_get_mo(ctx, node, URN_OMA_DM_DEVINFO, &valid, &err);
1762 if (devinfo == NULL) {
1763 ret = build_post_dev_data_response(ctx, NULL, session_id,
1764 "Error occurred", "Other");
1765 hs20_eventlog_node(ctx, user, realm, session_id,
1766 "No DevInfo moContainer in sppPostDevData",
1767 ret);
1768 os_free(err);
1769 goto out;
1770 }
1771
1772 hs20_eventlog_node(ctx, user, realm, session_id,
1773 "Received DevInfo MO", devinfo);
1774 if (valid == 0) {
1775 hs20_eventlog(ctx, user, realm, session_id,
1776 "OMA-DM DDF DTD validation errors in DevInfo MO",
1777 err);
1778 ret = build_post_dev_data_response(ctx, NULL, session_id,
1779 "Error occurred", "Other");
1780 os_free(err);
1781 goto out;
1782 }
1783 os_free(err);
1784 if (user)
1785 db_update_mo(ctx, user, realm, "devinfo", devinfo);
1786
1787 devdetail = spp_get_mo(ctx, node, URN_OMA_DM_DEVDETAIL, &valid, &err);
1788 if (devdetail == NULL) {
1789 ret = build_post_dev_data_response(ctx, NULL, session_id,
1790 "Error occurred", "Other");
1791 hs20_eventlog_node(ctx, user, realm, session_id,
1792 "No DevDetail moContainer in sppPostDevData",
1793 ret);
1794 os_free(err);
1795 goto out;
1796 }
1797
1798 hs20_eventlog_node(ctx, user, realm, session_id,
1799 "Received DevDetail MO", devdetail);
1800 if (valid == 0) {
1801 hs20_eventlog(ctx, user, realm, session_id,
1802 "OMA-DM DDF DTD validation errors "
1803 "in DevDetail MO", err);
1804 ret = build_post_dev_data_response(ctx, NULL, session_id,
1805 "Error occurred", "Other");
1806 os_free(err);
1807 goto out;
1808 }
1809 os_free(err);
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001810
1811 os_memset(wifi_mac_addr, 0, ETH_ALEN);
1812 macaddr = get_node(ctx->xml, devdetail,
1813 "Ext/org.wi-fi/Wi-Fi/Wi-FiMACAddress");
1814 if (macaddr) {
1815 char *addr, buf[50];
1816
1817 addr = xml_node_get_text(ctx->xml, macaddr);
1818 if (addr && hwaddr_compact_aton(addr, wifi_mac_addr) == 0) {
1819 snprintf(buf, sizeof(buf), "DevDetail MAC address: "
1820 MACSTR, MAC2STR(wifi_mac_addr));
1821 hs20_eventlog(ctx, user, realm, session_id, buf, NULL);
1822 xml_node_get_text_free(ctx->xml, addr);
1823 } else {
1824 hs20_eventlog(ctx, user, realm, session_id,
1825 "Could not extract MAC address from DevDetail",
1826 NULL);
1827 }
1828 } else {
1829 hs20_eventlog(ctx, user, realm, session_id,
1830 "No MAC address in DevDetail", NULL);
1831 }
1832
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001833 if (user)
1834 db_update_mo(ctx, user, realm, "devdetail", devdetail);
1835
1836 if (user)
1837 mo = spp_get_mo(ctx, node, URN_HS20_PPS, &valid, &err);
1838 else {
1839 mo = NULL;
1840 err = NULL;
1841 }
1842 if (user && mo) {
1843 hs20_eventlog_node(ctx, user, realm, session_id,
1844 "Received PPS MO", mo);
1845 if (valid == 0) {
1846 hs20_eventlog(ctx, user, realm, session_id,
1847 "OMA-DM DDF DTD validation errors "
1848 "in PPS MO", err);
1849 xml_node_get_attr_value_free(ctx->xml, redirect_uri);
1850 os_free(err);
1851 return build_post_dev_data_response(
1852 ctx, NULL, session_id,
1853 "Error occurred", "Other");
1854 }
1855 db_update_mo(ctx, user, realm, "pps", mo);
1856 db_update_val(ctx, user, realm, "fetch_pps", "0", dmacc);
1857 xml_node_free(ctx->xml, mo);
1858 }
1859 os_free(err);
1860
1861 if (user && !mo) {
1862 char *fetch;
1863 int fetch_pps;
1864
1865 fetch = db_get_val(ctx, user, realm, "fetch_pps", dmacc);
1866 fetch_pps = fetch ? atoi(fetch) : 0;
1867 free(fetch);
1868
1869 if (fetch_pps) {
1870 enum hs20_session_operation oper;
1871 if (strcasecmp(req_reason, "Subscription remediation")
1872 == 0)
1873 oper = CONTINUE_SUBSCRIPTION_REMEDIATION;
1874 else if (strcasecmp(req_reason, "Policy update") == 0)
1875 oper = CONTINUE_POLICY_UPDATE;
1876 else
1877 oper = NO_OPERATION;
1878 if (db_add_session(ctx, user, realm, session_id, NULL,
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001879 NULL, oper, NULL) < 0)
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001880 goto out;
1881
1882 ret = spp_exec_upload_mo(ctx, session_id,
1883 URN_HS20_PPS);
1884 hs20_eventlog_node(ctx, user, realm, session_id,
1885 "request PPS MO upload",
1886 ret);
1887 goto out;
1888 }
1889 }
1890
1891 if (user && strcasecmp(req_reason, "MO upload") == 0) {
1892 char *val = db_get_session_val(ctx, user, realm, session_id,
1893 "operation");
1894 enum hs20_session_operation oper;
1895 if (!val) {
1896 debug_print(ctx, 1, "No session %s found to continue",
1897 session_id);
1898 goto out;
1899 }
1900 oper = atoi(val);
1901 free(val);
1902 if (oper == CONTINUE_SUBSCRIPTION_REMEDIATION)
1903 req_reason = "Subscription remediation";
1904 else if (oper == CONTINUE_POLICY_UPDATE)
1905 req_reason = "Policy update";
1906 else {
1907 debug_print(ctx, 1,
1908 "No pending operation in session %s",
1909 session_id);
1910 goto out;
1911 }
1912 }
1913
1914 if (strcasecmp(req_reason, "Subscription registration") == 0) {
1915 ret = hs20_subscription_registration(ctx, realm, session_id,
Hai Shalom39ba6fc2019-01-22 12:40:38 -08001916 redirect_uri,
1917 wifi_mac_addr);
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001918 hs20_eventlog_node(ctx, user, realm, session_id,
1919 "subscription registration response",
1920 ret);
1921 goto out;
1922 }
1923 if (user && strcasecmp(req_reason, "Subscription remediation") == 0) {
1924 ret = hs20_subscription_remediation(ctx, user, realm,
1925 session_id, dmacc,
1926 redirect_uri);
1927 hs20_eventlog_node(ctx, user, realm, session_id,
1928 "subscription remediation response",
1929 ret);
1930 goto out;
1931 }
1932 if (user && strcasecmp(req_reason, "Policy update") == 0) {
1933 ret = hs20_policy_update(ctx, user, realm, session_id, dmacc);
1934 hs20_eventlog_node(ctx, user, realm, session_id,
1935 "policy update response",
1936 ret);
1937 goto out;
1938 }
1939
1940 if (strcasecmp(req_reason, "User input completed") == 0) {
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08001941 db_add_session_devinfo(ctx, session_id, devinfo);
1942 db_add_session_devdetail(ctx, session_id, devdetail);
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001943 ret = hs20_user_input_complete(ctx, user, realm, dmacc,
1944 session_id);
1945 hs20_eventlog_node(ctx, user, realm, session_id,
1946 "user input completed response", ret);
1947 goto out;
1948 }
1949
1950 if (strcasecmp(req_reason, "Certificate enrollment completed") == 0) {
1951 ret = hs20_cert_enroll_completed(ctx, user, realm, dmacc,
1952 session_id);
1953 hs20_eventlog_node(ctx, user, realm, session_id,
1954 "certificate enrollment response", ret);
1955 goto out;
1956 }
1957
1958 if (strcasecmp(req_reason, "Certificate enrollment failed") == 0) {
1959 ret = hs20_cert_enroll_failed(ctx, user, realm, dmacc,
1960 session_id);
1961 hs20_eventlog_node(ctx, user, realm, session_id,
1962 "certificate enrollment failed response",
1963 ret);
1964 goto out;
1965 }
1966
1967 debug_print(ctx, 1, "Unsupported requestReason '%s' user '%s'",
1968 req_reason, user);
1969out:
1970 xml_node_get_attr_value_free(ctx->xml, req_reason_buf);
1971 xml_node_get_attr_value_free(ctx->xml, redirect_uri);
1972 if (devinfo)
1973 xml_node_free(ctx->xml, devinfo);
1974 if (devdetail)
1975 xml_node_free(ctx->xml, devdetail);
1976 return ret;
1977}
1978
1979
1980static xml_node_t * build_spp_exchange_complete(struct hs20_svc *ctx,
1981 const char *session_id,
1982 const char *status,
1983 const char *error_code)
1984{
1985 xml_namespace_t *ns;
1986 xml_node_t *spp_node, *node;
1987
1988 spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
1989 "sppExchangeComplete");
1990
1991
1992 xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
1993 xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id);
1994 xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", status);
1995
1996 if (error_code) {
1997 node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
1998 xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
1999 error_code);
2000 }
2001
2002 return spp_node;
2003}
2004
2005
2006static int add_subscription(struct hs20_svc *ctx, const char *session_id)
2007{
2008 char *user, *realm, *pw, *pw_mm, *pps, *str;
2009 char *sql;
2010 int ret = -1;
2011 char *free_account;
2012 int free_acc;
2013 char *type;
2014 int cert = 0;
2015 char *cert_pem, *fingerprint;
2016
2017 user = db_get_session_val(ctx, NULL, NULL, session_id, "user");
2018 realm = db_get_session_val(ctx, NULL, NULL, session_id, "realm");
2019 pw = db_get_session_val(ctx, NULL, NULL, session_id, "password");
2020 pw_mm = db_get_session_val(ctx, NULL, NULL, session_id,
2021 "machine_managed");
2022 pps = db_get_session_val(ctx, NULL, NULL, session_id, "pps");
2023 cert_pem = db_get_session_val(ctx, NULL, NULL, session_id, "cert_pem");
2024 fingerprint = db_get_session_val(ctx, NULL, NULL, session_id, "cert");
2025 type = db_get_session_val(ctx, NULL, NULL, session_id, "type");
2026 if (type && strcmp(type, "cert") == 0)
2027 cert = 1;
2028 free(type);
2029
2030 if (!user || !realm || !pw) {
2031 debug_print(ctx, 1, "Could not find session info from DB for "
2032 "the new subscription");
2033 goto out;
2034 }
2035
2036 free_account = db_get_osu_config_val(ctx, realm, "free_account");
2037 free_acc = free_account && strcmp(free_account, user) == 0;
2038 free(free_account);
2039
2040 debug_print(ctx, 1,
2041 "New subscription: user='%s' realm='%s' free_acc=%d",
2042 user, realm, free_acc);
2043 debug_print(ctx, 1, "New subscription: pps='%s'", pps);
2044
2045 sql = sqlite3_mprintf("UPDATE eventlog SET user=%Q, realm=%Q WHERE "
2046 "sessionid=%Q AND (user='' OR user IS NULL)",
2047 user, realm, session_id);
2048 if (sql) {
2049 debug_print(ctx, 1, "DB: %s", sql);
2050 if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
2051 debug_print(ctx, 1, "Failed to update eventlog in "
2052 "sqlite database: %s",
2053 sqlite3_errmsg(ctx->db));
2054 }
2055 sqlite3_free(sql);
2056 }
2057
2058 if (free_acc) {
2059 hs20_eventlog(ctx, user, realm, session_id,
2060 "completed shared free account registration",
2061 NULL);
2062 ret = 0;
2063 goto out;
2064 }
2065
Hai Shalom39ba6fc2019-01-22 12:40:38 -08002066 str = db_get_session_val(ctx, NULL, NULL, session_id, "mac_addr");
2067
2068 sql = sqlite3_mprintf("INSERT INTO users(identity,realm,phase2,methods,cert,cert_pem,machine_managed,mac_addr) VALUES (%Q,%Q,1,%Q,%Q,%Q,%d,%Q)",
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07002069 user, realm, cert ? "TLS" : "TTLS-MSCHAPV2",
2070 fingerprint ? fingerprint : "",
2071 cert_pem ? cert_pem : "",
Hai Shalom39ba6fc2019-01-22 12:40:38 -08002072 pw_mm && atoi(pw_mm) ? 1 : 0,
2073 str ? str : "");
2074 free(str);
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07002075 if (sql == NULL)
2076 goto out;
2077 debug_print(ctx, 1, "DB: %s", sql);
2078 if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
2079 debug_print(ctx, 1, "Failed to add user in sqlite database: %s",
2080 sqlite3_errmsg(ctx->db));
2081 sqlite3_free(sql);
2082 goto out;
2083 }
2084 sqlite3_free(sql);
2085
2086 if (cert)
2087 ret = 0;
2088 else
2089 ret = update_password(ctx, user, realm, pw, 0);
2090 if (ret < 0) {
2091 sql = sqlite3_mprintf("DELETE FROM users WHERE identity=%Q AND "
2092 "realm=%Q AND phase2=1",
2093 user, realm);
2094 if (sql) {
2095 debug_print(ctx, 1, "DB: %s", sql);
2096 sqlite3_exec(ctx->db, sql, NULL, NULL, NULL);
2097 sqlite3_free(sql);
2098 }
2099 }
2100
2101 if (pps)
2102 db_update_mo_str(ctx, user, realm, "pps", pps);
2103
2104 str = db_get_session_val(ctx, NULL, NULL, session_id, "devinfo");
2105 if (str) {
2106 db_update_mo_str(ctx, user, realm, "devinfo", str);
2107 free(str);
2108 }
2109
2110 str = db_get_session_val(ctx, NULL, NULL, session_id, "devdetail");
2111 if (str) {
2112 db_update_mo_str(ctx, user, realm, "devdetail", str);
2113 free(str);
2114 }
2115
Hai Shalom39ba6fc2019-01-22 12:40:38 -08002116 if (cert && user) {
2117 const char *serialnum;
2118
2119 str = db_get_session_val(ctx, NULL, NULL, session_id,
2120 "mac_addr");
2121
2122 if (os_strncmp(user, "cert-", 5) == 0)
2123 serialnum = user + 5;
2124 else
2125 serialnum = "";
2126 sql = sqlite3_mprintf("INSERT OR REPLACE INTO cert_enroll (mac_addr,user,realm,serialnum) VALUES(%Q,%Q,%Q,%Q)",
2127 str ? str : "", user, realm ? realm : "",
2128 serialnum);
2129 free(str);
2130 if (sql) {
2131 debug_print(ctx, 1, "DB: %s", sql);
2132 if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) !=
2133 SQLITE_OK) {
2134 debug_print(ctx, 1,
2135 "Failed to add cert_enroll entry into sqlite database: %s",
2136 sqlite3_errmsg(ctx->db));
2137 }
2138 sqlite3_free(sql);
2139 }
2140 }
2141
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07002142 if (ret == 0) {
2143 hs20_eventlog(ctx, user, realm, session_id,
2144 "completed subscription registration", NULL);
2145 }
2146
2147out:
2148 free(user);
2149 free(realm);
2150 free(pw);
2151 free(pw_mm);
2152 free(pps);
2153 free(cert_pem);
2154 free(fingerprint);
2155 return ret;
2156}
2157
2158
2159static xml_node_t * hs20_spp_update_response(struct hs20_svc *ctx,
2160 xml_node_t *node,
2161 const char *user,
2162 const char *realm,
2163 const char *session_id,
2164 int dmacc)
2165{
2166 char *status;
2167 xml_node_t *ret;
2168 char *val;
2169 enum hs20_session_operation oper;
2170
2171 status = xml_node_get_attr_value_ns(ctx->xml, node, SPP_NS_URI,
2172 "sppStatus");
2173 if (status == NULL) {
2174 debug_print(ctx, 1, "No sppStatus attribute");
2175 return NULL;
2176 }
2177
2178 debug_print(ctx, 1, "sppUpdateResponse: sppStatus: %s sessionID: %s",
2179 status, session_id);
2180
2181 val = db_get_session_val(ctx, user, realm, session_id, "operation");
2182 if (!val) {
2183 debug_print(ctx, 1,
2184 "No session active for user: %s sessionID: %s",
2185 user, session_id);
2186 oper = NO_OPERATION;
2187 } else
2188 oper = atoi(val);
2189
2190 if (strcasecmp(status, "OK") == 0) {
2191 char *new_pw = NULL;
2192
2193 xml_node_get_attr_value_free(ctx->xml, status);
2194
2195 if (oper == USER_REMEDIATION) {
2196 new_pw = db_get_session_val(ctx, user, realm,
2197 session_id, "password");
2198 if (new_pw == NULL || strlen(new_pw) == 0) {
2199 free(new_pw);
2200 ret = build_spp_exchange_complete(
2201 ctx, session_id, "Error occurred",
2202 "Other");
2203 hs20_eventlog_node(ctx, user, realm,
2204 session_id, "No password "
2205 "had been assigned for "
2206 "session", ret);
2207 db_remove_session(ctx, user, realm, session_id);
2208 return ret;
2209 }
2210 oper = UPDATE_PASSWORD;
2211 }
2212 if (oper == UPDATE_PASSWORD) {
2213 if (!new_pw) {
2214 new_pw = db_get_session_val(ctx, user, realm,
2215 session_id,
2216 "password");
2217 if (!new_pw) {
2218 db_remove_session(ctx, user, realm,
2219 session_id);
2220 return NULL;
2221 }
2222 }
2223 debug_print(ctx, 1, "Update user '%s' password in DB",
2224 user);
2225 if (update_password(ctx, user, realm, new_pw, dmacc) <
2226 0) {
2227 debug_print(ctx, 1, "Failed to update user "
2228 "'%s' password in DB", user);
2229 ret = build_spp_exchange_complete(
2230 ctx, session_id, "Error occurred",
2231 "Other");
2232 hs20_eventlog_node(ctx, user, realm,
2233 session_id, "Failed to "
2234 "update database", ret);
2235 db_remove_session(ctx, user, realm, session_id);
2236 return ret;
2237 }
2238 hs20_eventlog(ctx, user, realm,
2239 session_id, "Updated user password "
2240 "in database", NULL);
2241 }
2242 if (oper == SUBSCRIPTION_REGISTRATION) {
2243 if (add_subscription(ctx, session_id) < 0) {
2244 debug_print(ctx, 1, "Failed to add "
2245 "subscription into DB");
2246 ret = build_spp_exchange_complete(
2247 ctx, session_id, "Error occurred",
2248 "Other");
2249 hs20_eventlog_node(ctx, user, realm,
2250 session_id, "Failed to "
2251 "update database", ret);
2252 db_remove_session(ctx, user, realm, session_id);
2253 return ret;
2254 }
2255 }
2256 if (oper == POLICY_REMEDIATION || oper == POLICY_UPDATE) {
2257 char *val;
2258 val = db_get_val(ctx, user, realm, "remediation",
2259 dmacc);
2260 if (val && strcmp(val, "policy") == 0)
2261 db_update_val(ctx, user, realm, "remediation",
2262 "", dmacc);
2263 free(val);
2264 }
Hai Shalom39ba6fc2019-01-22 12:40:38 -08002265 if (oper == POLICY_UPDATE)
2266 db_update_val(ctx, user, realm, "polupd_done", "1",
2267 dmacc);
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07002268 ret = build_spp_exchange_complete(
2269 ctx, session_id,
2270 "Exchange complete, release TLS connection", NULL);
2271 hs20_eventlog_node(ctx, user, realm, session_id,
2272 "Exchange completed", ret);
2273 db_remove_session(ctx, user, realm, session_id);
2274 return ret;
2275 }
2276
2277 ret = build_spp_exchange_complete(ctx, session_id, "Error occurred",
2278 "Other");
2279 hs20_eventlog_node(ctx, user, realm, session_id, "Error occurred", ret);
2280 db_remove_session(ctx, user, realm, session_id);
2281 xml_node_get_attr_value_free(ctx->xml, status);
2282 return ret;
2283}
2284
2285
2286#define SPP_SESSION_ID_LEN 16
2287
2288static char * gen_spp_session_id(void)
2289{
2290 FILE *f;
2291 int i;
2292 char *session;
2293
2294 session = os_malloc(SPP_SESSION_ID_LEN * 2 + 1);
2295 if (session == NULL)
2296 return NULL;
2297
2298 f = fopen("/dev/urandom", "r");
2299 if (f == NULL) {
2300 os_free(session);
2301 return NULL;
2302 }
2303 for (i = 0; i < SPP_SESSION_ID_LEN; i++)
2304 os_snprintf(session + i * 2, 3, "%02x", fgetc(f));
2305
2306 fclose(f);
2307 return session;
2308}
2309
2310xml_node_t * hs20_spp_server_process(struct hs20_svc *ctx, xml_node_t *node,
2311 const char *auth_user,
2312 const char *auth_realm, int dmacc)
2313{
2314 xml_node_t *ret = NULL;
2315 char *session_id;
2316 const char *op_name;
2317 char *xml_err;
2318 char fname[200];
2319
2320 debug_dump_node(ctx, "received request", node);
2321
2322 if (!dmacc && auth_user && auth_realm) {
2323 char *real;
2324 real = db_get_val(ctx, auth_user, auth_realm, "identity", 0);
2325 if (!real) {
2326 real = db_get_val(ctx, auth_user, auth_realm,
2327 "identity", 1);
2328 if (real)
2329 dmacc = 1;
2330 }
2331 os_free(real);
2332 }
2333
2334 snprintf(fname, sizeof(fname), "%s/spp/spp.xsd", ctx->root_dir);
2335 if (xml_validate(ctx->xml, node, fname, &xml_err) < 0) {
2336 /*
2337 * We may not be able to extract the sessionID from invalid
2338 * input, but well, we can try.
2339 */
2340 session_id = xml_node_get_attr_value_ns(ctx->xml, node,
2341 SPP_NS_URI,
2342 "sessionID");
Dmitry Shmidt912c6ec2015-03-30 13:16:51 -07002343 debug_print(ctx, 1,
2344 "SPP message failed validation, xsd file: %s xml-error: %s",
2345 fname, xml_err);
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07002346 hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
2347 "SPP message failed validation", node);
2348 hs20_eventlog(ctx, auth_user, auth_realm, session_id,
2349 "Validation errors", xml_err);
2350 os_free(xml_err);
2351 xml_node_get_attr_value_free(ctx->xml, session_id);
2352 /* TODO: what to return here? */
2353 ret = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
2354 "SppValidationError");
2355 return ret;
2356 }
2357
2358 session_id = xml_node_get_attr_value_ns(ctx->xml, node, SPP_NS_URI,
2359 "sessionID");
2360 if (session_id) {
2361 char *tmp;
2362 debug_print(ctx, 1, "Received sessionID %s", session_id);
2363 tmp = os_strdup(session_id);
2364 xml_node_get_attr_value_free(ctx->xml, session_id);
2365 if (tmp == NULL)
2366 return NULL;
2367 session_id = tmp;
2368 } else {
2369 session_id = gen_spp_session_id();
2370 if (session_id == NULL) {
2371 debug_print(ctx, 1, "Failed to generate sessionID");
2372 return NULL;
2373 }
2374 debug_print(ctx, 1, "Generated sessionID %s", session_id);
2375 }
2376
2377 op_name = xml_node_get_localname(ctx->xml, node);
2378 if (op_name == NULL) {
2379 debug_print(ctx, 1, "Could not get op_name");
2380 return NULL;
2381 }
2382
2383 if (strcmp(op_name, "sppPostDevData") == 0) {
2384 hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
2385 "sppPostDevData received and validated",
2386 node);
2387 ret = hs20_spp_post_dev_data(ctx, node, auth_user, auth_realm,
2388 session_id, dmacc);
2389 } else if (strcmp(op_name, "sppUpdateResponse") == 0) {
2390 hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
2391 "sppUpdateResponse received and validated",
2392 node);
2393 ret = hs20_spp_update_response(ctx, node, auth_user,
2394 auth_realm, session_id, dmacc);
2395 } else {
2396 hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
2397 "Unsupported SPP message received and "
2398 "validated", node);
2399 debug_print(ctx, 1, "Unsupported operation '%s'", op_name);
2400 /* TODO: what to return here? */
2401 ret = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
2402 "SppUnknownCommandError");
2403 }
2404 os_free(session_id);
2405
2406 if (ret == NULL) {
2407 /* TODO: what to return here? */
2408 ret = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
2409 "SppInternalError");
2410 }
2411
2412 return ret;
2413}
2414
2415
2416int hs20_spp_server_init(struct hs20_svc *ctx)
2417{
2418 char fname[200];
2419 ctx->db = NULL;
2420 snprintf(fname, sizeof(fname), "%s/AS/DB/eap_user.db", ctx->root_dir);
2421 if (sqlite3_open(fname, &ctx->db)) {
2422 printf("Failed to open sqlite database: %s\n",
2423 sqlite3_errmsg(ctx->db));
2424 sqlite3_close(ctx->db);
2425 return -1;
2426 }
2427
2428 return 0;
2429}
2430
2431
2432void hs20_spp_server_deinit(struct hs20_svc *ctx)
2433{
2434 sqlite3_close(ctx->db);
2435 ctx->db = NULL;
2436}