package de.tudarmstadt.informatik.hostage.sync.android; import android.accounts.Account; import android.accounts.AccountManager; import android.content.ContentResolver; import android.content.Context; import android.content.SharedPreferences; import android.location.Address; import android.location.Geocoder; import android.location.Location; import android.os.Bundle; import android.preference.PreferenceManager; import android.telephony.TelephonyManager; import android.util.Log; import org.apache.http.HttpResponse; import org.apache.http.HttpVersion; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpParams; import org.apache.http.params.HttpProtocolParams; import org.apache.http.protocol.HTTP; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.net.URLEncoder; import java.security.KeyStore; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import de.tudarmstadt.informatik.hostage.location.MyLocationManager; import de.tudarmstadt.informatik.hostage.logging.MessageRecord; import de.tudarmstadt.informatik.hostage.logging.NetworkRecord; import de.tudarmstadt.informatik.hostage.logging.Record; import de.tudarmstadt.informatik.hostage.logging.SyncData; import de.tudarmstadt.informatik.hostage.logging.SyncInfo; import de.tudarmstadt.informatik.hostage.logging.SyncRecord; import de.tudarmstadt.informatik.hostage.net.MySSLSocketFactory; import de.tudarmstadt.informatik.hostage.sync.Synchronizer; import de.tudarmstadt.informatik.hostage.ui.activity.MainActivity; /** * Created by abrakowski */ public class SyncUtils { public static final int SYNC_SUCCESSFUL = 0x0; private static final long SYNC_FREQUENCY_MINUTES = 5; private static final long SYNC_FREQUENCY_UNIT = 60; private static final long SYNC_FREQUENCY = SYNC_FREQUENCY_UNIT * SYNC_FREQUENCY_MINUTES; // 5 min (in seconds) public static final String CONTENT_AUTHORITY = "de.tudarmstadt.informatik.hostage.androidsync"; private static final String PREF_SETUP_COMPLETE = "sync_setup_complete"; private static final String PREF_SYNC_INTERNAL_FREQUENCY = "pref_sync_internal_frequency"; private static final String PREF_SYNC_FREQUENCY = "pref_sync_frequency"; private static final Map protocolsTypeMap; static { protocolsTypeMap = new HashMap(); protocolsTypeMap.put("UNKNOWN", 0); protocolsTypeMap.put("ECHO", 1); protocolsTypeMap.put("GHOST", 2); protocolsTypeMap.put("PORTSCAN", 11); protocolsTypeMap.put("SSH", 20); protocolsTypeMap.put("MySQL", 31); protocolsTypeMap.put("SMB", 40); protocolsTypeMap.put("SIP", 50); protocolsTypeMap.put("FTP", 60); protocolsTypeMap.put("HTTP", 70); protocolsTypeMap.put("HTTPS", 71); protocolsTypeMap.put("TELNET", 80); } /** * Create an entry for this application in the system account list, if it isn't already there. * * @param context Context */ public static void CreateSyncAccount(Context context) { boolean newAccount = false; SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); boolean setupComplete = preferences.getBoolean(PREF_SETUP_COMPLETE, false); // Create account, if it's missing. (Either first run, or user has deleted account.) Account account = HostageAccountService.GetAccount(); AccountManager accountManager = (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE); if (accountManager.addAccountExplicitly(account, null, null)) { // Inform the system that this account supports sync ContentResolver.setIsSyncable(account, CONTENT_AUTHORITY, 1); // Inform the system that this account is eligible for auto sync when the network is up ContentResolver.setSyncAutomatically(account, CONTENT_AUTHORITY, true); // Recommend a schedule for automatic synchronization. The system may modify this based // on other scheduled syncs and network utilization. SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context); long syncFrequency = pref.getInt("pref_sync_frequency", 5*60); // default is 5min ContentResolver.addPeriodicSync( account, CONTENT_AUTHORITY, new Bundle(), SYNC_FREQUENCY); preferences.edit().putLong(PREF_SYNC_INTERNAL_FREQUENCY, SYNC_FREQUENCY).commit(); newAccount = true; } // Schedule an initial sync if we detect problems with either our account or our local // data has been deleted. (Note that it's possible to clear app data WITHOUT affecting // the account list, so wee need to check both.) if (newAccount || !setupComplete) { TriggerRefresh(); preferences.edit().putBoolean(PREF_SETUP_COMPLETE, true).commit(); } if(setupComplete){ long syncFrequency = Long.valueOf(preferences.getString(PREF_SYNC_FREQUENCY, "" + SYNC_FREQUENCY_MINUTES)) * SYNC_FREQUENCY_UNIT; long internalFrequency = preferences.getLong(PREF_SYNC_INTERNAL_FREQUENCY, SYNC_FREQUENCY); if(syncFrequency != internalFrequency){ ContentResolver.removePeriodicSync(account, CONTENT_AUTHORITY, new Bundle()); ContentResolver.addPeriodicSync(account, CONTENT_AUTHORITY, new Bundle(), syncFrequency); preferences.edit().putLong(PREF_SYNC_INTERNAL_FREQUENCY, syncFrequency).commit(); } } } public static void TriggerRefresh() { Bundle b = new Bundle(); // Disable sync backoff and ignore sync preferences. In other words...perform sync NOW! b.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); b.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); ContentResolver.requestSync( HostageAccountService.GetAccount(), // Sync account CONTENT_AUTHORITY, // Content authority b); // Extras } public static String getProtocolFromInt(int p){ for(Map.Entry entry: protocolsTypeMap.entrySet()){ if(entry.getValue() == p) return entry.getKey(); } return "UNKNOWN"; } public static void appendRecordToStringWriter(Record record, Writer stream){ try { stream.append( "{" + "\"sensor\":{" + "\"name\":\"HosTaGe\"," + "\"type\":\"Honeypot\"" + "}," + "\"src\":{" + "\"ip\":\"" + record.getRemoteIP() + "\"," + "\"port\":" + record.getRemotePort() + "}," + "\"dst\":{" + "\"ip\":\"" + record.getLocalIP() + "\"," + "\"port\":" + record.getLocalPort() + "}," + "\"type\":" + (protocolsTypeMap.containsKey(record.getProtocol()) ? protocolsTypeMap.get(record.getProtocol()) : 0) + "," + "\"log\":\"" + record.getProtocol() + "\"," + "\"md5sum\":\"\"," + "\"date\":" + (int)(record.getTimestamp() / 1000) + "," + "\"bssid\":\"" + record.getBssid() + "\"," + "\"ssid\":\"" + record.getSsid() + "\"," + "\"device\":\"" + record.getDevice() + "\"," + "\"sync_id\":\"" + record.getSync_id() + "\"," + "\"internal_attack\":\"" + record.getWasInternalAttack() + "\"," + "\"external_ip\":\"" + record.getExternalIP() + "\"" + "}\n" ); } catch (IOException e) { e.printStackTrace(); } } public static boolean uploadRecordsToServer(String entity, String serverAddress){ HttpPost httppost; try { HttpClient httpClient = createHttpClient(); // Create HttpPost httppost = new HttpPost(serverAddress); StringEntity se = new StringEntity(entity); httppost.addHeader("content-type", "application/json+newline"); httppost.setEntity(se); // Execute HttpPost HttpResponse response = httpClient.execute(httppost); if(response.getStatusLine().getStatusCode() >= 400 && response.getStatusLine().getStatusCode() < 600){ return false; } Log.i("TracingSyncService", "Status Code: " + response.getStatusLine().getStatusCode()); } catch (Exception e) { e.printStackTrace(); return false; } return true; } public static T downloadFromServer(String address, Class klass){ HttpGet httpget; try { HttpClient httpClient = createHttpClient(); httpget = new HttpGet(address); httpget.addHeader("Accept", "application/json"); HttpResponse response = httpClient.execute(httpget); Log.i("downloadFromServer", "Status Code: " + response.getStatusLine().getStatusCode()); if(response.getStatusLine().getStatusCode() >= 400 && response.getStatusLine().getStatusCode() < 600){ return klass.newInstance(); } return klass.getConstructor(klass).newInstance(readResponseToString(response)); } catch (Exception e) { e.printStackTrace(); return null; } } public static String readResponseToString(HttpResponse response){ StringBuilder builder = new StringBuilder(); try { BufferedReader bReader = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); String line; while ((line = bReader.readLine()) != null) { builder.append(line); } } catch (IOException e) { e.printStackTrace(); } return builder.toString(); } public static SyncData getSyncDataFromTracing(Context context, Synchronizer synchronizer, long fromTime){ SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context); String serverAddress = pref.getString("pref_upload_server", "https://ssi.cased.de"); HttpPost httppost; try { HttpClient httpClient = createHttpClient(); // Create HttpPost httppost = new HttpPost(serverAddress + "/sync"); SyncInfo info = synchronizer.getSyncInfo(); JSONArray deviceMap = new JSONArray(); for(Map.Entry entry: info.deviceMap.entrySet()){ JSONObject m = new JSONObject(); m.put("sync_id", entry.getValue()); m.put("device", entry.getKey()); deviceMap.put(m); } JSONObject condition = new JSONObject(); /*if(fromTime > 0){ Calendar calendar = GregorianCalendar.getInstance(); calendar.setTimeInMillis(fromTime); condition.put("date", fromCalendar(calendar)); }*/ String country = null; Location location = MyLocationManager.getNewestLocation(); if(location != null){ Geocoder geocoder = new Geocoder(context, Locale.getDefault()); List
fromLocation = geocoder.getFromLocation(location.getLatitude(), location.getLongitude(), 1); if(fromLocation.size() > 0){ Address address = fromLocation.get(0); country = address.getCountryCode(); } } if(country == null){ // We could not get the gps coordinates, try to retrieve the country code from the SIM card TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); country = tm.getNetworkCountryIso(); } if(country == null || country.trim().isEmpty()) country = Locale.getDefault().getCountry(); condition.put("country", country); JSONObject req = new JSONObject(); req.put("condition", condition); req.put("info", deviceMap); StringEntity se = new StringEntity(req.toString()); httppost.addHeader("content-type", "application/json"); httppost.setEntity(se); // Execute HttpPost HttpResponse response = httpClient.execute(httppost); Log.i("TracingSyncService", "Status Code: " + response.getStatusLine().getStatusCode()); if(response.getStatusLine().getStatusCode() >= 400 && response.getStatusLine().getStatusCode() < 600){ return null; } String responseBody = readResponseToString(response); JSONArray syncData; // ensure, that the received data is an array try { syncData = new JSONArray(responseBody); } catch (JSONException ex){ ex.printStackTrace(); return null; } ArrayList syncRecords = new ArrayList(); Map networkRecordMap = new HashMap(); SyncData result = new SyncData(); for(int i=0; i(networkRecordMap.values()); result.syncRecords = syncRecords; return result; } catch (Exception e) { e.printStackTrace(); return null; } } public static String urlEncodeUTF8(String s) { try { return URLEncoder.encode(s, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new UnsupportedOperationException(e); } } public static String[] convertMapToStringArray(Map map){ String[] array = new String[map.size() * 2]; int i = 0; for(Map.Entry entry: map.entrySet()){ array[i] = entry.getKey(); array[i + 1] = entry.getValue(); i += 2; } return array; } public static String buildUrlFromBase(String baseUrl, String... query){ StringBuilder sb = new StringBuilder(baseUrl); if(query.length >= 2){ sb.append("?"); } for(int i=0; i 0){ sb.append("&"); } sb.append(String.format("%s=%s", urlEncodeUTF8(query[i]), urlEncodeUTF8(query[i + 1]) )); } return sb.toString(); } public static String buildUrlFromBase(String baseUrl, Map query){ return buildUrlFromBase(baseUrl, convertMapToStringArray(query)); } public static String buildUrl(String protocol, String domain, int port, String path, String ... query){ return buildUrlFromBase( String.format("%s://%s:%d/%s", urlEncodeUTF8(protocol), urlEncodeUTF8(domain), port, path), query ); } public static String buildUrl(String protocol, String domain, int port, String path, Map query){ return buildUrl(protocol, domain, port, path, convertMapToStringArray(query)); } public static List getCountriesFromServer(String serverAddress){ List ret = new ArrayList(); JSONArray array = downloadFromServer(serverAddress + "/get_countries", JSONArray.class); try { for (int i = 0; i < array.length(); i++) { JSONObject ob = array.getJSONObject(i); ret.add(new String[]{ob.getString("cc"), ob.getString("country")}); } } catch(Exception e){ e.printStackTrace(); } return ret; } public static String fromCalendar(final Calendar calendar) { Date date = calendar.getTime(); String formatted = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ") .format(date); return formatted.substring(0, 22) + ":" + formatted.substring(22); } public static Calendar toCalendar(final String iso8601string) throws ParseException { Calendar calendar = GregorianCalendar.getInstance(); String s = iso8601string.replace("Z", "0+0000"); try { s = s.substring(0, 22) + s.substring(23); // to get rid of the ":" } catch (IndexOutOfBoundsException e) { throw new ParseException("Invalid length", 0); } Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").parse(s); calendar.setTime(date); return calendar; } public static JSONArray retrieveNewAttacks(Context context, boolean fromPosition){ SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context); String serverAddress = pref.getString("pref_download_server", "http://ssi.cased.de/api"); long lastDownloadTime = pref.getLong("pref_download_last_time", 0); Calendar calendar = GregorianCalendar.getInstance(); calendar.setTimeInMillis(lastDownloadTime); String baseUri = serverAddress + "/get_attacks"; Map query = new HashMap(); query.put("start", fromCalendar(calendar)); if(fromPosition){ Location location = MyLocationManager.getNewestLocation(); if(location != null) { query.put("latitude", String.valueOf(location.getLatitude())); query.put("longitude", String.valueOf(location.getLongitude())); query.put("distance", "300"); } } return downloadFromServer(buildUrlFromBase(baseUri, "start", fromCalendar(calendar)), JSONArray.class); } public static HttpClient createHttpClient() { try { KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); trustStore.load(null, null); SSLSocketFactory sf = new MySSLSocketFactory(trustStore); sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); HttpParams params = new BasicHttpParams(); HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); HttpProtocolParams.setContentCharset(params, HTTP.UTF_8); SchemeRegistry registry = new SchemeRegistry(); registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); registry.register(new Scheme("https", sf, 443)); ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry); return new DefaultHttpClient(ccm, params); } catch (Exception e) { e.printStackTrace(); return new DefaultHttpClient(); } } }