2013-03-28 09:27:27 +01:00
/*
* Hotspot 2.0 SPP server
* Copyright ( c ) 2012 - 2013 , Qualcomm Atheros , Inc .
*
* This software may be distributed under the terms of the BSD license .
* See README for more details .
*/
# include <stdlib.h>
# include <stdio.h>
# include <string.h>
# include <ctype.h>
# include <time.h>
# include <errno.h>
# include <sqlite3.h>
# include "common.h"
# include "base64.h"
# include "md5_i.h"
# include "xml-utils.h"
# include "spp_server.h"
# define SPP_NS_URI "http: //www.wi-fi.org/specifications/hotspot2dot0/v1.0/spp"
# define URN_OMA_DM_DEVINFO "urn:oma:mo:oma-dm-devinfo:1.0"
# define URN_OMA_DM_DEVDETAIL "urn:oma:mo:oma-dm-devdetail:1.0"
# define URN_OMA_DM_DMACC "urn:oma:mo:oma-dm-dmacc:1.0"
# define URN_HS20_PPS "urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0"
/* TODO: timeout to expire sessions */
enum hs20_session_operation {
NO_OPERATION ,
UPDATE_PASSWORD ,
CONTINUE_SUBSCRIPTION_REMEDIATION ,
CONTINUE_POLICY_UPDATE ,
USER_REMEDIATION ,
SUBSCRIPTION_REGISTRATION ,
POLICY_REMEDIATION ,
POLICY_UPDATE ,
FREE_REMEDIATION ,
2018-12-03 23:11:37 +01:00
CLEAR_REMEDIATION ,
2018-12-04 13:11:39 +01:00
CERT_REENROLL ,
2013-03-28 09:27:27 +01:00
} ;
static char * db_get_session_val ( struct hs20_svc * ctx , const char * user ,
const char * realm , const char * session_id ,
const char * field ) ;
static char * db_get_osu_config_val ( struct hs20_svc * ctx , const char * realm ,
const char * field ) ;
static xml_node_t * build_policy ( struct hs20_svc * ctx , const char * user ,
const char * realm , int use_dmacc ) ;
2018-12-04 13:11:39 +01:00
static xml_node_t * spp_exec_get_certificate ( struct hs20_svc * ctx ,
const char * session_id ,
const char * user ,
const char * realm ,
int add_est_user ) ;
2013-03-28 09:27:27 +01:00
static int db_add_session ( struct hs20_svc * ctx ,
const char * user , const char * realm ,
const char * sessionid , const char * pw ,
const char * redirect_uri ,
2018-09-15 01:53:49 +02:00
enum hs20_session_operation operation ,
const u8 * mac_addr )
2013-03-28 09:27:27 +01:00
{
char * sql ;
int ret = 0 ;
2018-09-15 01:53:49 +02:00
char addr [ 20 ] ;
2013-03-28 09:27:27 +01:00
2018-09-15 01:53:49 +02:00
if ( mac_addr )
snprintf ( addr , sizeof ( addr ) , MACSTR , MAC2STR ( mac_addr ) ) ;
else
addr [ 0 ] = ' \0 ' ;
2013-03-28 09:27:27 +01:00
sql = sqlite3_mprintf ( " INSERT INTO sessions(timestamp,id,user,realm, "
2018-10-17 18:08:12 +02:00
" operation,password,redirect_uri,mac_addr,test) "
2013-03-28 09:27:27 +01:00
" VALUES "
" (strftime('%%Y-%%m-%%d %%H:%%M:%%f','now'), "
2018-10-17 18:08:12 +02:00
" %Q,%Q,%Q,%d,%Q,%Q,%Q,%Q) " ,
2013-03-28 09:27:27 +01:00
sessionid , user ? user : " " , realm ? realm : " " ,
operation , pw ? pw : " " ,
2018-09-15 01:53:49 +02:00
redirect_uri ? redirect_uri : " " ,
2018-10-17 18:08:12 +02:00
addr , ctx - > test ) ;
2013-03-28 09:27:27 +01:00
if ( sql = = NULL )
return - 1 ;
debug_print ( ctx , 1 , " DB: %s " , sql ) ;
if ( sqlite3_exec ( ctx - > db , sql , NULL , NULL , NULL ) ! = SQLITE_OK ) {
debug_print ( ctx , 1 , " Failed to add session entry into sqlite "
" database: %s " , sqlite3_errmsg ( ctx - > db ) ) ;
ret = - 1 ;
}
sqlite3_free ( sql ) ;
return ret ;
}
static void db_update_session_password ( struct hs20_svc * ctx , const char * user ,
const char * realm , const char * sessionid ,
const char * pw )
{
char * sql ;
sql = sqlite3_mprintf ( " UPDATE sessions SET password=%Q WHERE id=%Q AND "
" user=%Q AND realm=%Q " ,
pw , sessionid , user , realm ) ;
if ( sql = = NULL )
return ;
debug_print ( ctx , 1 , " DB: %s " , sql ) ;
if ( sqlite3_exec ( ctx - > db , sql , NULL , NULL , NULL ) ! = SQLITE_OK ) {
debug_print ( ctx , 1 , " Failed to update session password: %s " ,
sqlite3_errmsg ( ctx - > db ) ) ;
}
sqlite3_free ( sql ) ;
}
2014-04-10 12:46:45 +02:00
static void db_update_session_machine_managed ( struct hs20_svc * ctx ,
const char * user ,
const char * realm ,
const char * sessionid ,
const int pw_mm )
{
char * sql ;
sql = sqlite3_mprintf ( " UPDATE sessions SET machine_managed=%Q WHERE id=%Q AND user=%Q AND realm=%Q " ,
pw_mm ? " 1 " : " 0 " , sessionid , user , realm ) ;
if ( sql = = NULL )
return ;
debug_print ( ctx , 1 , " DB: %s " , sql ) ;
if ( sqlite3_exec ( ctx - > db , sql , NULL , NULL , NULL ) ! = SQLITE_OK ) {
debug_print ( ctx , 1 ,
" Failed to update session machine_managed: %s " ,
sqlite3_errmsg ( ctx - > db ) ) ;
}
sqlite3_free ( sql ) ;
}
2013-03-28 09:27:27 +01:00
static void db_add_session_pps ( struct hs20_svc * ctx , const char * user ,
const char * realm , const char * sessionid ,
xml_node_t * node )
{
char * str ;
char * sql ;
str = xml_node_to_str ( ctx - > xml , node ) ;
if ( str = = NULL )
return ;
sql = sqlite3_mprintf ( " UPDATE sessions SET pps=%Q WHERE id=%Q AND "
" user=%Q AND realm=%Q " ,
str , sessionid , user , realm ) ;
free ( str ) ;
if ( sql = = NULL )
return ;
debug_print ( ctx , 1 , " DB: %s " , sql ) ;
if ( sqlite3_exec ( ctx - > db , sql , NULL , NULL , NULL ) ! = SQLITE_OK ) {
debug_print ( ctx , 1 , " Failed to add session pps: %s " ,
sqlite3_errmsg ( ctx - > db ) ) ;
}
sqlite3_free ( sql ) ;
}
static void db_add_session_devinfo ( struct hs20_svc * ctx , const char * sessionid ,
xml_node_t * node )
{
char * str ;
char * sql ;
str = xml_node_to_str ( ctx - > xml , node ) ;
if ( str = = NULL )
return ;
sql = sqlite3_mprintf ( " UPDATE sessions SET devinfo=%Q WHERE id=%Q " ,
str , sessionid ) ;
free ( str ) ;
if ( sql = = NULL )
return ;
debug_print ( ctx , 1 , " DB: %s " , sql ) ;
if ( sqlite3_exec ( ctx - > db , sql , NULL , NULL , NULL ) ! = SQLITE_OK ) {
debug_print ( ctx , 1 , " Failed to add session devinfo: %s " ,
sqlite3_errmsg ( ctx - > db ) ) ;
}
sqlite3_free ( sql ) ;
}
static void db_add_session_devdetail ( struct hs20_svc * ctx ,
const char * sessionid ,
xml_node_t * node )
{
char * str ;
char * sql ;
str = xml_node_to_str ( ctx - > xml , node ) ;
if ( str = = NULL )
return ;
sql = sqlite3_mprintf ( " UPDATE sessions SET devdetail=%Q WHERE id=%Q " ,
str , sessionid ) ;
free ( str ) ;
if ( sql = = NULL )
return ;
debug_print ( ctx , 1 , " DB: %s " , sql ) ;
if ( sqlite3_exec ( ctx - > db , sql , NULL , NULL , NULL ) ! = SQLITE_OK ) {
debug_print ( ctx , 1 , " Failed to add session devdetail: %s " ,
sqlite3_errmsg ( ctx - > db ) ) ;
}
sqlite3_free ( sql ) ;
}
2018-12-15 17:00:12 +01:00
static void db_add_session_dmacc ( struct hs20_svc * ctx , const char * sessionid ,
const char * username , const char * password )
{
char * sql ;
sql = sqlite3_mprintf ( " UPDATE sessions SET osu_user=%Q, osu_password=%Q WHERE id=%Q " ,
username , password , sessionid ) ;
if ( ! sql )
return ;
debug_print ( ctx , 1 , " DB: %s " , sql ) ;
if ( sqlite3_exec ( ctx - > db , sql , NULL , NULL , NULL ) ! = SQLITE_OK ) {
debug_print ( ctx , 1 , " Failed to add session DMAcc: %s " ,
sqlite3_errmsg ( ctx - > db ) ) ;
}
sqlite3_free ( sql ) ;
}
static void db_add_session_eap_method ( struct hs20_svc * ctx ,
const char * sessionid ,
const char * method )
{
char * sql ;
sql = sqlite3_mprintf ( " UPDATE sessions SET eap_method=%Q WHERE id=%Q " ,
method , sessionid ) ;
if ( ! sql )
return ;
debug_print ( ctx , 1 , " DB: %s " , sql ) ;
if ( sqlite3_exec ( ctx - > db , sql , NULL , NULL , NULL ) ! = SQLITE_OK ) {
debug_print ( ctx , 1 , " Failed to add session EAP method: %s " ,
sqlite3_errmsg ( ctx - > db ) ) ;
}
sqlite3_free ( sql ) ;
}
static void db_add_session_id_hash ( struct hs20_svc * ctx , const char * sessionid ,
const char * id_hash )
{
char * sql ;
sql = sqlite3_mprintf ( " UPDATE sessions SET mobile_identifier_hash=%Q WHERE id=%Q " ,
id_hash , sessionid ) ;
if ( ! sql )
return ;
debug_print ( ctx , 1 , " DB: %s " , sql ) ;
if ( sqlite3_exec ( ctx - > db , sql , NULL , NULL , NULL ) ! = SQLITE_OK ) {
debug_print ( ctx , 1 , " Failed to add session ID hash: %s " ,
sqlite3_errmsg ( ctx - > db ) ) ;
}
sqlite3_free ( sql ) ;
}
2013-03-28 09:27:27 +01:00
static void db_remove_session ( struct hs20_svc * ctx ,
const char * user , const char * realm ,
const char * sessionid )
{
char * sql ;
if ( user = = NULL | | realm = = NULL ) {
sql = sqlite3_mprintf ( " DELETE FROM sessions WHERE "
" id=%Q " , sessionid ) ;
} else {
sql = sqlite3_mprintf ( " DELETE FROM sessions WHERE "
" user=%Q AND realm=%Q AND id=%Q " ,
user , realm , sessionid ) ;
}
if ( sql = = NULL )
return ;
debug_print ( ctx , 1 , " DB: %s " , sql ) ;
if ( sqlite3_exec ( ctx - > db , sql , NULL , NULL , NULL ) ! = SQLITE_OK ) {
debug_print ( ctx , 1 , " Failed to delete session entry from "
" sqlite database: %s " , sqlite3_errmsg ( ctx - > db ) ) ;
}
sqlite3_free ( sql ) ;
}
static void hs20_eventlog ( struct hs20_svc * ctx ,
const char * user , const char * realm ,
const char * sessionid , const char * notes ,
const char * dump )
{
char * sql ;
char * user_buf = NULL , * realm_buf = NULL ;
debug_print ( ctx , 1 , " eventlog: %s " , notes ) ;
if ( user = = NULL ) {
user_buf = db_get_session_val ( ctx , NULL , NULL , sessionid ,
" user " ) ;
user = user_buf ;
realm_buf = db_get_session_val ( ctx , NULL , NULL , sessionid ,
" realm " ) ;
realm = realm_buf ;
}
sql = sqlite3_mprintf ( " INSERT INTO eventlog "
" (user,realm,sessionid,timestamp,notes,dump,addr) "
" VALUES (%Q,%Q,%Q, "
" strftime('%%Y-%%m-%%d %%H:%%M:%%f','now'), "
" %Q,%Q,%Q) " ,
user , realm , sessionid , notes ,
dump ? dump : " " , ctx - > addr ? ctx - > addr : " " ) ;
free ( user_buf ) ;
free ( realm_buf ) ;
if ( sql = = NULL )
return ;
if ( sqlite3_exec ( ctx - > db , sql , NULL , NULL , NULL ) ! = SQLITE_OK ) {
debug_print ( ctx , 1 , " Failed to add eventlog entry into sqlite "
" database: %s " , sqlite3_errmsg ( ctx - > db ) ) ;
}
sqlite3_free ( sql ) ;
}
static void hs20_eventlog_node ( struct hs20_svc * ctx ,
const char * user , const char * realm ,
const char * sessionid , const char * notes ,
xml_node_t * node )
{
char * str ;
if ( node )
str = xml_node_to_str ( ctx - > xml , node ) ;
else
str = NULL ;
hs20_eventlog ( ctx , user , realm , sessionid , notes , str ) ;
free ( str ) ;
}
static void db_update_mo_str ( struct hs20_svc * ctx , const char * user ,
const char * realm , const char * name ,
const char * str )
{
char * sql ;
if ( user = = NULL | | realm = = NULL | | name = = NULL )
return ;
2018-12-03 22:45:32 +01:00
sql = sqlite3_mprintf ( " UPDATE users SET %s=%Q WHERE identity=%Q AND realm=%Q AND (phase2=1 OR methods='TLS') " ,
2013-03-28 09:27:27 +01:00
name , str , user , realm ) ;
if ( sql = = NULL )
return ;
debug_print ( ctx , 1 , " DB: %s " , sql ) ;
if ( sqlite3_exec ( ctx - > db , sql , NULL , NULL , NULL ) ! = SQLITE_OK ) {
debug_print ( ctx , 1 , " Failed to update user MO entry in sqlite "
" database: %s " , sqlite3_errmsg ( ctx - > db ) ) ;
}
sqlite3_free ( sql ) ;
}
static void db_update_mo ( struct hs20_svc * ctx , const char * user ,
const char * realm , const char * name , xml_node_t * mo )
{
char * str ;
str = xml_node_to_str ( ctx - > xml , mo ) ;
if ( str = = NULL )
return ;
db_update_mo_str ( ctx , user , realm , name , str ) ;
free ( str ) ;
}
static void add_text_node ( struct hs20_svc * ctx , xml_node_t * parent ,
const char * name , const char * value )
{
xml_node_create_text ( ctx - > xml , parent , NULL , name , value ? value : " " ) ;
}
static void add_text_node_conf ( struct hs20_svc * ctx , const char * realm ,
xml_node_t * parent , const char * name ,
const char * field )
{
char * val ;
val = db_get_osu_config_val ( ctx , realm , field ) ;
xml_node_create_text ( ctx - > xml , parent , NULL , name , val ? val : " " ) ;
os_free ( val ) ;
}
2018-10-17 18:08:12 +02:00
static void add_text_node_conf_corrupt ( struct hs20_svc * ctx , const char * realm ,
xml_node_t * parent , const char * name ,
const char * field )
{
char * val ;
val = db_get_osu_config_val ( ctx , realm , field ) ;
if ( val ) {
size_t len ;
len = os_strlen ( val ) ;
if ( len > 0 ) {
if ( val [ len - 1 ] = = ' 0 ' )
val [ len - 1 ] = ' 1 ' ;
else
val [ len - 1 ] = ' 0 ' ;
}
}
xml_node_create_text ( ctx - > xml , parent , NULL , name , val ? val : " " ) ;
os_free ( val ) ;
}
2013-03-28 09:27:27 +01:00
static int new_password ( char * buf , int buflen )
{
int i ;
if ( buflen < 1 )
return - 1 ;
buf [ buflen - 1 ] = ' \0 ' ;
if ( os_get_random ( ( unsigned char * ) buf , buflen - 1 ) < 0 )
return - 1 ;
for ( i = 0 ; i < buflen - 1 ; i + + ) {
unsigned char val = buf [ i ] ;
val % = 2 * 26 + 10 ;
if ( val < 26 )
buf [ i ] = ' a ' + val ;
else if ( val < 2 * 26 )
buf [ i ] = ' A ' + val - 26 ;
else
buf [ i ] = ' 0 ' + val - 2 * 26 ;
}
return 0 ;
}
struct get_db_field_data {
const char * field ;
char * value ;
} ;
static int get_db_field ( void * ctx , int argc , char * argv [ ] , char * col [ ] )
{
struct get_db_field_data * data = ctx ;
int i ;
for ( i = 0 ; i < argc ; i + + ) {
if ( os_strcmp ( col [ i ] , data - > field ) = = 0 & & argv [ i ] ) {
os_free ( data - > value ) ;
data - > value = os_strdup ( argv [ i ] ) ;
break ;
}
}
return 0 ;
}
static char * db_get_val ( struct hs20_svc * ctx , const char * user ,
const char * realm , const char * field , int dmacc )
{
char * cmd ;
struct get_db_field_data data ;
2018-12-03 22:45:32 +01:00
cmd = sqlite3_mprintf ( " SELECT %s FROM users WHERE %s=%Q AND realm=%Q AND (phase2=1 OR methods='TLS') " ,
2013-03-28 09:27:27 +01:00
field , dmacc ? " osu_user " : " identity " ,
user , realm ) ;
if ( cmd = = NULL )
return NULL ;
memset ( & data , 0 , sizeof ( data ) ) ;
data . field = field ;
if ( sqlite3_exec ( ctx - > db , cmd , get_db_field , & data , NULL ) ! = SQLITE_OK )
{
debug_print ( ctx , 1 , " Could not find user '%s' " , user ) ;
sqlite3_free ( cmd ) ;
return NULL ;
}
sqlite3_free ( cmd ) ;
debug_print ( ctx , 1 , " DB: user='%s' realm='%s' field='%s' dmacc=%d --> "
" value='%s' " , user , realm , field , dmacc , data . value ) ;
return data . value ;
}
static int db_update_val ( struct hs20_svc * ctx , const char * user ,
const char * realm , const char * field ,
const char * val , int dmacc )
{
char * cmd ;
int ret ;
2018-12-03 22:45:32 +01:00
cmd = sqlite3_mprintf ( " UPDATE users SET %s=%Q WHERE %s=%Q AND realm=%Q AND (phase2=1 OR methods='TLS') " ,
2013-03-28 09:27:27 +01:00
field , val , dmacc ? " osu_user " : " identity " , user ,
realm ) ;
if ( cmd = = NULL )
return - 1 ;
debug_print ( ctx , 1 , " DB: %s " , cmd ) ;
if ( sqlite3_exec ( ctx - > db , cmd , NULL , NULL , NULL ) ! = SQLITE_OK ) {
debug_print ( ctx , 1 ,
" Failed to update user in sqlite database: %s " ,
sqlite3_errmsg ( ctx - > db ) ) ;
ret = - 1 ;
} else {
debug_print ( ctx , 1 ,
" DB: user='%s' realm='%s' field='%s' set to '%s' " ,
user , realm , field , val ) ;
ret = 0 ;
}
sqlite3_free ( cmd ) ;
return ret ;
}
static char * db_get_session_val ( struct hs20_svc * ctx , const char * user ,
const char * realm , const char * session_id ,
const char * field )
{
char * cmd ;
struct get_db_field_data data ;
if ( user = = NULL | | realm = = NULL ) {
cmd = sqlite3_mprintf ( " SELECT %s FROM sessions WHERE "
" id=%Q " , field , session_id ) ;
} else {
cmd = sqlite3_mprintf ( " SELECT %s FROM sessions WHERE "
" user=%Q AND realm=%Q AND id=%Q " ,
field , user , realm , session_id ) ;
}
if ( cmd = = NULL )
return NULL ;
debug_print ( ctx , 1 , " DB: %s " , cmd ) ;
memset ( & data , 0 , sizeof ( data ) ) ;
data . field = field ;
if ( sqlite3_exec ( ctx - > db , cmd , get_db_field , & data , NULL ) ! = SQLITE_OK )
{
debug_print ( ctx , 1 , " DB: Could not find session %s: %s " ,
session_id , sqlite3_errmsg ( ctx - > db ) ) ;
sqlite3_free ( cmd ) ;
return NULL ;
}
sqlite3_free ( cmd ) ;
debug_print ( ctx , 1 , " DB: return '%s' " , data . value ) ;
return data . value ;
}
static int update_password ( struct hs20_svc * ctx , const char * user ,
const char * realm , const char * pw , int dmacc )
{
char * cmd ;
cmd = sqlite3_mprintf ( " UPDATE users SET password=%Q, "
" remediation='' "
" WHERE %s=%Q AND phase2=1 " ,
pw , dmacc ? " osu_user " : " identity " ,
user ) ;
if ( cmd = = NULL )
return - 1 ;
debug_print ( ctx , 1 , " DB: %s " , cmd ) ;
if ( sqlite3_exec ( ctx - > db , cmd , NULL , NULL , NULL ) ! = SQLITE_OK ) {
debug_print ( ctx , 1 , " Failed to update database for user '%s' " ,
user ) ;
}
sqlite3_free ( cmd ) ;
return 0 ;
}
2018-12-03 23:11:37 +01:00
static int clear_remediation ( struct hs20_svc * ctx , const char * user ,
const char * realm , int dmacc )
{
char * cmd ;
cmd = sqlite3_mprintf ( " UPDATE users SET remediation='' WHERE %s=%Q " ,
dmacc ? " osu_user " : " identity " ,
user ) ;
if ( cmd = = NULL )
return - 1 ;
debug_print ( ctx , 1 , " DB: %s " , cmd ) ;
if ( sqlite3_exec ( ctx - > db , cmd , NULL , NULL , NULL ) ! = SQLITE_OK ) {
debug_print ( ctx , 1 , " Failed to update database for user '%s' " ,
user ) ;
}
sqlite3_free ( cmd ) ;
return 0 ;
}
2013-03-28 09:27:27 +01:00
static int add_eap_ttls ( struct hs20_svc * ctx , xml_node_t * parent )
{
xml_node_t * node ;
node = xml_node_create ( ctx - > xml , parent , NULL , " EAPMethod " ) ;
if ( node = = NULL )
return - 1 ;
add_text_node ( ctx , node , " EAPType " , " 21 " ) ;
add_text_node ( ctx , node , " InnerMethod " , " MS-CHAP-V2 " ) ;
return 0 ;
}
static xml_node_t * build_username_password ( struct hs20_svc * ctx ,
xml_node_t * parent ,
const char * user , const char * pw )
{
xml_node_t * node ;
char * b64 ;
2018-12-15 17:00:12 +01:00
size_t len ;
2013-03-28 09:27:27 +01:00
node = xml_node_create ( ctx - > xml , parent , NULL , " UsernamePassword " ) ;
if ( node = = NULL )
return NULL ;
add_text_node ( ctx , node , " Username " , user ) ;
b64 = ( char * ) base64_encode ( ( unsigned char * ) pw , strlen ( pw ) , NULL ) ;
if ( b64 = = NULL )
return NULL ;
2018-12-15 17:00:12 +01:00
len = os_strlen ( b64 ) ;
if ( len > 0 & & b64 [ len - 1 ] = = ' \n ' )
b64 [ len - 1 ] = ' \0 ' ;
2013-03-28 09:27:27 +01:00
add_text_node ( ctx , node , " Password " , b64 ) ;
free ( b64 ) ;
return node ;
}
static int add_username_password ( struct hs20_svc * ctx , xml_node_t * cred ,
2018-10-08 17:07:00 +02:00
const char * user , const char * pw ,
int machine_managed )
2013-03-28 09:27:27 +01:00
{
xml_node_t * node ;
node = build_username_password ( ctx , cred , user , pw ) ;
if ( node = = NULL )
return - 1 ;
2018-10-08 17:07:00 +02:00
add_text_node ( ctx , node , " MachineManaged " ,
machine_managed ? " TRUE " : " FALSE " ) ;
2013-03-28 09:27:27 +01:00
add_text_node ( ctx , node , " SoftTokenApp " , " " ) ;
add_eap_ttls ( ctx , node ) ;
return 0 ;
}
static void add_creation_date ( struct hs20_svc * ctx , xml_node_t * cred )
{
char str [ 30 ] ;
time_t now ;
struct tm tm ;
time ( & now ) ;
gmtime_r ( & now , & tm ) ;
snprintf ( str , sizeof ( str ) , " %04u-%02u-%02uT%02u:%02u:%02uZ " ,
tm . tm_year + 1900 , tm . tm_mon + 1 , tm . tm_mday ,
tm . tm_hour , tm . tm_min , tm . tm_sec ) ;
xml_node_create_text ( ctx - > xml , cred , NULL , " CreationDate " , str ) ;
}
static xml_node_t * build_credential_pw ( struct hs20_svc * ctx ,
const char * user , const char * realm ,
2018-10-08 17:07:00 +02:00
const char * pw , int machine_managed )
2013-03-28 09:27:27 +01:00
{
xml_node_t * cred ;
cred = xml_node_create_root ( ctx - > xml , NULL , NULL , NULL , " Credential " ) ;
if ( cred = = NULL ) {
debug_print ( ctx , 1 , " Failed to create Credential node " ) ;
return NULL ;
}
add_creation_date ( ctx , cred ) ;
2018-10-08 17:07:00 +02:00
if ( add_username_password ( ctx , cred , user , pw , machine_managed ) < 0 ) {
2013-03-28 09:27:27 +01:00
xml_node_free ( ctx - > xml , cred ) ;
return NULL ;
}
add_text_node ( ctx , cred , " Realm " , realm ) ;
return cred ;
}
static xml_node_t * build_credential ( struct hs20_svc * ctx ,
const char * user , const char * realm ,
char * new_pw , size_t new_pw_len )
{
if ( new_password ( new_pw , new_pw_len ) < 0 )
return NULL ;
debug_print ( ctx , 1 , " Update password to '%s' " , new_pw ) ;
2018-10-08 17:07:00 +02:00
return build_credential_pw ( ctx , user , realm , new_pw , 1 ) ;
2013-03-28 09:27:27 +01:00
}
static xml_node_t * build_credential_cert ( struct hs20_svc * ctx ,
const char * user , const char * realm ,
const char * cert_fingerprint )
{
xml_node_t * cred , * cert ;
cred = xml_node_create_root ( ctx - > xml , NULL , NULL , NULL , " Credential " ) ;
if ( cred = = NULL ) {
debug_print ( ctx , 1 , " Failed to create Credential node " ) ;
return NULL ;
}
add_creation_date ( ctx , cred ) ;
cert = xml_node_create ( ctx - > xml , cred , NULL , " DigitalCertificate " ) ;
add_text_node ( ctx , cert , " CertificateType " , " x509v3 " ) ;
add_text_node ( ctx , cert , " CertSHA256Fingerprint " , cert_fingerprint ) ;
add_text_node ( ctx , cred , " Realm " , realm ) ;
return cred ;
}
static xml_node_t * build_post_dev_data_response ( struct hs20_svc * ctx ,
xml_namespace_t * * ret_ns ,
const char * session_id ,
const char * status ,
const char * error_code )
{
xml_node_t * spp_node = NULL ;
xml_namespace_t * ns ;
spp_node = xml_node_create_root ( ctx - > xml , SPP_NS_URI , " spp " , & ns ,
" sppPostDevDataResponse " ) ;
if ( spp_node = = NULL )
return NULL ;
if ( ret_ns )
* ret_ns = ns ;
xml_node_add_attr ( ctx - > xml , spp_node , ns , " sppVersion " , " 1.0 " ) ;
xml_node_add_attr ( ctx - > xml , spp_node , ns , " sessionID " , session_id ) ;
xml_node_add_attr ( ctx - > xml , spp_node , ns , " sppStatus " , status ) ;
if ( error_code ) {
xml_node_t * node ;
node = xml_node_create ( ctx - > xml , spp_node , ns , " sppError " ) ;
if ( node )
xml_node_add_attr ( ctx - > xml , node , NULL , " errorCode " ,
error_code ) ;
}
return spp_node ;
}
static int add_update_node ( struct hs20_svc * ctx , xml_node_t * spp_node ,
xml_namespace_t * ns , const char * uri ,
xml_node_t * upd_node )
{
xml_node_t * node , * tnds ;
char * str ;
tnds = mo_to_tnds ( ctx - > xml , upd_node , 0 , NULL , NULL ) ;
if ( ! tnds )
return - 1 ;
str = xml_node_to_str ( ctx - > xml , tnds ) ;
xml_node_free ( ctx - > xml , tnds ) ;
if ( str = = NULL )
return - 1 ;
node = xml_node_create_text ( ctx - > xml , spp_node , ns , " updateNode " , str ) ;
free ( str ) ;
xml_node_add_attr ( ctx - > xml , node , ns , " managementTreeURI " , uri ) ;
return 0 ;
}
2019-01-23 00:03:46 +01:00
static xml_node_t * read_subrem_file ( struct hs20_svc * ctx ,
const char * subrem_id ,
char * uri , size_t uri_size )
{
char fname [ 200 ] ;
char * buf , * buf2 , * pos ;
size_t len ;
xml_node_t * node ;
os_snprintf ( fname , sizeof ( fname ) , " %s/spp/subrem/%s " ,
ctx - > root_dir , subrem_id ) ;
debug_print ( ctx , 1 , " Use subrem file %s " , fname ) ;
buf = os_readfile ( fname , & len ) ;
if ( ! buf )
return NULL ;
buf2 = os_realloc ( buf , len + 1 ) ;
if ( ! buf2 ) {
os_free ( buf ) ;
return NULL ;
}
buf = buf2 ;
buf [ len ] = ' \0 ' ;
pos = os_strchr ( buf , ' \n ' ) ;
if ( ! pos ) {
os_free ( buf ) ;
return NULL ;
}
* pos + + = ' \0 ' ;
os_strlcpy ( uri , buf , uri_size ) ;
node = xml_node_from_buf ( ctx - > xml , pos ) ;
os_free ( buf ) ;
return node ;
}
2013-03-28 09:27:27 +01:00
static xml_node_t * build_sub_rem_resp ( struct hs20_svc * ctx ,
const char * user , const char * realm ,
const char * session_id ,
int machine_rem , int dmacc )
{
xml_namespace_t * ns ;
xml_node_t * spp_node , * cred ;
char buf [ 400 ] ;
char new_pw [ 33 ] ;
char * status ;
char * cert ;
cert = db_get_val ( ctx , user , realm , " cert " , dmacc ) ;
2018-12-04 13:12:44 +01:00
if ( cert & & cert [ 0 ] = = ' \0 ' ) {
os_free ( cert ) ;
2013-03-28 09:27:27 +01:00
cert = NULL ;
2018-12-04 13:12:44 +01:00
}
2013-03-28 09:27:27 +01:00
if ( cert ) {
2019-01-23 00:03:46 +01:00
char * subrem ;
/* No change needed in PPS MO unless specifically asked to */
2019-01-22 22:31:06 +01:00
cred = NULL ;
2019-01-23 00:03:46 +01:00
buf [ 0 ] = ' \0 ' ;
subrem = db_get_val ( ctx , user , realm , " subrem " , dmacc ) ;
if ( subrem & & subrem [ 0 ] ) {
cred = read_subrem_file ( ctx , subrem , buf , sizeof ( buf ) ) ;
if ( ! cred ) {
debug_print ( ctx , 1 ,
" Could not create updateNode from subrem file " ) ;
os_free ( subrem ) ;
os_free ( cert ) ;
return NULL ;
}
}
os_free ( subrem ) ;
2013-03-28 09:27:27 +01:00
} else {
2019-01-22 22:31:06 +01:00
char * real_user = NULL ;
2018-10-08 17:07:00 +02:00
char * pw ;
2019-01-22 22:31:06 +01:00
if ( dmacc ) {
real_user = db_get_val ( ctx , user , realm , " identity " ,
dmacc ) ;
if ( ! real_user ) {
debug_print ( ctx , 1 ,
" Could not find user identity for dmacc user '%s' " ,
user ) ;
return NULL ;
}
}
2018-10-08 17:07:00 +02:00
pw = db_get_session_val ( ctx , user , realm , session_id ,
" password " ) ;
if ( pw & & pw [ 0 ] ) {
debug_print ( ctx , 1 , " New password from the user: '%s' " ,
pw ) ;
snprintf ( new_pw , sizeof ( new_pw ) , " %s " , pw ) ;
free ( pw ) ;
cred = build_credential_pw ( ctx ,
real_user ? real_user : user ,
realm , new_pw , 0 ) ;
} else {
cred = build_credential ( ctx ,
real_user ? real_user : user ,
realm , new_pw , sizeof ( new_pw ) ) ;
}
2019-01-22 22:31:06 +01:00
free ( real_user ) ;
if ( ! cred ) {
debug_print ( ctx , 1 , " Could not build credential " ) ;
os_free ( cert ) ;
return NULL ;
}
2019-01-23 00:03:46 +01:00
snprintf ( buf , sizeof ( buf ) ,
" ./Wi-Fi/%s/PerProviderSubscription/Cred01/Credential " ,
realm ) ;
2013-03-28 09:27:27 +01:00
}
status = " Remediation complete, request sppUpdateResponse " ;
spp_node = build_post_dev_data_response ( ctx , & ns , session_id , status ,
NULL ) ;
if ( spp_node = = NULL ) {
debug_print ( ctx , 1 , " Could not build sppPostDevDataResponse " ) ;
2018-12-04 13:12:44 +01:00
os_free ( cert ) ;
2013-03-28 09:27:27 +01:00
return NULL ;
}
2019-01-22 22:31:06 +01:00
if ( ( cred & & add_update_node ( ctx , spp_node , ns , buf , cred ) < 0 ) | |
( ! cred & & ! xml_node_create ( ctx - > xml , spp_node , ns , " noMOUpdate " ) ) ) {
2013-03-28 09:27:27 +01:00
debug_print ( ctx , 1 , " Could not add update node " ) ;
xml_node_free ( ctx - > xml , spp_node ) ;
2018-12-04 13:12:44 +01:00
os_free ( cert ) ;
2013-03-28 09:27:27 +01:00
return NULL ;
}
hs20_eventlog_node ( ctx , user , realm , session_id ,
machine_rem ? " machine remediation " :
" user remediation " , cred ) ;
xml_node_free ( ctx - > xml , cred ) ;
if ( cert ) {
2018-12-03 23:11:37 +01:00
debug_print ( ctx , 1 , " Request DB remediation clearing on success notification (certificate credential) " ) ;
db_add_session ( ctx , user , realm , session_id , NULL , NULL ,
CLEAR_REMEDIATION , NULL ) ;
2013-03-28 09:27:27 +01:00
} else {
debug_print ( ctx , 1 , " Request DB password update on success "
" notification " ) ;
db_add_session ( ctx , user , realm , session_id , new_pw , NULL ,
2018-09-15 01:53:49 +02:00
UPDATE_PASSWORD , NULL ) ;
2013-03-28 09:27:27 +01:00
}
2018-12-04 13:12:44 +01:00
os_free ( cert ) ;
2013-03-28 09:27:27 +01:00
return spp_node ;
}
static xml_node_t * machine_remediation ( struct hs20_svc * ctx ,
const char * user ,
const char * realm ,
const char * session_id , int dmacc )
{
return build_sub_rem_resp ( ctx , user , realm , session_id , 1 , dmacc ) ;
}
2018-12-04 13:11:39 +01:00
static xml_node_t * cert_reenroll ( struct hs20_svc * ctx ,
const char * user ,
const char * realm ,
const char * session_id )
{
db_add_session ( ctx , user , realm , session_id , NULL , NULL ,
CERT_REENROLL , NULL ) ;
return spp_exec_get_certificate ( ctx , session_id , user , realm , 0 ) ;
}
2013-03-28 09:27:27 +01:00
static xml_node_t * policy_remediation ( struct hs20_svc * ctx ,
const char * user , const char * realm ,
const char * session_id , int dmacc )
{
xml_namespace_t * ns ;
xml_node_t * spp_node , * policy ;
char buf [ 400 ] ;
const char * status ;
hs20_eventlog ( ctx , user , realm , session_id ,
" requires policy remediation " , NULL ) ;
db_add_session ( ctx , user , realm , session_id , NULL , NULL ,
2018-09-15 01:53:49 +02:00
POLICY_REMEDIATION , NULL ) ;
2013-03-28 09:27:27 +01:00
policy = build_policy ( ctx , user , realm , dmacc ) ;
if ( ! policy ) {
return build_post_dev_data_response (
ctx , NULL , session_id ,
" No update available at this time " , NULL ) ;
}
status = " Remediation complete, request sppUpdateResponse " ;
spp_node = build_post_dev_data_response ( ctx , & ns , session_id , status ,
NULL ) ;
if ( spp_node = = NULL )
return NULL ;
snprintf ( buf , sizeof ( buf ) ,
2018-10-19 17:07:37 +02:00
" ./Wi-Fi/%s/PerProviderSubscription/Cred01/Policy " ,
2013-03-28 09:27:27 +01:00
realm ) ;
if ( add_update_node ( ctx , spp_node , ns , buf , policy ) < 0 ) {
xml_node_free ( ctx - > xml , spp_node ) ;
xml_node_free ( ctx - > xml , policy ) ;
return NULL ;
}
hs20_eventlog_node ( ctx , user , realm , session_id ,
" policy update (sub rem) " , policy ) ;
xml_node_free ( ctx - > xml , policy ) ;
return spp_node ;
}
static xml_node_t * browser_remediation ( struct hs20_svc * ctx ,
const char * session_id ,
const char * redirect_uri ,
const char * uri )
{
xml_namespace_t * ns ;
xml_node_t * spp_node , * exec_node ;
if ( redirect_uri = = NULL ) {
debug_print ( ctx , 1 , " Missing redirectURI attribute for user "
" remediation " ) ;
return NULL ;
}
debug_print ( ctx , 1 , " redirectURI %s " , redirect_uri ) ;
spp_node = build_post_dev_data_response ( ctx , & ns , session_id , " OK " ,
NULL ) ;
if ( spp_node = = NULL )
return NULL ;
exec_node = xml_node_create ( ctx - > xml , spp_node , ns , " exec " ) ;
xml_node_create_text ( ctx - > xml , exec_node , ns , " launchBrowserToURI " ,
uri ) ;
return spp_node ;
}
static xml_node_t * user_remediation ( struct hs20_svc * ctx , const char * user ,
const char * realm , const char * session_id ,
const char * redirect_uri )
{
char uri [ 300 ] , * val ;
hs20_eventlog ( ctx , user , realm , session_id ,
" requires user remediation " , NULL ) ;
val = db_get_osu_config_val ( ctx , realm , " remediation_url " ) ;
if ( val = = NULL )
return NULL ;
db_add_session ( ctx , user , realm , session_id , NULL , redirect_uri ,
2018-09-15 01:53:49 +02:00
USER_REMEDIATION , NULL ) ;
2013-03-28 09:27:27 +01:00
snprintf ( uri , sizeof ( uri ) , " %s%s " , val , session_id ) ;
os_free ( val ) ;
return browser_remediation ( ctx , session_id , redirect_uri , uri ) ;
}
static xml_node_t * free_remediation ( struct hs20_svc * ctx ,
const char * user , const char * realm ,
const char * session_id ,
const char * redirect_uri )
{
char uri [ 300 ] , * val ;
hs20_eventlog ( ctx , user , realm , session_id ,
" requires free/public account remediation " , NULL ) ;
val = db_get_osu_config_val ( ctx , realm , " free_remediation_url " ) ;
if ( val = = NULL )
return NULL ;
db_add_session ( ctx , user , realm , session_id , NULL , redirect_uri ,
2018-09-15 01:53:49 +02:00
FREE_REMEDIATION , NULL ) ;
2013-03-28 09:27:27 +01:00
snprintf ( uri , sizeof ( uri ) , " %s%s " , val , session_id ) ;
os_free ( val ) ;
return browser_remediation ( ctx , session_id , redirect_uri , uri ) ;
}
static xml_node_t * no_sub_rem ( struct hs20_svc * ctx ,
const char * user , const char * realm ,
const char * session_id )
{
const char * status ;
hs20_eventlog ( ctx , user , realm , session_id ,
" no subscription mediation available " , NULL ) ;
status = " No update available at this time " ;
return build_post_dev_data_response ( ctx , NULL , session_id , status ,
NULL ) ;
}
static xml_node_t * hs20_subscription_remediation ( struct hs20_svc * ctx ,
const char * user ,
const char * realm ,
const char * session_id ,
int dmacc ,
const char * redirect_uri )
{
char * type , * identity ;
xml_node_t * ret ;
char * free_account ;
identity = db_get_val ( ctx , user , realm , " identity " , dmacc ) ;
if ( identity = = NULL | | strlen ( identity ) = = 0 ) {
hs20_eventlog ( ctx , user , realm , session_id ,
" user not found in database for remediation " ,
NULL ) ;
os_free ( identity ) ;
return build_post_dev_data_response ( ctx , NULL , session_id ,
" Error occurred " ,
" Not found " ) ;
}
os_free ( identity ) ;
free_account = db_get_osu_config_val ( ctx , realm , " free_account " ) ;
if ( free_account & & strcmp ( free_account , user ) = = 0 ) {
free ( free_account ) ;
return no_sub_rem ( ctx , user , realm , session_id ) ;
}
free ( free_account ) ;
type = db_get_val ( ctx , user , realm , " remediation " , dmacc ) ;
if ( type & & strcmp ( type , " free " ) ! = 0 ) {
char * val ;
int shared = 0 ;
val = db_get_val ( ctx , user , realm , " shared " , dmacc ) ;
if ( val )
shared = atoi ( val ) ;
free ( val ) ;
if ( shared ) {
free ( type ) ;
return no_sub_rem ( ctx , user , realm , session_id ) ;
}
}
if ( type & & strcmp ( type , " user " ) = = 0 )
ret = user_remediation ( ctx , user , realm , session_id ,
redirect_uri ) ;
else if ( type & & strcmp ( type , " free " ) = = 0 )
ret = free_remediation ( ctx , user , realm , session_id ,
redirect_uri ) ;
else if ( type & & strcmp ( type , " policy " ) = = 0 )
ret = policy_remediation ( ctx , user , realm , session_id , dmacc ) ;
2018-10-08 12:15:59 +02:00
else if ( type & & strcmp ( type , " machine " ) = = 0 )
2013-03-28 09:27:27 +01:00
ret = machine_remediation ( ctx , user , realm , session_id , dmacc ) ;
2018-12-04 13:11:39 +01:00
else if ( type & & strcmp ( type , " reenroll " ) = = 0 )
ret = cert_reenroll ( ctx , user , realm , session_id ) ;
2018-10-08 12:15:59 +02:00
else
ret = no_sub_rem ( ctx , user , realm , session_id ) ;
2013-03-28 09:27:27 +01:00
free ( type ) ;
return ret ;
}
2018-12-16 17:33:11 +01:00
static xml_node_t * read_policy_file ( struct hs20_svc * ctx ,
const char * policy_id )
{
char fname [ 200 ] ;
snprintf ( fname , sizeof ( fname ) , " %s/spp/policy/%s.xml " ,
ctx - > root_dir , policy_id ) ;
debug_print ( ctx , 1 , " Use policy file %s " , fname ) ;
return node_from_file ( ctx - > xml , fname ) ;
}
static void update_policy_update_uri ( struct hs20_svc * ctx , const char * realm ,
xml_node_t * policy )
{
xml_node_t * node ;
char * url ;
node = get_node_uri ( ctx - > xml , policy , " Policy/PolicyUpdate/URI " ) ;
if ( ! node )
return ;
url = db_get_osu_config_val ( ctx , realm , " policy_url " ) ;
if ( ! url )
return ;
xml_node_set_text ( ctx - > xml , node , url ) ;
free ( url ) ;
}
2013-03-28 09:27:27 +01:00
static xml_node_t * build_policy ( struct hs20_svc * ctx , const char * user ,
const char * realm , int use_dmacc )
{
char * policy_id ;
xml_node_t * policy , * node ;
policy_id = db_get_val ( ctx , user , realm , " policy " , use_dmacc ) ;
if ( policy_id = = NULL | | strlen ( policy_id ) = = 0 ) {
free ( policy_id ) ;
policy_id = strdup ( " default " ) ;
if ( policy_id = = NULL )
return NULL ;
}
2018-12-16 17:33:11 +01:00
policy = read_policy_file ( ctx , policy_id ) ;
2013-03-28 09:27:27 +01:00
free ( policy_id ) ;
if ( policy = = NULL )
return NULL ;
2018-12-16 17:33:11 +01:00
update_policy_update_uri ( ctx , realm , policy ) ;
2013-03-28 09:27:27 +01:00
node = get_node_uri ( ctx - > xml , policy , " Policy/PolicyUpdate " ) ;
if ( node & & use_dmacc ) {
char * pw ;
pw = db_get_val ( ctx , user , realm , " osu_password " , use_dmacc ) ;
if ( pw = = NULL | |
build_username_password ( ctx , node , user , pw ) = = NULL ) {
debug_print ( ctx , 1 , " Failed to add Policy/PolicyUpdate/ "
" UsernamePassword " ) ;
free ( pw ) ;
xml_node_free ( ctx - > xml , policy ) ;
return NULL ;
}
free ( pw ) ;
}
return policy ;
}
static xml_node_t * hs20_policy_update ( struct hs20_svc * ctx ,
const char * user , const char * realm ,
const char * session_id , int dmacc )
{
xml_namespace_t * ns ;
xml_node_t * spp_node ;
xml_node_t * policy ;
char buf [ 400 ] ;
const char * status ;
char * identity ;
identity = db_get_val ( ctx , user , realm , " identity " , dmacc ) ;
if ( identity = = NULL | | strlen ( identity ) = = 0 ) {
hs20_eventlog ( ctx , user , realm , session_id ,
" user not found in database for policy update " ,
NULL ) ;
os_free ( identity ) ;
return build_post_dev_data_response ( ctx , NULL , session_id ,
" Error occurred " ,
" Not found " ) ;
}
os_free ( identity ) ;
policy = build_policy ( ctx , user , realm , dmacc ) ;
if ( ! policy ) {
return build_post_dev_data_response (
ctx , NULL , session_id ,
" No update available at this time " , NULL ) ;
}
2018-09-15 01:53:49 +02:00
db_add_session ( ctx , user , realm , session_id , NULL , NULL , POLICY_UPDATE ,
NULL ) ;
2013-03-28 09:27:27 +01:00
status = " Update complete, request sppUpdateResponse " ;
spp_node = build_post_dev_data_response ( ctx , & ns , session_id , status ,
NULL ) ;
if ( spp_node = = NULL )
return NULL ;
snprintf ( buf , sizeof ( buf ) ,
2018-10-19 17:07:37 +02:00
" ./Wi-Fi/%s/PerProviderSubscription/Cred01/Policy " ,
2013-03-28 09:27:27 +01:00
realm ) ;
if ( add_update_node ( ctx , spp_node , ns , buf , policy ) < 0 ) {
xml_node_free ( ctx - > xml , spp_node ) ;
xml_node_free ( ctx - > xml , policy ) ;
return NULL ;
}
hs20_eventlog_node ( ctx , user , realm , session_id , " policy update " ,
policy ) ;
xml_node_free ( ctx - > xml , policy ) ;
return spp_node ;
}
static xml_node_t * spp_get_mo ( struct hs20_svc * ctx , xml_node_t * node ,
const char * urn , int * valid , char * * ret_err )
{
xml_node_t * child , * tnds , * mo ;
const char * name ;
char * mo_urn ;
char * str ;
char fname [ 200 ] ;
* valid = - 1 ;
if ( ret_err )
* ret_err = NULL ;
xml_node_for_each_child ( ctx - > xml , child , node ) {
xml_node_for_each_check ( ctx - > xml , child ) ;
name = xml_node_get_localname ( ctx - > xml , child ) ;
if ( strcmp ( name , " moContainer " ) ! = 0 )
continue ;
mo_urn = xml_node_get_attr_value_ns ( ctx - > xml , child , SPP_NS_URI ,
" moURN " ) ;
if ( strcasecmp ( urn , mo_urn ) = = 0 ) {
xml_node_get_attr_value_free ( ctx - > xml , mo_urn ) ;
break ;
}
xml_node_get_attr_value_free ( ctx - > xml , mo_urn ) ;
}
if ( child = = NULL )
return NULL ;
debug_print ( ctx , 1 , " moContainer text for %s " , urn ) ;
debug_dump_node ( ctx , " moContainer " , child ) ;
str = xml_node_get_text ( ctx - > xml , child ) ;
debug_print ( ctx , 1 , " moContainer payload: '%s' " , str ) ;
tnds = xml_node_from_buf ( ctx - > xml , str ) ;
xml_node_get_text_free ( ctx - > xml , str ) ;
if ( tnds = = NULL ) {
debug_print ( ctx , 1 , " could not parse moContainer text " ) ;
return NULL ;
}
snprintf ( fname , sizeof ( fname ) , " %s/spp/dm_ddf-v1_2.dtd " , ctx - > root_dir ) ;
if ( xml_validate_dtd ( ctx - > xml , tnds , fname , ret_err ) = = 0 )
* valid = 1 ;
else if ( ret_err & & * ret_err & &
os_strcmp ( * ret_err , " No declaration for attribute xmlns of element MgmtTree \n " ) = = 0 ) {
free ( * ret_err ) ;
debug_print ( ctx , 1 , " Ignore OMA-DM DDF DTD validation error for MgmtTree namespace declaration with xmlns attribute " ) ;
* ret_err = NULL ;
* valid = 1 ;
} else
* valid = 0 ;
mo = tnds_to_mo ( ctx - > xml , tnds ) ;
xml_node_free ( ctx - > xml , tnds ) ;
if ( mo = = NULL ) {
debug_print ( ctx , 1 , " invalid moContainer for %s " , urn ) ;
}
return mo ;
}
static xml_node_t * spp_exec_upload_mo ( struct hs20_svc * ctx ,
const char * session_id , const char * urn )
{
xml_namespace_t * ns ;
xml_node_t * spp_node , * node , * exec_node ;
spp_node = build_post_dev_data_response ( ctx , & ns , session_id , " OK " ,
NULL ) ;
if ( spp_node = = NULL )
return NULL ;
exec_node = xml_node_create ( ctx - > xml , spp_node , ns , " exec " ) ;
node = xml_node_create ( ctx - > xml , exec_node , ns , " uploadMO " ) ;
xml_node_add_attr ( ctx - > xml , node , ns , " moURN " , urn ) ;
return spp_node ;
}
static xml_node_t * hs20_subscription_registration ( struct hs20_svc * ctx ,
const char * realm ,
const char * session_id ,
2018-09-15 01:53:49 +02:00
const char * redirect_uri ,
const u8 * mac_addr )
2013-03-28 09:27:27 +01:00
{
xml_namespace_t * ns ;
xml_node_t * spp_node , * exec_node ;
char uri [ 300 ] , * val ;
if ( db_add_session ( ctx , NULL , realm , session_id , NULL , redirect_uri ,
2018-09-15 01:53:49 +02:00
SUBSCRIPTION_REGISTRATION , mac_addr ) < 0 )
2013-03-28 09:27:27 +01:00
return NULL ;
val = db_get_osu_config_val ( ctx , realm , " signup_url " ) ;
if ( val = = NULL )
return NULL ;
spp_node = build_post_dev_data_response ( ctx , & ns , session_id , " OK " ,
NULL ) ;
if ( spp_node = = NULL )
return NULL ;
exec_node = xml_node_create ( ctx - > xml , spp_node , ns , " exec " ) ;
snprintf ( uri , sizeof ( uri ) , " %s%s " , val , session_id ) ;
os_free ( val ) ;
xml_node_create_text ( ctx - > xml , exec_node , ns , " launchBrowserToURI " ,
uri ) ;
return spp_node ;
}
static xml_node_t * hs20_user_input_remediation ( struct hs20_svc * ctx ,
const char * user ,
const char * realm , int dmacc ,
const char * session_id )
{
return build_sub_rem_resp ( ctx , user , realm , session_id , 0 , dmacc ) ;
}
static char * db_get_osu_config_val ( struct hs20_svc * ctx , const char * realm ,
const char * field )
{
char * cmd ;
struct get_db_field_data data ;
cmd = sqlite3_mprintf ( " SELECT value FROM osu_config WHERE realm=%Q AND "
" field=%Q " , realm , field ) ;
if ( cmd = = NULL )
return NULL ;
debug_print ( ctx , 1 , " DB: %s " , cmd ) ;
memset ( & data , 0 , sizeof ( data ) ) ;
data . field = " value " ;
if ( sqlite3_exec ( ctx - > db , cmd , get_db_field , & data , NULL ) ! = SQLITE_OK )
{
debug_print ( ctx , 1 , " DB: Could not find osu_config %s: %s " ,
realm , sqlite3_errmsg ( ctx - > db ) ) ;
sqlite3_free ( cmd ) ;
return NULL ;
}
sqlite3_free ( cmd ) ;
debug_print ( ctx , 1 , " DB: return '%s' " , data . value ) ;
return data . value ;
}
static xml_node_t * build_pps ( struct hs20_svc * ctx ,
const char * user , const char * realm ,
const char * pw , const char * cert ,
2018-12-15 17:00:12 +01:00
int machine_managed , const char * test ,
const char * imsi , const char * dmacc_username ,
2018-12-16 17:33:11 +01:00
const char * dmacc_password ,
xml_node_t * policy_node )
2013-03-28 09:27:27 +01:00
{
2018-10-19 16:57:39 +02:00
xml_node_t * pps , * c , * trust , * aaa , * aaa1 , * upd , * homesp , * p ;
2013-03-28 09:27:27 +01:00
xml_node_t * cred , * eap , * userpw ;
pps = xml_node_create_root ( ctx - > xml , NULL , NULL , NULL ,
" PerProviderSubscription " ) ;
2018-12-16 17:33:11 +01:00
if ( ! pps ) {
xml_node_free ( ctx - > xml , policy_node ) ;
2013-03-28 09:27:27 +01:00
return NULL ;
2018-12-16 17:33:11 +01:00
}
2013-03-28 09:27:27 +01:00
add_text_node ( ctx , pps , " UpdateIdentifier " , " 1 " ) ;
2018-10-19 17:07:37 +02:00
c = xml_node_create ( ctx - > xml , pps , NULL , " Cred01 " ) ;
2013-03-28 09:27:27 +01:00
add_text_node ( ctx , c , " CredentialPriority " , " 1 " ) ;
2018-12-15 17:00:12 +01:00
if ( imsi )
goto skip_aaa_trust_root ;
2013-03-28 09:27:27 +01:00
aaa = xml_node_create ( ctx - > xml , c , NULL , " AAAServerTrustRoot " ) ;
aaa1 = xml_node_create ( ctx - > xml , aaa , NULL , " AAA1 " ) ;
add_text_node_conf ( ctx , realm , aaa1 , " CertURL " ,
" aaa_trust_root_cert_url " ) ;
2018-10-17 18:08:12 +02:00
if ( test & & os_strcmp ( test , " corrupt_aaa_hash " ) = = 0 ) {
debug_print ( ctx , 1 ,
" TEST: Corrupt PPS/Cred*/AAAServerTrustRoot/Root*/CertSHA256FingerPrint " ) ;
add_text_node_conf_corrupt ( ctx , realm , aaa1 ,
" CertSHA256Fingerprint " ,
" aaa_trust_root_cert_fingerprint " ) ;
} else {
add_text_node_conf ( ctx , realm , aaa1 , " CertSHA256Fingerprint " ,
" aaa_trust_root_cert_fingerprint " ) ;
}
2013-03-28 09:27:27 +01:00
2018-10-19 16:57:39 +02:00
if ( test & & os_strcmp ( test , " corrupt_polupd_hash " ) = = 0 ) {
debug_print ( ctx , 1 ,
" TEST: Corrupt PPS/Cred*/Policy/PolicyUpdate/Trustroot/CertSHA256FingerPrint " ) ;
p = xml_node_create ( ctx - > xml , c , NULL , " Policy " ) ;
upd = xml_node_create ( ctx - > xml , p , NULL , " PolicyUpdate " ) ;
add_text_node ( ctx , upd , " UpdateInterval " , " 30 " ) ;
add_text_node ( ctx , upd , " UpdateMethod " , " SPP-ClientInitiated " ) ;
add_text_node ( ctx , upd , " Restriction " , " Unrestricted " ) ;
add_text_node_conf ( ctx , realm , upd , " URI " , " policy_url " ) ;
trust = xml_node_create ( ctx - > xml , upd , NULL , " TrustRoot " ) ;
add_text_node_conf ( ctx , realm , trust , " CertURL " ,
" policy_trust_root_cert_url " ) ;
add_text_node_conf_corrupt ( ctx , realm , trust ,
" CertSHA256Fingerprint " ,
" policy_trust_root_cert_fingerprint " ) ;
}
2018-12-15 17:00:12 +01:00
skip_aaa_trust_root :
2018-10-19 16:57:39 +02:00
2013-03-28 09:27:27 +01:00
upd = xml_node_create ( ctx - > xml , c , NULL , " SubscriptionUpdate " ) ;
add_text_node ( ctx , upd , " UpdateInterval " , " 4294967295 " ) ;
2018-10-19 17:00:02 +02:00
add_text_node ( ctx , upd , " UpdateMethod " , " SPP-ClientInitiated " ) ;
2013-03-28 09:27:27 +01:00
add_text_node ( ctx , upd , " Restriction " , " HomeSP " ) ;
add_text_node_conf ( ctx , realm , upd , " URI " , " spp_http_auth_url " ) ;
trust = xml_node_create ( ctx - > xml , upd , NULL , " TrustRoot " ) ;
add_text_node_conf ( ctx , realm , trust , " CertURL " , " trust_root_cert_url " ) ;
2018-10-17 18:08:12 +02:00
if ( test & & os_strcmp ( test , " corrupt_subrem_hash " ) = = 0 ) {
debug_print ( ctx , 1 ,
" TEST: Corrupt PPS/Cred*/SubscriptionUpdate/Trustroot/CertSHA256FingerPrint " ) ;
add_text_node_conf_corrupt ( ctx , realm , trust ,
" CertSHA256Fingerprint " ,
" trust_root_cert_fingerprint " ) ;
} else {
add_text_node_conf ( ctx , realm , trust , " CertSHA256Fingerprint " ,
" trust_root_cert_fingerprint " ) ;
}
2013-03-28 09:27:27 +01:00
2018-12-15 17:00:12 +01:00
if ( dmacc_username & &
! build_username_password ( ctx , upd , dmacc_username ,
dmacc_password ) ) {
xml_node_free ( ctx - > xml , pps ) ;
2018-12-16 17:33:11 +01:00
xml_node_free ( ctx - > xml , policy_node ) ;
2018-12-15 17:00:12 +01:00
return NULL ;
}
2018-12-16 17:33:11 +01:00
if ( policy_node )
xml_node_add_child ( ctx - > xml , c , policy_node ) ;
2013-03-28 09:27:27 +01:00
homesp = xml_node_create ( ctx - > xml , c , NULL , " HomeSP " ) ;
add_text_node_conf ( ctx , realm , homesp , " FriendlyName " , " friendly_name " ) ;
add_text_node_conf ( ctx , realm , homesp , " FQDN " , " fqdn " ) ;
xml_node_create ( ctx - > xml , c , NULL , " SubscriptionParameters " ) ;
cred = xml_node_create ( ctx - > xml , c , NULL , " Credential " ) ;
add_creation_date ( ctx , cred ) ;
2018-12-15 17:00:12 +01:00
if ( imsi ) {
xml_node_t * sim ;
const char * type = " 18 " ; /* default to EAP-SIM */
sim = xml_node_create ( ctx - > xml , cred , NULL , " SIM " ) ;
add_text_node ( ctx , sim , " IMSI " , imsi ) ;
if ( ctx - > eap_method & & os_strcmp ( ctx - > eap_method , " AKA " ) = = 0 )
type = " 23 " ;
else if ( ctx - > eap_method & &
os_strcmp ( ctx - > eap_method , " AKA' " ) = = 0 )
type = " 50 " ;
add_text_node ( ctx , sim , " EAPType " , type ) ;
} else if ( cert ) {
2013-03-28 09:27:27 +01:00
xml_node_t * dc ;
dc = xml_node_create ( ctx - > xml , cred , NULL ,
" DigitalCertificate " ) ;
add_text_node ( ctx , dc , " CertificateType " , " x509v3 " ) ;
add_text_node ( ctx , dc , " CertSHA256Fingerprint " , cert ) ;
} else {
userpw = build_username_password ( ctx , cred , user , pw ) ;
add_text_node ( ctx , userpw , " MachineManaged " ,
machine_managed ? " TRUE " : " FALSE " ) ;
eap = xml_node_create ( ctx - > xml , userpw , NULL , " EAPMethod " ) ;
add_text_node ( ctx , eap , " EAPType " , " 21 " ) ;
add_text_node ( ctx , eap , " InnerMethod " , " MS-CHAP-V2 " ) ;
}
add_text_node ( ctx , cred , " Realm " , realm ) ;
return pps ;
}
static xml_node_t * spp_exec_get_certificate ( struct hs20_svc * ctx ,
const char * session_id ,
const char * user ,
2018-12-04 13:11:39 +01:00
const char * realm ,
int add_est_user )
2013-03-28 09:27:27 +01:00
{
xml_namespace_t * ns ;
xml_node_t * spp_node , * enroll , * exec_node ;
char * val ;
char password [ 11 ] ;
char * b64 ;
2018-12-04 13:11:39 +01:00
if ( add_est_user & & new_password ( password , sizeof ( password ) ) < 0 )
2013-03-28 09:27:27 +01:00
return NULL ;
spp_node = build_post_dev_data_response ( ctx , & ns , session_id , " OK " ,
NULL ) ;
if ( spp_node = = NULL )
return NULL ;
exec_node = xml_node_create ( ctx - > xml , spp_node , ns , " exec " ) ;
enroll = xml_node_create ( ctx - > xml , exec_node , ns , " getCertificate " ) ;
xml_node_add_attr ( ctx - > xml , enroll , NULL , " enrollmentProtocol " , " EST " ) ;
val = db_get_osu_config_val ( ctx , realm , " est_url " ) ;
xml_node_create_text ( ctx - > xml , enroll , ns , " enrollmentServerURI " ,
val ? val : " " ) ;
os_free ( val ) ;
2018-12-04 13:11:39 +01:00
if ( ! add_est_user )
return spp_node ;
2013-03-28 09:27:27 +01:00
xml_node_create_text ( ctx - > xml , enroll , ns , " estUserID " , user ) ;
b64 = ( char * ) base64_encode ( ( unsigned char * ) password ,
strlen ( password ) , NULL ) ;
if ( b64 = = NULL ) {
xml_node_free ( ctx - > xml , spp_node ) ;
return NULL ;
}
xml_node_create_text ( ctx - > xml , enroll , ns , " estPassword " , b64 ) ;
free ( b64 ) ;
db_update_session_password ( ctx , user , realm , session_id , password ) ;
return spp_node ;
}
static xml_node_t * hs20_user_input_registration ( struct hs20_svc * ctx ,
const char * session_id ,
int enrollment_done )
{
xml_namespace_t * ns ;
xml_node_t * spp_node , * node = NULL ;
xml_node_t * pps , * tnds ;
char buf [ 400 ] ;
char * str ;
2018-10-17 18:08:12 +02:00
char * user , * realm , * pw , * type , * mm , * test ;
2013-03-28 09:27:27 +01:00
const char * status ;
int cert = 0 ;
int machine_managed = 0 ;
char * fingerprint ;
user = db_get_session_val ( ctx , NULL , NULL , session_id , " user " ) ;
realm = db_get_session_val ( ctx , NULL , NULL , session_id , " realm " ) ;
pw = db_get_session_val ( ctx , NULL , NULL , session_id , " password " ) ;
if ( ! user | | ! realm | | ! pw ) {
debug_print ( ctx , 1 , " Could not find session info from DB for "
" the new subscription " ) ;
free ( user ) ;
free ( realm ) ;
free ( pw ) ;
return NULL ;
}
mm = db_get_session_val ( ctx , NULL , NULL , session_id , " machine_managed " ) ;
if ( mm & & atoi ( mm ) )
machine_managed = 1 ;
free ( mm ) ;
type = db_get_session_val ( ctx , NULL , NULL , session_id , " type " ) ;
if ( type & & strcmp ( type , " cert " ) = = 0 )
cert = 1 ;
free ( type ) ;
if ( cert & & ! enrollment_done ) {
xml_node_t * ret ;
hs20_eventlog ( ctx , user , realm , session_id ,
" request client certificate enrollment " , NULL ) ;
2018-12-04 13:11:39 +01:00
ret = spp_exec_get_certificate ( ctx , session_id , user , realm , 1 ) ;
2013-03-28 09:27:27 +01:00
free ( user ) ;
free ( realm ) ;
free ( pw ) ;
return ret ;
}
if ( ! cert & & strlen ( pw ) = = 0 ) {
machine_managed = 1 ;
free ( pw ) ;
pw = malloc ( 11 ) ;
if ( pw = = NULL | | new_password ( pw , 11 ) < 0 ) {
free ( user ) ;
free ( realm ) ;
free ( pw ) ;
return NULL ;
}
}
status = " Provisioning complete, request sppUpdateResponse " ;
spp_node = build_post_dev_data_response ( ctx , & ns , session_id , status ,
NULL ) ;
if ( spp_node = = NULL )
return NULL ;
fingerprint = db_get_session_val ( ctx , NULL , NULL , session_id , " cert " ) ;
2018-10-17 18:08:12 +02:00
test = db_get_session_val ( ctx , NULL , NULL , session_id , " test " ) ;
if ( test )
debug_print ( ctx , 1 , " TEST: Requested special behavior: %s " ,
test ) ;
2013-03-28 09:27:27 +01:00
pps = build_pps ( ctx , user , realm , pw ,
2018-10-17 18:08:12 +02:00
fingerprint ? fingerprint : NULL , machine_managed ,
2018-12-16 17:33:11 +01:00
test , NULL , NULL , NULL , NULL ) ;
2013-03-28 09:27:27 +01:00
free ( fingerprint ) ;
2018-10-17 18:08:12 +02:00
free ( test ) ;
2013-03-28 09:27:27 +01:00
if ( ! pps ) {
xml_node_free ( ctx - > xml , spp_node ) ;
free ( user ) ;
free ( realm ) ;
free ( pw ) ;
return NULL ;
}
debug_print ( ctx , 1 , " Request DB subscription registration on success "
" notification " ) ;
2014-04-10 12:46:45 +02:00
if ( machine_managed ) {
db_update_session_password ( ctx , user , realm , session_id , pw ) ;
db_update_session_machine_managed ( ctx , user , realm , session_id ,
machine_managed ) ;
}
2013-03-28 09:27:27 +01:00
db_add_session_pps ( ctx , user , realm , session_id , pps ) ;
hs20_eventlog_node ( ctx , user , realm , session_id ,
" new subscription " , pps ) ;
free ( user ) ;
free ( pw ) ;
tnds = mo_to_tnds ( ctx - > xml , pps , 0 , URN_HS20_PPS , NULL ) ;
xml_node_free ( ctx - > xml , pps ) ;
if ( ! tnds ) {
xml_node_free ( ctx - > xml , spp_node ) ;
free ( realm ) ;
return NULL ;
}
str = xml_node_to_str ( ctx - > xml , tnds ) ;
xml_node_free ( ctx - > xml , tnds ) ;
if ( str = = NULL ) {
xml_node_free ( ctx - > xml , spp_node ) ;
free ( realm ) ;
return NULL ;
}
node = xml_node_create_text ( ctx - > xml , spp_node , ns , " addMO " , str ) ;
free ( str ) ;
snprintf ( buf , sizeof ( buf ) , " ./Wi-Fi/%s/PerProviderSubscription " , realm ) ;
free ( realm ) ;
xml_node_add_attr ( ctx - > xml , node , ns , " managementTreeURI " , buf ) ;
xml_node_add_attr ( ctx - > xml , node , ns , " moURN " , URN_HS20_PPS ) ;
return spp_node ;
}
static xml_node_t * hs20_user_input_free_remediation ( struct hs20_svc * ctx ,
const char * user ,
const char * realm ,
const char * session_id )
{
xml_namespace_t * ns ;
xml_node_t * spp_node ;
xml_node_t * cred ;
char buf [ 400 ] ;
char * status ;
char * free_account , * pw ;
free_account = db_get_osu_config_val ( ctx , realm , " free_account " ) ;
if ( free_account = = NULL )
return NULL ;
pw = db_get_val ( ctx , free_account , realm , " password " , 0 ) ;
if ( pw = = NULL ) {
free ( free_account ) ;
return NULL ;
}
2018-10-08 17:07:00 +02:00
cred = build_credential_pw ( ctx , free_account , realm , pw , 1 ) ;
2013-03-28 09:27:27 +01:00
free ( free_account ) ;
free ( pw ) ;
if ( ! cred ) {
xml_node_free ( ctx - > xml , cred ) ;
return NULL ;
}
status = " Remediation complete, request sppUpdateResponse " ;
spp_node = build_post_dev_data_response ( ctx , & ns , session_id , status ,
NULL ) ;
if ( spp_node = = NULL )
return NULL ;
snprintf ( buf , sizeof ( buf ) ,
2018-10-19 17:07:37 +02:00
" ./Wi-Fi/%s/PerProviderSubscription/Cred01/Credential " ,
2013-03-28 09:27:27 +01:00
realm ) ;
if ( add_update_node ( ctx , spp_node , ns , buf , cred ) < 0 ) {
xml_node_free ( ctx - > xml , spp_node ) ;
return NULL ;
}
hs20_eventlog_node ( ctx , user , realm , session_id ,
" free/public remediation " , cred ) ;
xml_node_free ( ctx - > xml , cred ) ;
return spp_node ;
}
static xml_node_t * hs20_user_input_complete ( struct hs20_svc * ctx ,
const char * user ,
const char * realm , int dmacc ,
const char * session_id )
{
char * val ;
enum hs20_session_operation oper ;
val = db_get_session_val ( ctx , user , realm , session_id , " operation " ) ;
if ( val = = NULL ) {
debug_print ( ctx , 1 , " No session %s found to continue " ,
session_id ) ;
return NULL ;
}
oper = atoi ( val ) ;
free ( val ) ;
if ( oper = = USER_REMEDIATION ) {
return hs20_user_input_remediation ( ctx , user , realm , dmacc ,
session_id ) ;
}
if ( oper = = FREE_REMEDIATION ) {
return hs20_user_input_free_remediation ( ctx , user , realm ,
session_id ) ;
}
if ( oper = = SUBSCRIPTION_REGISTRATION ) {
return hs20_user_input_registration ( ctx , session_id , 0 ) ;
}
debug_print ( ctx , 1 , " User session %s not in state for user input "
" completion " , session_id ) ;
return NULL ;
}
2018-12-04 13:11:39 +01:00
static xml_node_t * hs20_cert_reenroll_complete ( struct hs20_svc * ctx ,
const char * session_id )
{
char * user , * realm , * cert ;
char * status ;
xml_namespace_t * ns ;
xml_node_t * spp_node , * cred ;
char buf [ 400 ] ;
user = db_get_session_val ( ctx , NULL , NULL , session_id , " user " ) ;
realm = db_get_session_val ( ctx , NULL , NULL , session_id , " realm " ) ;
cert = db_get_session_val ( ctx , NULL , NULL , session_id , " cert " ) ;
if ( ! user | | ! realm | | ! cert ) {
debug_print ( ctx , 1 ,
" Could not find session info from DB for certificate reenrollment " ) ;
free ( user ) ;
free ( realm ) ;
free ( cert ) ;
return NULL ;
}
cred = build_credential_cert ( ctx , user , realm , cert ) ;
if ( ! cred ) {
debug_print ( ctx , 1 , " Could not build credential " ) ;
free ( user ) ;
free ( realm ) ;
free ( cert ) ;
return NULL ;
}
status = " Remediation complete, request sppUpdateResponse " ;
spp_node = build_post_dev_data_response ( ctx , & ns , session_id , status ,
NULL ) ;
if ( spp_node = = NULL ) {
debug_print ( ctx , 1 , " Could not build sppPostDevDataResponse " ) ;
free ( user ) ;
free ( realm ) ;
free ( cert ) ;
xml_node_free ( ctx - > xml , cred ) ;
return NULL ;
}
snprintf ( buf , sizeof ( buf ) ,
" ./Wi-Fi/%s/PerProviderSubscription/Cred01/Credential " ,
realm ) ;
if ( add_update_node ( ctx , spp_node , ns , buf , cred ) < 0 ) {
debug_print ( ctx , 1 , " Could not add update node " ) ;
xml_node_free ( ctx - > xml , spp_node ) ;
free ( user ) ;
free ( realm ) ;
free ( cert ) ;
return NULL ;
}
hs20_eventlog_node ( ctx , user , realm , session_id ,
" certificate reenrollment " , cred ) ;
xml_node_free ( ctx - > xml , cred ) ;
free ( user ) ;
free ( realm ) ;
free ( cert ) ;
return spp_node ;
}
2013-03-28 09:27:27 +01:00
static xml_node_t * hs20_cert_enroll_completed ( struct hs20_svc * ctx ,
const char * user ,
const char * realm , int dmacc ,
const char * session_id )
{
char * val ;
enum hs20_session_operation oper ;
2018-12-04 13:11:39 +01:00
val = db_get_session_val ( ctx , NULL , NULL , session_id , " operation " ) ;
2013-03-28 09:27:27 +01:00
if ( val = = NULL ) {
debug_print ( ctx , 1 , " No session %s found to continue " ,
session_id ) ;
return NULL ;
}
oper = atoi ( val ) ;
free ( val ) ;
if ( oper = = SUBSCRIPTION_REGISTRATION )
return hs20_user_input_registration ( ctx , session_id , 1 ) ;
2018-12-04 13:11:39 +01:00
if ( oper = = CERT_REENROLL )
return hs20_cert_reenroll_complete ( ctx , session_id ) ;
2013-03-28 09:27:27 +01:00
debug_print ( ctx , 1 , " User session %s not in state for certificate "
" enrollment completion " , session_id ) ;
return NULL ;
}
static xml_node_t * hs20_cert_enroll_failed ( struct hs20_svc * ctx ,
const char * user ,
const char * realm , int dmacc ,
const char * session_id )
{
char * val ;
enum hs20_session_operation oper ;
xml_node_t * spp_node , * node ;
char * status ;
xml_namespace_t * ns ;
val = db_get_session_val ( ctx , user , realm , session_id , " operation " ) ;
if ( val = = NULL ) {
debug_print ( ctx , 1 , " No session %s found to continue " ,
session_id ) ;
return NULL ;
}
oper = atoi ( val ) ;
free ( val ) ;
if ( oper ! = SUBSCRIPTION_REGISTRATION ) {
debug_print ( ctx , 1 , " User session %s not in state for "
" enrollment failure " , session_id ) ;
return NULL ;
}
status = " Error occurred " ;
spp_node = build_post_dev_data_response ( ctx , & ns , session_id , status ,
NULL ) ;
if ( spp_node = = NULL )
return NULL ;
node = xml_node_create ( ctx - > xml , spp_node , ns , " sppError " ) ;
xml_node_add_attr ( ctx - > xml , node , NULL , " errorCode " ,
" Credentials cannot be provisioned at this time " ) ;
db_remove_session ( ctx , user , realm , session_id ) ;
return spp_node ;
}
2018-12-15 17:00:12 +01:00
static xml_node_t * hs20_sim_provisioning ( struct hs20_svc * ctx ,
const char * user ,
const char * realm , int dmacc ,
const char * session_id )
{
xml_namespace_t * ns ;
xml_node_t * spp_node , * node = NULL ;
xml_node_t * pps , * tnds ;
char buf [ 400 ] ;
char * str ;
const char * status ;
char dmacc_username [ 32 ] ;
char dmacc_password [ 32 ] ;
2018-12-16 17:33:11 +01:00
char * policy ;
xml_node_t * policy_node = NULL ;
2018-12-15 17:00:12 +01:00
if ( ! ctx - > imsi ) {
debug_print ( ctx , 1 , " IMSI not available for SIM provisioning " ) ;
return NULL ;
}
if ( new_password ( dmacc_username , sizeof ( dmacc_username ) ) < 0 | |
new_password ( dmacc_password , sizeof ( dmacc_password ) ) < 0 ) {
debug_print ( ctx , 1 ,
" Failed to generate DMAcc username/password " ) ;
return NULL ;
}
status = " Provisioning complete, request sppUpdateResponse " ;
spp_node = build_post_dev_data_response ( ctx , & ns , session_id , status ,
NULL ) ;
if ( ! spp_node )
return NULL ;
2018-12-16 17:33:11 +01:00
policy = db_get_osu_config_val ( ctx , realm , " sim_policy " ) ;
if ( policy ) {
policy_node = read_policy_file ( ctx , policy ) ;
os_free ( policy ) ;
if ( ! policy_node ) {
xml_node_free ( ctx - > xml , spp_node ) ;
return NULL ;
}
update_policy_update_uri ( ctx , realm , policy_node ) ;
node = get_node_uri ( ctx - > xml , policy_node ,
" Policy/PolicyUpdate " ) ;
if ( node )
build_username_password ( ctx , node , dmacc_username ,
dmacc_password ) ;
}
2018-12-15 17:00:12 +01:00
pps = build_pps ( ctx , NULL , realm , NULL , NULL , 0 , NULL , ctx - > imsi ,
2018-12-16 17:33:11 +01:00
dmacc_username , dmacc_password , policy_node ) ;
2018-12-15 17:00:12 +01:00
if ( ! pps ) {
xml_node_free ( ctx - > xml , spp_node ) ;
return NULL ;
}
debug_print ( ctx , 1 ,
" Request DB subscription registration on success notification " ) ;
if ( ! user | | ! user [ 0 ] )
user = ctx - > imsi ;
db_add_session ( ctx , user , realm , session_id , NULL , NULL ,
SUBSCRIPTION_REGISTRATION , NULL ) ;
db_add_session_dmacc ( ctx , session_id , dmacc_username , dmacc_password ) ;
if ( ctx - > eap_method )
db_add_session_eap_method ( ctx , session_id , ctx - > eap_method ) ;
if ( ctx - > id_hash )
db_add_session_id_hash ( ctx , session_id , ctx - > id_hash ) ;
db_add_session_pps ( ctx , user , realm , session_id , pps ) ;
hs20_eventlog_node ( ctx , user , realm , session_id ,
" new subscription " , pps ) ;
tnds = mo_to_tnds ( ctx - > xml , pps , 0 , URN_HS20_PPS , NULL ) ;
xml_node_free ( ctx - > xml , pps ) ;
if ( ! tnds ) {
xml_node_free ( ctx - > xml , spp_node ) ;
return NULL ;
}
str = xml_node_to_str ( ctx - > xml , tnds ) ;
xml_node_free ( ctx - > xml , tnds ) ;
if ( ! str ) {
xml_node_free ( ctx - > xml , spp_node ) ;
return NULL ;
}
node = xml_node_create_text ( ctx - > xml , spp_node , ns , " addMO " , str ) ;
free ( str ) ;
snprintf ( buf , sizeof ( buf ) , " ./Wi-Fi/%s/PerProviderSubscription " , realm ) ;
xml_node_add_attr ( ctx - > xml , node , ns , " managementTreeURI " , buf ) ;
xml_node_add_attr ( ctx - > xml , node , ns , " moURN " , URN_HS20_PPS ) ;
return spp_node ;
}
2013-03-28 09:27:27 +01:00
static xml_node_t * hs20_spp_post_dev_data ( struct hs20_svc * ctx ,
xml_node_t * node ,
const char * user ,
const char * realm ,
const char * session_id ,
int dmacc )
{
const char * req_reason ;
char * redirect_uri = NULL ;
char * req_reason_buf = NULL ;
char str [ 200 ] ;
xml_node_t * ret = NULL , * devinfo = NULL , * devdetail = NULL ;
2018-09-15 01:53:49 +02:00
xml_node_t * mo , * macaddr ;
2013-03-28 09:27:27 +01:00
char * version ;
int valid ;
char * supp , * pos ;
char * err ;
2018-09-15 01:53:49 +02:00
u8 wifi_mac_addr [ ETH_ALEN ] ;
2013-03-28 09:27:27 +01:00
version = xml_node_get_attr_value_ns ( ctx - > xml , node , SPP_NS_URI ,
" sppVersion " ) ;
if ( version = = NULL | | strstr ( version , " 1.0 " ) = = NULL ) {
ret = build_post_dev_data_response (
ctx , NULL , session_id , " Error occurred " ,
" SPP version not supported " ) ;
hs20_eventlog_node ( ctx , user , realm , session_id ,
" Unsupported sppVersion " , ret ) ;
xml_node_get_attr_value_free ( ctx - > xml , version ) ;
return ret ;
}
xml_node_get_attr_value_free ( ctx - > xml , version ) ;
mo = get_node ( ctx - > xml , node , " supportedMOList " ) ;
if ( mo = = NULL ) {
ret = build_post_dev_data_response (
ctx , NULL , session_id , " Error occurred " ,
" Other " ) ;
hs20_eventlog_node ( ctx , user , realm , session_id ,
" No supportedMOList element " , ret ) ;
return ret ;
}
supp = xml_node_get_text ( ctx - > xml , mo ) ;
for ( pos = supp ; pos & & * pos ; pos + + )
* pos = tolower ( * pos ) ;
if ( supp = = NULL | |
strstr ( supp , URN_OMA_DM_DEVINFO ) = = NULL | |
strstr ( supp , URN_OMA_DM_DEVDETAIL ) = = NULL | |
strstr ( supp , URN_HS20_PPS ) = = NULL ) {
xml_node_get_text_free ( ctx - > xml , supp ) ;
ret = build_post_dev_data_response (
ctx , NULL , session_id , " Error occurred " ,
" One or more mandatory MOs not supported " ) ;
hs20_eventlog_node ( ctx , user , realm , session_id ,
" Unsupported MOs " , ret ) ;
return ret ;
}
xml_node_get_text_free ( ctx - > xml , supp ) ;
req_reason_buf = xml_node_get_attr_value ( ctx - > xml , node ,
" requestReason " ) ;
if ( req_reason_buf = = NULL ) {
debug_print ( ctx , 1 , " No requestReason attribute " ) ;
return NULL ;
}
req_reason = req_reason_buf ;
redirect_uri = xml_node_get_attr_value ( ctx - > xml , node , " redirectURI " ) ;
debug_print ( ctx , 1 , " requestReason: %s sessionID: %s redirectURI: %s " ,
req_reason , session_id , redirect_uri ) ;
snprintf ( str , sizeof ( str ) , " sppPostDevData: requestReason=%s " ,
req_reason ) ;
hs20_eventlog ( ctx , user , realm , session_id , str , NULL ) ;
devinfo = spp_get_mo ( ctx , node , URN_OMA_DM_DEVINFO , & valid , & err ) ;
if ( devinfo = = NULL ) {
ret = build_post_dev_data_response ( ctx , NULL , session_id ,
" Error occurred " , " Other " ) ;
hs20_eventlog_node ( ctx , user , realm , session_id ,
" No DevInfo moContainer in sppPostDevData " ,
ret ) ;
os_free ( err ) ;
goto out ;
}
hs20_eventlog_node ( ctx , user , realm , session_id ,
" Received DevInfo MO " , devinfo ) ;
if ( valid = = 0 ) {
hs20_eventlog ( ctx , user , realm , session_id ,
" OMA-DM DDF DTD validation errors in DevInfo MO " ,
err ) ;
ret = build_post_dev_data_response ( ctx , NULL , session_id ,
" Error occurred " , " Other " ) ;
os_free ( err ) ;
goto out ;
}
os_free ( err ) ;
if ( user )
db_update_mo ( ctx , user , realm , " devinfo " , devinfo ) ;
devdetail = spp_get_mo ( ctx , node , URN_OMA_DM_DEVDETAIL , & valid , & err ) ;
if ( devdetail = = NULL ) {
ret = build_post_dev_data_response ( ctx , NULL , session_id ,
" Error occurred " , " Other " ) ;
hs20_eventlog_node ( ctx , user , realm , session_id ,
" No DevDetail moContainer in sppPostDevData " ,
ret ) ;
os_free ( err ) ;
goto out ;
}
hs20_eventlog_node ( ctx , user , realm , session_id ,
" Received DevDetail MO " , devdetail ) ;
if ( valid = = 0 ) {
hs20_eventlog ( ctx , user , realm , session_id ,
" OMA-DM DDF DTD validation errors "
" in DevDetail MO " , err ) ;
ret = build_post_dev_data_response ( ctx , NULL , session_id ,
" Error occurred " , " Other " ) ;
os_free ( err ) ;
goto out ;
}
os_free ( err ) ;
2018-09-15 01:53:49 +02:00
os_memset ( wifi_mac_addr , 0 , ETH_ALEN ) ;
macaddr = get_node ( ctx - > xml , devdetail ,
" Ext/org.wi-fi/Wi-Fi/Wi-FiMACAddress " ) ;
if ( macaddr ) {
char * addr , buf [ 50 ] ;
addr = xml_node_get_text ( ctx - > xml , macaddr ) ;
if ( addr & & hwaddr_compact_aton ( addr , wifi_mac_addr ) = = 0 ) {
snprintf ( buf , sizeof ( buf ) , " DevDetail MAC address: "
MACSTR , MAC2STR ( wifi_mac_addr ) ) ;
hs20_eventlog ( ctx , user , realm , session_id , buf , NULL ) ;
xml_node_get_text_free ( ctx - > xml , addr ) ;
} else {
hs20_eventlog ( ctx , user , realm , session_id ,
" Could not extract MAC address from DevDetail " ,
NULL ) ;
}
} else {
hs20_eventlog ( ctx , user , realm , session_id ,
" No MAC address in DevDetail " , NULL ) ;
}
2013-03-28 09:27:27 +01:00
if ( user )
db_update_mo ( ctx , user , realm , " devdetail " , devdetail ) ;
if ( user )
mo = spp_get_mo ( ctx , node , URN_HS20_PPS , & valid , & err ) ;
else {
mo = NULL ;
err = NULL ;
}
if ( user & & mo ) {
hs20_eventlog_node ( ctx , user , realm , session_id ,
" Received PPS MO " , mo ) ;
if ( valid = = 0 ) {
hs20_eventlog ( ctx , user , realm , session_id ,
" OMA-DM DDF DTD validation errors "
" in PPS MO " , err ) ;
xml_node_get_attr_value_free ( ctx - > xml , redirect_uri ) ;
os_free ( err ) ;
return build_post_dev_data_response (
ctx , NULL , session_id ,
" Error occurred " , " Other " ) ;
}
db_update_mo ( ctx , user , realm , " pps " , mo ) ;
db_update_val ( ctx , user , realm , " fetch_pps " , " 0 " , dmacc ) ;
xml_node_free ( ctx - > xml , mo ) ;
}
os_free ( err ) ;
if ( user & & ! mo ) {
char * fetch ;
int fetch_pps ;
fetch = db_get_val ( ctx , user , realm , " fetch_pps " , dmacc ) ;
fetch_pps = fetch ? atoi ( fetch ) : 0 ;
free ( fetch ) ;
if ( fetch_pps ) {
enum hs20_session_operation oper ;
if ( strcasecmp ( req_reason , " Subscription remediation " )
= = 0 )
oper = CONTINUE_SUBSCRIPTION_REMEDIATION ;
else if ( strcasecmp ( req_reason , " Policy update " ) = = 0 )
oper = CONTINUE_POLICY_UPDATE ;
else
oper = NO_OPERATION ;
if ( db_add_session ( ctx , user , realm , session_id , NULL ,
2018-09-15 01:53:49 +02:00
NULL , oper , NULL ) < 0 )
2013-03-28 09:27:27 +01:00
goto out ;
ret = spp_exec_upload_mo ( ctx , session_id ,
URN_HS20_PPS ) ;
hs20_eventlog_node ( ctx , user , realm , session_id ,
" request PPS MO upload " ,
ret ) ;
goto out ;
}
}
if ( user & & strcasecmp ( req_reason , " MO upload " ) = = 0 ) {
char * val = db_get_session_val ( ctx , user , realm , session_id ,
" operation " ) ;
enum hs20_session_operation oper ;
if ( ! val ) {
debug_print ( ctx , 1 , " No session %s found to continue " ,
session_id ) ;
goto out ;
}
oper = atoi ( val ) ;
free ( val ) ;
if ( oper = = CONTINUE_SUBSCRIPTION_REMEDIATION )
req_reason = " Subscription remediation " ;
else if ( oper = = CONTINUE_POLICY_UPDATE )
req_reason = " Policy update " ;
else {
debug_print ( ctx , 1 ,
" No pending operation in session %s " ,
session_id ) ;
goto out ;
}
}
if ( strcasecmp ( req_reason , " Subscription registration " ) = = 0 ) {
ret = hs20_subscription_registration ( ctx , realm , session_id ,
2018-09-15 01:53:49 +02:00
redirect_uri ,
wifi_mac_addr ) ;
2013-03-28 09:27:27 +01:00
hs20_eventlog_node ( ctx , user , realm , session_id ,
" subscription registration response " ,
ret ) ;
goto out ;
}
if ( user & & strcasecmp ( req_reason , " Subscription remediation " ) = = 0 ) {
ret = hs20_subscription_remediation ( ctx , user , realm ,
session_id , dmacc ,
redirect_uri ) ;
hs20_eventlog_node ( ctx , user , realm , session_id ,
" subscription remediation response " ,
ret ) ;
goto out ;
}
if ( user & & strcasecmp ( req_reason , " Policy update " ) = = 0 ) {
ret = hs20_policy_update ( ctx , user , realm , session_id , dmacc ) ;
hs20_eventlog_node ( ctx , user , realm , session_id ,
" policy update response " ,
ret ) ;
goto out ;
}
if ( strcasecmp ( req_reason , " User input completed " ) = = 0 ) {
2016-10-28 08:16:27 +02:00
db_add_session_devinfo ( ctx , session_id , devinfo ) ;
db_add_session_devdetail ( ctx , session_id , devdetail ) ;
2013-03-28 09:27:27 +01:00
ret = hs20_user_input_complete ( ctx , user , realm , dmacc ,
session_id ) ;
hs20_eventlog_node ( ctx , user , realm , session_id ,
" user input completed response " , ret ) ;
goto out ;
}
if ( strcasecmp ( req_reason , " Certificate enrollment completed " ) = = 0 ) {
ret = hs20_cert_enroll_completed ( ctx , user , realm , dmacc ,
session_id ) ;
hs20_eventlog_node ( ctx , user , realm , session_id ,
" certificate enrollment response " , ret ) ;
goto out ;
}
if ( strcasecmp ( req_reason , " Certificate enrollment failed " ) = = 0 ) {
ret = hs20_cert_enroll_failed ( ctx , user , realm , dmacc ,
session_id ) ;
hs20_eventlog_node ( ctx , user , realm , session_id ,
" certificate enrollment failed response " ,
ret ) ;
goto out ;
}
2018-12-15 17:00:12 +01:00
if ( strcasecmp ( req_reason , " Subscription provisioning " ) = = 0 ) {
ret = hs20_sim_provisioning ( ctx , user , realm , dmacc ,
session_id ) ;
hs20_eventlog_node ( ctx , user , realm , session_id ,
" subscription provisioning response " ,
ret ) ;
goto out ;
}
2013-03-28 09:27:27 +01:00
debug_print ( ctx , 1 , " Unsupported requestReason '%s' user '%s' " ,
req_reason , user ) ;
out :
xml_node_get_attr_value_free ( ctx - > xml , req_reason_buf ) ;
xml_node_get_attr_value_free ( ctx - > xml , redirect_uri ) ;
if ( devinfo )
xml_node_free ( ctx - > xml , devinfo ) ;
if ( devdetail )
xml_node_free ( ctx - > xml , devdetail ) ;
return ret ;
}
static xml_node_t * build_spp_exchange_complete ( struct hs20_svc * ctx ,
const char * session_id ,
const char * status ,
const char * error_code )
{
xml_namespace_t * ns ;
xml_node_t * spp_node , * node ;
spp_node = xml_node_create_root ( ctx - > xml , SPP_NS_URI , " spp " , & ns ,
" sppExchangeComplete " ) ;
xml_node_add_attr ( ctx - > xml , spp_node , ns , " sppVersion " , " 1.0 " ) ;
xml_node_add_attr ( ctx - > xml , spp_node , ns , " sessionID " , session_id ) ;
xml_node_add_attr ( ctx - > xml , spp_node , ns , " sppStatus " , status ) ;
if ( error_code ) {
node = xml_node_create ( ctx - > xml , spp_node , ns , " sppError " ) ;
xml_node_add_attr ( ctx - > xml , node , NULL , " errorCode " ,
error_code ) ;
}
return spp_node ;
}
static int add_subscription ( struct hs20_svc * ctx , const char * session_id )
{
char * user , * realm , * pw , * pw_mm , * pps , * str ;
2018-12-15 17:00:12 +01:00
char * osu_user , * osu_password , * eap_method ;
2018-12-16 17:33:11 +01:00
char * policy = NULL ;
2013-03-28 09:27:27 +01:00
char * sql ;
int ret = - 1 ;
char * free_account ;
int free_acc ;
char * type ;
int cert = 0 ;
char * cert_pem , * fingerprint ;
2018-12-15 17:00:12 +01:00
const char * method ;
2013-03-28 09:27:27 +01:00
user = db_get_session_val ( ctx , NULL , NULL , session_id , " user " ) ;
realm = db_get_session_val ( ctx , NULL , NULL , session_id , " realm " ) ;
pw = db_get_session_val ( ctx , NULL , NULL , session_id , " password " ) ;
pw_mm = db_get_session_val ( ctx , NULL , NULL , session_id ,
" machine_managed " ) ;
pps = db_get_session_val ( ctx , NULL , NULL , session_id , " pps " ) ;
cert_pem = db_get_session_val ( ctx , NULL , NULL , session_id , " cert_pem " ) ;
fingerprint = db_get_session_val ( ctx , NULL , NULL , session_id , " cert " ) ;
type = db_get_session_val ( ctx , NULL , NULL , session_id , " type " ) ;
if ( type & & strcmp ( type , " cert " ) = = 0 )
cert = 1 ;
free ( type ) ;
2018-12-15 17:00:12 +01:00
osu_user = db_get_session_val ( ctx , NULL , NULL , session_id , " osu_user " ) ;
osu_password = db_get_session_val ( ctx , NULL , NULL , session_id ,
" osu_password " ) ;
eap_method = db_get_session_val ( ctx , NULL , NULL , session_id ,
" eap_method " ) ;
2013-03-28 09:27:27 +01:00
if ( ! user | | ! realm | | ! pw ) {
debug_print ( ctx , 1 , " Could not find session info from DB for "
" the new subscription " ) ;
goto out ;
}
free_account = db_get_osu_config_val ( ctx , realm , " free_account " ) ;
free_acc = free_account & & strcmp ( free_account , user ) = = 0 ;
free ( free_account ) ;
2018-12-16 17:33:11 +01:00
policy = db_get_osu_config_val ( ctx , realm , " sim_policy " ) ;
2013-03-28 09:27:27 +01:00
debug_print ( ctx , 1 ,
" New subscription: user='%s' realm='%s' free_acc=%d " ,
user , realm , free_acc ) ;
debug_print ( ctx , 1 , " New subscription: pps='%s' " , pps ) ;
sql = sqlite3_mprintf ( " UPDATE eventlog SET user=%Q, realm=%Q WHERE "
" sessionid=%Q AND (user='' OR user IS NULL) " ,
user , realm , session_id ) ;
if ( sql ) {
debug_print ( ctx , 1 , " DB: %s " , sql ) ;
if ( sqlite3_exec ( ctx - > db , sql , NULL , NULL , NULL ) ! = SQLITE_OK ) {
debug_print ( ctx , 1 , " Failed to update eventlog in "
" sqlite database: %s " ,
sqlite3_errmsg ( ctx - > db ) ) ;
}
sqlite3_free ( sql ) ;
}
if ( free_acc ) {
hs20_eventlog ( ctx , user , realm , session_id ,
" completed shared free account registration " ,
NULL ) ;
ret = 0 ;
goto out ;
}
2018-09-15 01:53:49 +02:00
str = db_get_session_val ( ctx , NULL , NULL , session_id , " mac_addr " ) ;
2018-12-15 17:00:12 +01:00
if ( eap_method & & eap_method [ 0 ] )
method = eap_method ;
else
method = cert ? " TLS " : " TTLS-MSCHAPV2 " ;
2018-12-16 17:33:11 +01:00
sql = sqlite3_mprintf ( " INSERT INTO users(identity,realm,phase2,methods,cert,cert_pem,machine_managed,mac_addr,osu_user,osu_password,policy) VALUES (%Q,%Q,%d,%Q,%Q,%Q,%d,%Q,%Q,%Q,%Q) " ,
2018-12-03 22:45:32 +01:00
user , realm , cert ? 0 : 1 ,
2018-12-15 17:00:12 +01:00
method ,
2013-03-28 09:27:27 +01:00
fingerprint ? fingerprint : " " ,
cert_pem ? cert_pem : " " ,
2018-09-15 01:53:49 +02:00
pw_mm & & atoi ( pw_mm ) ? 1 : 0 ,
2018-12-15 17:00:12 +01:00
str ? str : " " ,
osu_user ? osu_user : " " ,
2018-12-16 17:33:11 +01:00
osu_password ? osu_password : " " ,
policy ? policy : " " ) ;
2018-09-15 01:53:49 +02:00
free ( str ) ;
2013-03-28 09:27:27 +01:00
if ( sql = = NULL )
goto out ;
debug_print ( ctx , 1 , " DB: %s " , sql ) ;
if ( sqlite3_exec ( ctx - > db , sql , NULL , NULL , NULL ) ! = SQLITE_OK ) {
debug_print ( ctx , 1 , " Failed to add user in sqlite database: %s " ,
sqlite3_errmsg ( ctx - > db ) ) ;
sqlite3_free ( sql ) ;
goto out ;
}
sqlite3_free ( sql ) ;
if ( cert )
ret = 0 ;
else
ret = update_password ( ctx , user , realm , pw , 0 ) ;
if ( ret < 0 ) {
2018-12-03 22:45:32 +01:00
sql = sqlite3_mprintf ( " DELETE FROM users WHERE identity=%Q AND realm=%Q AND (phase2=1 OR methods='TLS') " ,
2013-03-28 09:27:27 +01:00
user , realm ) ;
if ( sql ) {
debug_print ( ctx , 1 , " DB: %s " , sql ) ;
sqlite3_exec ( ctx - > db , sql , NULL , NULL , NULL ) ;
sqlite3_free ( sql ) ;
}
}
if ( pps )
db_update_mo_str ( ctx , user , realm , " pps " , pps ) ;
str = db_get_session_val ( ctx , NULL , NULL , session_id , " devinfo " ) ;
if ( str ) {
db_update_mo_str ( ctx , user , realm , " devinfo " , str ) ;
free ( str ) ;
}
str = db_get_session_val ( ctx , NULL , NULL , session_id , " devdetail " ) ;
if ( str ) {
db_update_mo_str ( ctx , user , realm , " devdetail " , str ) ;
free ( str ) ;
}
2018-09-15 01:53:49 +02:00
if ( cert & & user ) {
const char * serialnum ;
str = db_get_session_val ( ctx , NULL , NULL , session_id ,
" mac_addr " ) ;
if ( os_strncmp ( user , " cert- " , 5 ) = = 0 )
serialnum = user + 5 ;
else
serialnum = " " ;
sql = sqlite3_mprintf ( " INSERT OR REPLACE INTO cert_enroll (mac_addr,user,realm,serialnum) VALUES(%Q,%Q,%Q,%Q) " ,
str ? str : " " , user , realm ? realm : " " ,
serialnum ) ;
free ( str ) ;
if ( sql ) {
debug_print ( ctx , 1 , " DB: %s " , sql ) ;
if ( sqlite3_exec ( ctx - > db , sql , NULL , NULL , NULL ) ! =
SQLITE_OK ) {
debug_print ( ctx , 1 ,
" Failed to add cert_enroll entry into sqlite database: %s " ,
sqlite3_errmsg ( ctx - > db ) ) ;
}
sqlite3_free ( sql ) ;
}
}
2018-12-15 17:00:12 +01:00
str = db_get_session_val ( ctx , NULL , NULL , session_id ,
" mobile_identifier_hash " ) ;
if ( str ) {
sql = sqlite3_mprintf ( " DELETE FROM sim_provisioning WHERE mobile_identifier_hash=%Q " ,
str ) ;
if ( sql ) {
debug_print ( ctx , 1 , " DB: %s " , sql ) ;
if ( sqlite3_exec ( ctx - > db , sql , NULL , NULL , NULL ) ! =
SQLITE_OK ) {
debug_print ( ctx , 1 ,
" Failed to delete pending sim_provisioning entry: %s " ,
sqlite3_errmsg ( ctx - > db ) ) ;
}
sqlite3_free ( sql ) ;
}
os_free ( str ) ;
}
2013-03-28 09:27:27 +01:00
if ( ret = = 0 ) {
hs20_eventlog ( ctx , user , realm , session_id ,
" completed subscription registration " , NULL ) ;
}
out :
free ( user ) ;
free ( realm ) ;
free ( pw ) ;
free ( pw_mm ) ;
free ( pps ) ;
free ( cert_pem ) ;
free ( fingerprint ) ;
2018-12-15 17:00:12 +01:00
free ( osu_user ) ;
free ( osu_password ) ;
free ( eap_method ) ;
2018-12-16 17:33:11 +01:00
os_free ( policy ) ;
2013-03-28 09:27:27 +01:00
return ret ;
}
static xml_node_t * hs20_spp_update_response ( struct hs20_svc * ctx ,
xml_node_t * node ,
const char * user ,
const char * realm ,
const char * session_id ,
int dmacc )
{
char * status ;
xml_node_t * ret ;
char * val ;
enum hs20_session_operation oper ;
status = xml_node_get_attr_value_ns ( ctx - > xml , node , SPP_NS_URI ,
" sppStatus " ) ;
if ( status = = NULL ) {
debug_print ( ctx , 1 , " No sppStatus attribute " ) ;
return NULL ;
}
debug_print ( ctx , 1 , " sppUpdateResponse: sppStatus: %s sessionID: %s " ,
status , session_id ) ;
2018-12-04 13:11:39 +01:00
val = db_get_session_val ( ctx , NULL , NULL , session_id , " operation " ) ;
2013-03-28 09:27:27 +01:00
if ( ! val ) {
debug_print ( ctx , 1 ,
2018-12-04 13:11:39 +01:00
" No session active for sessionID: %s " ,
session_id ) ;
2013-03-28 09:27:27 +01:00
oper = NO_OPERATION ;
} else
oper = atoi ( val ) ;
if ( strcasecmp ( status , " OK " ) = = 0 ) {
char * new_pw = NULL ;
xml_node_get_attr_value_free ( ctx - > xml , status ) ;
if ( oper = = USER_REMEDIATION ) {
new_pw = db_get_session_val ( ctx , user , realm ,
session_id , " password " ) ;
if ( new_pw = = NULL | | strlen ( new_pw ) = = 0 ) {
free ( new_pw ) ;
ret = build_spp_exchange_complete (
ctx , session_id , " Error occurred " ,
" Other " ) ;
hs20_eventlog_node ( ctx , user , realm ,
session_id , " No password "
" had been assigned for "
" session " , ret ) ;
db_remove_session ( ctx , user , realm , session_id ) ;
return ret ;
}
oper = UPDATE_PASSWORD ;
}
if ( oper = = UPDATE_PASSWORD ) {
if ( ! new_pw ) {
new_pw = db_get_session_val ( ctx , user , realm ,
session_id ,
" password " ) ;
if ( ! new_pw ) {
db_remove_session ( ctx , user , realm ,
session_id ) ;
return NULL ;
}
}
debug_print ( ctx , 1 , " Update user '%s' password in DB " ,
user ) ;
if ( update_password ( ctx , user , realm , new_pw , dmacc ) <
0 ) {
debug_print ( ctx , 1 , " Failed to update user "
" '%s' password in DB " , user ) ;
ret = build_spp_exchange_complete (
ctx , session_id , " Error occurred " ,
" Other " ) ;
hs20_eventlog_node ( ctx , user , realm ,
session_id , " Failed to "
" update database " , ret ) ;
db_remove_session ( ctx , user , realm , session_id ) ;
return ret ;
}
hs20_eventlog ( ctx , user , realm ,
session_id , " Updated user password "
" in database " , NULL ) ;
}
2018-12-03 23:11:37 +01:00
if ( oper = = CLEAR_REMEDIATION ) {
debug_print ( ctx , 1 ,
" Clear remediation requirement for user '%s' in DB " ,
user ) ;
if ( clear_remediation ( ctx , user , realm , dmacc ) < 0 ) {
debug_print ( ctx , 1 ,
" Failed to clear remediation requirement for user '%s' in DB " ,
user ) ;
ret = build_spp_exchange_complete (
ctx , session_id , " Error occurred " ,
" Other " ) ;
hs20_eventlog_node ( ctx , user , realm ,
session_id ,
" Failed to update database " ,
ret ) ;
db_remove_session ( ctx , user , realm , session_id ) ;
return ret ;
}
hs20_eventlog ( ctx , user , realm ,
session_id ,
" Cleared remediation requirement in database " ,
NULL ) ;
}
2013-03-28 09:27:27 +01:00
if ( oper = = SUBSCRIPTION_REGISTRATION ) {
if ( add_subscription ( ctx , session_id ) < 0 ) {
debug_print ( ctx , 1 , " Failed to add "
" subscription into DB " ) ;
ret = build_spp_exchange_complete (
ctx , session_id , " Error occurred " ,
" Other " ) ;
hs20_eventlog_node ( ctx , user , realm ,
session_id , " Failed to "
" update database " , ret ) ;
db_remove_session ( ctx , user , realm , session_id ) ;
return ret ;
}
}
if ( oper = = POLICY_REMEDIATION | | oper = = POLICY_UPDATE ) {
char * val ;
val = db_get_val ( ctx , user , realm , " remediation " ,
dmacc ) ;
if ( val & & strcmp ( val , " policy " ) = = 0 )
db_update_val ( ctx , user , realm , " remediation " ,
" " , dmacc ) ;
free ( val ) ;
}
2018-10-19 18:00:37 +02:00
if ( oper = = POLICY_UPDATE )
db_update_val ( ctx , user , realm , " polupd_done " , " 1 " ,
dmacc ) ;
2018-12-04 13:11:39 +01:00
if ( oper = = CERT_REENROLL ) {
char * new_user ;
2019-01-09 23:47:04 +01:00
char event [ 200 ] ;
2018-12-04 13:11:39 +01:00
new_user = db_get_session_val ( ctx , NULL , NULL ,
session_id , " user " ) ;
if ( ! new_user ) {
debug_print ( ctx , 1 ,
" Failed to find new user name (cert-serialnum) " ) ;
ret = build_spp_exchange_complete (
ctx , session_id , " Error occurred " ,
" Other " ) ;
hs20_eventlog_node ( ctx , user , realm ,
session_id ,
" Failed to find new user name (cert reenroll) " ,
ret ) ;
db_remove_session ( ctx , NULL , NULL , session_id ) ;
return ret ;
}
debug_print ( ctx , 1 ,
" Update certificate user entry to use the new serial number (old=%s new=%s) " ,
user , new_user ) ;
2019-01-09 23:47:04 +01:00
os_snprintf ( event , sizeof ( event ) , " renamed user to: %s " ,
new_user ) ;
hs20_eventlog ( ctx , user , realm , session_id , event ,
NULL ) ;
2018-12-04 13:11:39 +01:00
if ( db_update_val ( ctx , user , realm , " identity " ,
new_user , 0 ) < 0 | |
db_update_val ( ctx , new_user , realm , " remediation " ,
" " , 0 ) < 0 ) {
debug_print ( ctx , 1 ,
" Failed to update user name (cert-serialnum) " ) ;
ret = build_spp_exchange_complete (
ctx , session_id , " Error occurred " ,
" Other " ) ;
hs20_eventlog_node ( ctx , user , realm ,
session_id ,
" Failed to update user name (cert reenroll) " ,
ret ) ;
db_remove_session ( ctx , NULL , NULL , session_id ) ;
os_free ( new_user ) ;
return ret ;
}
os_free ( new_user ) ;
}
2013-03-28 09:27:27 +01:00
ret = build_spp_exchange_complete (
ctx , session_id ,
" Exchange complete, release TLS connection " , NULL ) ;
hs20_eventlog_node ( ctx , user , realm , session_id ,
" Exchange completed " , ret ) ;
2018-12-04 13:11:39 +01:00
db_remove_session ( ctx , NULL , NULL , session_id ) ;
2013-03-28 09:27:27 +01:00
return ret ;
}
ret = build_spp_exchange_complete ( ctx , session_id , " Error occurred " ,
" Other " ) ;
hs20_eventlog_node ( ctx , user , realm , session_id , " Error occurred " , ret ) ;
db_remove_session ( ctx , user , realm , session_id ) ;
xml_node_get_attr_value_free ( ctx - > xml , status ) ;
return ret ;
}
# define SPP_SESSION_ID_LEN 16
static char * gen_spp_session_id ( void )
{
FILE * f ;
int i ;
char * session ;
session = os_malloc ( SPP_SESSION_ID_LEN * 2 + 1 ) ;
if ( session = = NULL )
return NULL ;
f = fopen ( " /dev/urandom " , " r " ) ;
if ( f = = NULL ) {
os_free ( session ) ;
return NULL ;
}
for ( i = 0 ; i < SPP_SESSION_ID_LEN ; i + + )
os_snprintf ( session + i * 2 , 3 , " %02x " , fgetc ( f ) ) ;
fclose ( f ) ;
return session ;
}
xml_node_t * hs20_spp_server_process ( struct hs20_svc * ctx , xml_node_t * node ,
const char * auth_user ,
const char * auth_realm , int dmacc )
{
xml_node_t * ret = NULL ;
char * session_id ;
const char * op_name ;
char * xml_err ;
char fname [ 200 ] ;
debug_dump_node ( ctx , " received request " , node ) ;
if ( ! dmacc & & auth_user & & auth_realm ) {
char * real ;
real = db_get_val ( ctx , auth_user , auth_realm , " identity " , 0 ) ;
if ( ! real ) {
real = db_get_val ( ctx , auth_user , auth_realm ,
" identity " , 1 ) ;
if ( real )
dmacc = 1 ;
}
os_free ( real ) ;
}
snprintf ( fname , sizeof ( fname ) , " %s/spp/spp.xsd " , ctx - > root_dir ) ;
if ( xml_validate ( ctx - > xml , node , fname , & xml_err ) < 0 ) {
/*
* We may not be able to extract the sessionID from invalid
* input , but well , we can try .
*/
session_id = xml_node_get_attr_value_ns ( ctx - > xml , node ,
SPP_NS_URI ,
" sessionID " ) ;
2015-03-26 22:39:53 +01:00
debug_print ( ctx , 1 ,
" SPP message failed validation, xsd file: %s xml-error: %s " ,
fname , xml_err ) ;
2013-03-28 09:27:27 +01:00
hs20_eventlog_node ( ctx , auth_user , auth_realm , session_id ,
" SPP message failed validation " , node ) ;
hs20_eventlog ( ctx , auth_user , auth_realm , session_id ,
" Validation errors " , xml_err ) ;
os_free ( xml_err ) ;
xml_node_get_attr_value_free ( ctx - > xml , session_id ) ;
/* TODO: what to return here? */
ret = xml_node_create_root ( ctx - > xml , NULL , NULL , NULL ,
" SppValidationError " ) ;
return ret ;
}
session_id = xml_node_get_attr_value_ns ( ctx - > xml , node , SPP_NS_URI ,
" sessionID " ) ;
if ( session_id ) {
char * tmp ;
debug_print ( ctx , 1 , " Received sessionID %s " , session_id ) ;
tmp = os_strdup ( session_id ) ;
xml_node_get_attr_value_free ( ctx - > xml , session_id ) ;
if ( tmp = = NULL )
return NULL ;
session_id = tmp ;
} else {
session_id = gen_spp_session_id ( ) ;
if ( session_id = = NULL ) {
debug_print ( ctx , 1 , " Failed to generate sessionID " ) ;
return NULL ;
}
debug_print ( ctx , 1 , " Generated sessionID %s " , session_id ) ;
}
op_name = xml_node_get_localname ( ctx - > xml , node ) ;
if ( op_name = = NULL ) {
debug_print ( ctx , 1 , " Could not get op_name " ) ;
return NULL ;
}
if ( strcmp ( op_name , " sppPostDevData " ) = = 0 ) {
hs20_eventlog_node ( ctx , auth_user , auth_realm , session_id ,
" sppPostDevData received and validated " ,
node ) ;
ret = hs20_spp_post_dev_data ( ctx , node , auth_user , auth_realm ,
session_id , dmacc ) ;
} else if ( strcmp ( op_name , " sppUpdateResponse " ) = = 0 ) {
hs20_eventlog_node ( ctx , auth_user , auth_realm , session_id ,
" sppUpdateResponse received and validated " ,
node ) ;
ret = hs20_spp_update_response ( ctx , node , auth_user ,
auth_realm , session_id , dmacc ) ;
} else {
hs20_eventlog_node ( ctx , auth_user , auth_realm , session_id ,
" Unsupported SPP message received and "
" validated " , node ) ;
debug_print ( ctx , 1 , " Unsupported operation '%s' " , op_name ) ;
/* TODO: what to return here? */
ret = xml_node_create_root ( ctx - > xml , NULL , NULL , NULL ,
" SppUnknownCommandError " ) ;
}
os_free ( session_id ) ;
if ( ret = = NULL ) {
/* TODO: what to return here? */
ret = xml_node_create_root ( ctx - > xml , NULL , NULL , NULL ,
" SppInternalError " ) ;
}
return ret ;
}
int hs20_spp_server_init ( struct hs20_svc * ctx )
{
char fname [ 200 ] ;
ctx - > db = NULL ;
snprintf ( fname , sizeof ( fname ) , " %s/AS/DB/eap_user.db " , ctx - > root_dir ) ;
if ( sqlite3_open ( fname , & ctx - > db ) ) {
printf ( " Failed to open sqlite database: %s \n " ,
sqlite3_errmsg ( ctx - > db ) ) ;
sqlite3_close ( ctx - > db ) ;
return - 1 ;
}
return 0 ;
}
void hs20_spp_server_deinit ( struct hs20_svc * ctx )
{
sqlite3_close ( ctx - > db ) ;
ctx - > db = NULL ;
}