Harden parsing of keyserver results (OKC-01-003)
This commit is contained in:
@@ -77,7 +77,7 @@ public class CloudSearch {
|
|||||||
// kill threads that haven't returned yet
|
// kill threads that haven't returned yet
|
||||||
thread.interrupt();
|
thread.interrupt();
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import com.squareup.okhttp.OkHttpClient;
|
|||||||
import com.squareup.okhttp.Request;
|
import com.squareup.okhttp.Request;
|
||||||
import com.squareup.okhttp.RequestBody;
|
import com.squareup.okhttp.RequestBody;
|
||||||
import com.squareup.okhttp.Response;
|
import com.squareup.okhttp.Response;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
||||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||||
@@ -196,9 +197,9 @@ public class HkpKeyserver extends Keyserver {
|
|||||||
/**
|
/**
|
||||||
* returns a client with pinned certificate if necessary
|
* returns a client with pinned certificate if necessary
|
||||||
*
|
*
|
||||||
* @param url url to be queried by client
|
* @param url url to be queried by client
|
||||||
* @param proxy proxy to be used by client
|
* @param proxy proxy to be used by client
|
||||||
* @return client with a pinned certificate if necesary
|
* @return client with a pinned certificate if necessary
|
||||||
*/
|
*/
|
||||||
public static OkHttpClient getClient(URL url, Proxy proxy) throws IOException {
|
public static OkHttpClient getClient(URL url, Proxy proxy) throws IOException {
|
||||||
OkHttpClient client = new OkHttpClient();
|
OkHttpClient client = new OkHttpClient();
|
||||||
@@ -232,7 +233,7 @@ public class HkpKeyserver extends Keyserver {
|
|||||||
OkHttpClient client = getClient(url, proxy);
|
OkHttpClient client = getClient(url, proxy);
|
||||||
Response response = client.newCall(new Request.Builder().url(url).build()).execute();
|
Response response = client.newCall(new Request.Builder().url(url).build()).execute();
|
||||||
|
|
||||||
String responseBody = response.body().string();// contains body both in case of success or failure
|
String responseBody = response.body().string(); // contains body both in case of success or failure
|
||||||
|
|
||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
return responseBody;
|
return responseBody;
|
||||||
@@ -242,17 +243,12 @@ public class HkpKeyserver extends Keyserver {
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(Constants.TAG, "IOException at HkpKeyserver", e);
|
Log.e(Constants.TAG, "IOException at HkpKeyserver", e);
|
||||||
throw new QueryFailedException("Keyserver '" + mHost + "' is unavailable. Check your Internet connection!" +
|
throw new QueryFailedException("Keyserver '" + mHost + "' is unavailable. Check your Internet connection!" +
|
||||||
proxy == null?"":" Using proxy " + proxy);
|
(proxy == null ? "" : " Using proxy " + proxy));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Results are sorted by creation date of key!
|
* Results are sorted by creation date of key!
|
||||||
*
|
|
||||||
* @param query
|
|
||||||
* @return
|
|
||||||
* @throws QueryFailedException
|
|
||||||
* @throws QueryNeedsRepairException
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ArrayList<ImportKeysListEntry> search(String query, Proxy proxy) throws QueryFailedException,
|
public ArrayList<ImportKeysListEntry> search(String query, Proxy proxy) throws QueryFailedException,
|
||||||
@@ -303,30 +299,46 @@ public class HkpKeyserver extends Keyserver {
|
|||||||
entry.setQuery(query);
|
entry.setQuery(query);
|
||||||
entry.addOrigin(getUrlPrefix() + mHost + ":" + mPort);
|
entry.addOrigin(getUrlPrefix() + mHost + ":" + mPort);
|
||||||
|
|
||||||
int bitSize = Integer.parseInt(matcher.group(3));
|
|
||||||
entry.setBitStrength(bitSize);
|
|
||||||
int algorithmId = Integer.decode(matcher.group(2));
|
|
||||||
entry.setAlgorithm(KeyFormattingUtils.getAlgorithmInfo(algorithmId, bitSize, null));
|
|
||||||
|
|
||||||
// group 1 contains the full fingerprint (v4) or the long key id if available
|
// group 1 contains the full fingerprint (v4) or the long key id if available
|
||||||
// see https://bitbucket.org/skskeyserver/sks-keyserver/pull-request/12/fixes-for-machine-readable-indexes/diff
|
// see https://bitbucket.org/skskeyserver/sks-keyserver/pull-request/12/fixes-for-machine-readable-indexes/diff
|
||||||
String fingerprintOrKeyId = matcher.group(1).toLowerCase(Locale.ENGLISH);
|
String fingerprintOrKeyId = matcher.group(1).toLowerCase(Locale.ENGLISH);
|
||||||
if (fingerprintOrKeyId.length() > 16) {
|
if (fingerprintOrKeyId.length() == 40) {
|
||||||
entry.setFingerprintHex(fingerprintOrKeyId);
|
entry.setFingerprintHex(fingerprintOrKeyId);
|
||||||
entry.setKeyIdHex("0x" + fingerprintOrKeyId.substring(fingerprintOrKeyId.length()
|
entry.setKeyIdHex("0x" + fingerprintOrKeyId.substring(fingerprintOrKeyId.length()
|
||||||
- 16, fingerprintOrKeyId.length()));
|
- 16, fingerprintOrKeyId.length()));
|
||||||
} else {
|
} else if (fingerprintOrKeyId.length() == 16) {
|
||||||
// set key id only
|
// set key id only
|
||||||
entry.setKeyIdHex("0x" + fingerprintOrKeyId);
|
entry.setKeyIdHex("0x" + fingerprintOrKeyId);
|
||||||
|
} else {
|
||||||
|
Log.e(Constants.TAG, "Wrong length for fingerprint/long key id.");
|
||||||
|
// skip this key
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
final long creationDate = Long.parseLong(matcher.group(4));
|
try {
|
||||||
final GregorianCalendar tmpGreg = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
int bitSize = Integer.parseInt(matcher.group(3));
|
||||||
tmpGreg.setTimeInMillis(creationDate * 1000);
|
entry.setBitStrength(bitSize);
|
||||||
entry.setDate(tmpGreg.getTime());
|
int algorithmId = Integer.decode(matcher.group(2));
|
||||||
|
entry.setAlgorithm(KeyFormattingUtils.getAlgorithmInfo(algorithmId, bitSize, null));
|
||||||
|
|
||||||
entry.setRevoked(matcher.group(6).contains("r"));
|
final long creationDate = Long.parseLong(matcher.group(4));
|
||||||
entry.setExpired(matcher.group(6).contains("e"));
|
final GregorianCalendar tmpGreg = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
||||||
|
tmpGreg.setTimeInMillis(creationDate * 1000);
|
||||||
|
entry.setDate(tmpGreg.getTime());
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Log.e(Constants.TAG, "Conversation for bit size, algorithm, or creation date failed.", e);
|
||||||
|
// skip this key
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
entry.setRevoked(matcher.group(6).contains("r"));
|
||||||
|
entry.setExpired(matcher.group(6).contains("e"));
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
Log.e(Constants.TAG, "Check for revocation or expiry failed.", e);
|
||||||
|
// skip this key
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ArrayList<String> userIds = new ArrayList<>();
|
ArrayList<String> userIds = new ArrayList<>();
|
||||||
final String uidLines = matcher.group(7);
|
final String uidLines = matcher.group(7);
|
||||||
@@ -344,6 +356,10 @@ public class HkpKeyserver extends Keyserver {
|
|||||||
tmp = URLDecoder.decode(tmp, "UTF8");
|
tmp = URLDecoder.decode(tmp, "UTF8");
|
||||||
} catch (UnsupportedEncodingException ignored) {
|
} catch (UnsupportedEncodingException ignored) {
|
||||||
// will never happen, because "UTF8" is supported
|
// will never happen, because "UTF8" is supported
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
Log.e(Constants.TAG, "User ID encoding broken", e);
|
||||||
|
// skip this user id
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
userIds.add(tmp);
|
userIds.add(tmp);
|
||||||
@@ -367,11 +383,14 @@ public class HkpKeyserver extends Keyserver {
|
|||||||
Log.d(Constants.TAG, "Failed to get key at HkpKeyserver", httpError);
|
Log.d(Constants.TAG, "Failed to get key at HkpKeyserver", httpError);
|
||||||
throw new QueryFailedException("not found");
|
throw new QueryFailedException("not found");
|
||||||
}
|
}
|
||||||
|
if (data == null) {
|
||||||
|
throw new QueryFailedException("data is null");
|
||||||
|
}
|
||||||
Matcher matcher = PgpHelper.PGP_PUBLIC_KEY.matcher(data);
|
Matcher matcher = PgpHelper.PGP_PUBLIC_KEY.matcher(data);
|
||||||
if (matcher.find()) {
|
if (matcher.find()) {
|
||||||
return matcher.group(1);
|
return matcher.group(1);
|
||||||
}
|
}
|
||||||
return null;
|
throw new QueryFailedException("data is null");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -422,7 +441,7 @@ public class HkpKeyserver extends Keyserver {
|
|||||||
* Tries to find a server responsible for a given domain
|
* Tries to find a server responsible for a given domain
|
||||||
*
|
*
|
||||||
* @return A responsible Keyserver or null if not found.
|
* @return A responsible Keyserver or null if not found.
|
||||||
* TODO: PHILIP Add proxy functionality
|
* TODO: Add proxy functionality
|
||||||
*/
|
*/
|
||||||
public static HkpKeyserver resolve(String domain) {
|
public static HkpKeyserver resolve(String domain) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -62,15 +62,15 @@ public abstract class Keyserver {
|
|||||||
* query too short _or_ too many responses
|
* query too short _or_ too many responses
|
||||||
*/
|
*/
|
||||||
public static class QueryTooShortOrTooManyResponsesException extends QueryNeedsRepairException {
|
public static class QueryTooShortOrTooManyResponsesException extends QueryNeedsRepairException {
|
||||||
private static final long serialVersionUID = 2703768928624654514L;
|
private static final long serialVersionUID = 2703768928624654518L;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class AddKeyException extends Exception {
|
public static class AddKeyException extends Exception {
|
||||||
private static final long serialVersionUID = -507574859137295530L;
|
private static final long serialVersionUID = -507574859137295530L;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract List<ImportKeysListEntry> search(String query, Proxy proxy) throws QueryFailedException,
|
public abstract List<ImportKeysListEntry> search(String query, Proxy proxy)
|
||||||
QueryNeedsRepairException;
|
throws QueryFailedException, QueryNeedsRepairException;
|
||||||
|
|
||||||
public abstract String get(String keyIdHex, Proxy proxy) throws QueryFailedException;
|
public abstract String get(String keyIdHex, Proxy proxy) throws QueryFailedException;
|
||||||
|
|
||||||
|
|||||||
@@ -103,8 +103,7 @@ public class EmailKeyHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Keyserver.QueryFailedException ignored) {
|
} catch (Keyserver.CloudSearchFailureException ignored) {
|
||||||
} catch (Keyserver.QueryNeedsRepairException ignored) {
|
|
||||||
}
|
}
|
||||||
return new ArrayList<>(keys);
|
return new ArrayList<>(keys);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user