[MM-18642] Notify on server certificate trust problems when connecting (#4282)
* MM-18642 Notify user on server certificate trust problems Presents a new alert specific to SSL connection/certificate trouble on the server, when pinging. * Change Alert title (PR review)
This commit is contained in:
parent
901f31ec4b
commit
ee5d336190
3 changed files with 184 additions and 9 deletions
|
|
@ -7,6 +7,7 @@ import PropTypes from 'prop-types';
|
|||
import {intlShape} from 'react-intl';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Alert,
|
||||
DeviceEventEmitter,
|
||||
Image,
|
||||
Keyboard,
|
||||
|
|
@ -103,6 +104,7 @@ export default class SelectServer extends PureComponent {
|
|||
}
|
||||
|
||||
this.certificateListener = DeviceEventEmitter.addListener('RNFetchBlobCertificate', this.selectCertificate);
|
||||
this.sslProblemListener = DeviceEventEmitter.addListener('RNFetchBlobSslProblem', this.handleSslProblem);
|
||||
|
||||
telemetry.end(['start:select_server_screen']);
|
||||
telemetry.save();
|
||||
|
|
@ -131,6 +133,7 @@ export default class SelectServer extends PureComponent {
|
|||
}
|
||||
|
||||
this.certificateListener.remove();
|
||||
this.sslProblemListener.remove();
|
||||
|
||||
this.navigationEventListener.remove();
|
||||
}
|
||||
|
|
@ -367,6 +370,38 @@ export default class SelectServer extends PureComponent {
|
|||
actions.scheduleExpiredNotification(intl);
|
||||
};
|
||||
|
||||
handleSslProblem = () => {
|
||||
if (!this.state.connecting && !this.state.connected) {
|
||||
return null;
|
||||
}
|
||||
|
||||
this.cancelPing();
|
||||
|
||||
const urlParse = require('url-parse');
|
||||
const host = urlParse(this.state.url, true).host || this.state.url;
|
||||
|
||||
const {formatMessage} = this.context.intl;
|
||||
Alert.alert(
|
||||
formatMessage({
|
||||
id: 'mobile.server_ssl.error.title',
|
||||
defaultMessage: 'Untrusted Certificate',
|
||||
}),
|
||||
|
||||
formatMessage({
|
||||
id: 'mobile.server_ssl.error.text',
|
||||
defaultMessage: 'The certificate from {host} is not trusted.\n\nPlease contact your System Administrator to resolve the certificate issues and allow connections to this server.',
|
||||
},
|
||||
{
|
||||
host,
|
||||
}),
|
||||
[
|
||||
{text: 'OK'},
|
||||
],
|
||||
{cancelable: false},
|
||||
);
|
||||
return null;
|
||||
};
|
||||
|
||||
selectCertificate = () => {
|
||||
const url = this.getUrl();
|
||||
RNFetchBlob.cba.selectCertificate((certificate) => {
|
||||
|
|
|
|||
|
|
@ -449,6 +449,8 @@
|
|||
"mobile.server_link.error.title": "Link Error",
|
||||
"mobile.server_link.unreachable_channel.error": "This link belongs to a deleted channel or to a channel to which you do not have access.",
|
||||
"mobile.server_link.unreachable_team.error": "This link belongs to a deleted team or to a team to which you do not have access.",
|
||||
"mobile.server_ssl.error.text": "The certificate from {host} is not trusted.\n\nPlease contact your System Administrator to resolve the certificate issues and allow connections to this server.",
|
||||
"mobile.server_ssl.error.title": "Untrusted Certificate",
|
||||
"mobile.server_upgrade.button": "OK",
|
||||
"mobile.server_upgrade.description": "\nA server upgrade is required to use the Mattermost app. Please ask your System Administrator for details.\n",
|
||||
"mobile.server_upgrade.title": "Server upgrade required",
|
||||
|
|
|
|||
|
|
@ -59,6 +59,18 @@ index adbe48b..b87d383 100644
|
|||
os.write(header.getBytes());
|
||||
byte[] fieldData = field.data.getBytes();
|
||||
os.write(fieldData);
|
||||
diff --git a/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/RNFetchBlobConst.java b/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/RNFetchBlobConst.java
|
||||
index b86902a..f1a16de 100644
|
||||
--- a/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/RNFetchBlobConst.java
|
||||
+++ b/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/RNFetchBlobConst.java
|
||||
@@ -6,6 +6,7 @@ public class RNFetchBlobConst {
|
||||
public static final String EVENT_PROGRESS = "RNFetchBlobProgress";
|
||||
public static final String EVENT_HTTP_STATE = "RNFetchBlobState";
|
||||
public static final String EVENT_MESSAGE = "RNFetchBlobMessage";
|
||||
+ public static final String EVENT_SSL_TRUST_MESSAGE = "RNFetchBlobSslProblem";
|
||||
public static final String FILE_PREFIX = "RNFetchBlob-file://";
|
||||
public static final String CONTENT_PREFIX = "RNFetchBlob-content://";
|
||||
public static final String FILE_PREFIX_BUNDLE_ASSET = "bundle-assets://";
|
||||
diff --git a/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java b/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java
|
||||
index a4d7015..f430865 100644
|
||||
--- a/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java
|
||||
|
|
@ -119,9 +131,18 @@ index a4d7015..f430865 100644
|
|||
* List content of folder
|
||||
* @param path Target folder
|
||||
diff --git a/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java b/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java
|
||||
index a8abd71..ad273ce 100644
|
||||
index a8abd71..9078855 100644
|
||||
--- a/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java
|
||||
+++ b/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java
|
||||
@@ -233,7 +233,7 @@ public class RNFetchBlobReq extends BroadcastReceiver implements Runnable {
|
||||
if (this.options.trusty) {
|
||||
clientBuilder = RNFetchBlobUtils.getUnsafeOkHttpClient(client);
|
||||
} else {
|
||||
- clientBuilder = client.newBuilder();
|
||||
+ clientBuilder = RNFetchBlobUtils.newOkHttpClientBuilder(client); // Emits warning if SSL connection problem encountered.
|
||||
}
|
||||
|
||||
// wifi only, need ACCESS_NETWORK_STATE permission
|
||||
@@ -407,6 +407,7 @@ public class RNFetchBlobReq extends BroadcastReceiver implements Runnable {
|
||||
extended = new RNFetchBlobFileResp(
|
||||
RNFetchBlob.RCTContext,
|
||||
|
|
@ -172,6 +193,106 @@ index a8abd71..ad273ce 100644
|
|||
|
||||
if(rnFetchBlobFileResp != null && !rnFetchBlobFileResp.isDownloadComplete()){
|
||||
callback.invoke("Download interrupted.", null);
|
||||
diff --git a/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/RNFetchBlobUtils.java b/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/RNFetchBlobUtils.java
|
||||
index ab35fdd..69f4b49 100644
|
||||
--- a/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/RNFetchBlobUtils.java
|
||||
+++ b/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/RNFetchBlobUtils.java
|
||||
@@ -6,12 +6,16 @@ import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.cert.CertificateException;
|
||||
+import java.security.KeyStore;
|
||||
+import java.security.NoSuchAlgorithmException;
|
||||
+import java.security.KeyStoreException;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
+import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
@@ -43,14 +47,16 @@ public class RNFetchBlobUtils {
|
||||
|
||||
}
|
||||
|
||||
- public static void emitWarningEvent(String data) {
|
||||
+ public static void emitWarningEvent(String data, String... messageType) {
|
||||
+ String msgType = messageType.length > 0 ? messageType[0] : RNFetchBlobConst.EVENT_MESSAGE;
|
||||
+
|
||||
WritableMap args = Arguments.createMap();
|
||||
args.putString("event", "warn");
|
||||
args.putString("detail", data);
|
||||
|
||||
// emit event to js context
|
||||
RNFetchBlob.RCTContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
||||
- .emit(RNFetchBlobConst.EVENT_MESSAGE, args);
|
||||
+ .emit(msgType, args);
|
||||
}
|
||||
|
||||
public static OkHttpClient.Builder getUnsafeOkHttpClient(OkHttpClient client) {
|
||||
@@ -92,4 +98,59 @@ public class RNFetchBlobUtils {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
+
|
||||
+ // Uses default, secure trust manager for connection validation.
|
||||
+ public static OkHttpClient.Builder newOkHttpClientBuilder(OkHttpClient client) {
|
||||
+ try {
|
||||
+ final X509TrustManager defaultTrustManager = getDefaultTrustManager();
|
||||
+
|
||||
+ final X509TrustManager x509TrustManager = new X509TrustManager() {
|
||||
+ @Override
|
||||
+ public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
|
||||
+ defaultTrustManager.checkClientTrusted(chain, authType);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void checkServerTrusted(java.security.cert.X509Certificate[] chain, final String authType) throws CertificateException {
|
||||
+ try {
|
||||
+ defaultTrustManager.checkServerTrusted(chain, authType);
|
||||
+ } catch(CertificateException ce) {
|
||||
+ emitWarningEvent("RNFetchBlob custom: Server presented problem with SSL handshake.", RNFetchBlobConst.EVENT_SSL_TRUST_MESSAGE);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public java.security.cert.X509Certificate[] getAcceptedIssuers() {
|
||||
+ return defaultTrustManager.getAcceptedIssuers();
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ final TrustManager[] trustManagers = new TrustManager[]{x509TrustManager};
|
||||
+
|
||||
+ final SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
+ sslContext.init(null, trustManagers, new java.security.SecureRandom());
|
||||
+
|
||||
+ final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
|
||||
+ OkHttpClient.Builder builder = client.newBuilder();
|
||||
+ builder.sslSocketFactory(sslSocketFactory, x509TrustManager);
|
||||
+
|
||||
+ return builder;
|
||||
+
|
||||
+ } catch (Exception e) {
|
||||
+ throw new RuntimeException(e);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public static X509TrustManager getDefaultTrustManager() throws NoSuchAlgorithmException, KeyStoreException {
|
||||
+ TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
+ trustManagerFactory.init((KeyStore) null); // Using null here initialises the TrustManagerFactory with the default trust store.
|
||||
+
|
||||
+ X509TrustManager defaultTrustManager = null;
|
||||
+ for (TrustManager tm : trustManagerFactory.getTrustManagers()) {
|
||||
+ if (tm instanceof X509TrustManager) {
|
||||
+ defaultTrustManager = (X509TrustManager) tm;
|
||||
+ }
|
||||
+ }
|
||||
+ return defaultTrustManager;
|
||||
+ }
|
||||
}
|
||||
diff --git a/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/Response/RNFetchBlobFileResp.java b/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/Response/RNFetchBlobFileResp.java
|
||||
index 2470eef..4e92989 100644
|
||||
--- a/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/Response/RNFetchBlobFileResp.java
|
||||
|
|
@ -7351,18 +7472,19 @@ index 671aa69..0522ffc 100644
|
|||
|
||||
@end
|
||||
diff --git a/node_modules/rn-fetch-blob/ios/RNFetchBlobConst.h b/node_modules/rn-fetch-blob/ios/RNFetchBlobConst.h
|
||||
index 7d09c3a..f2ddcdf 100644
|
||||
index 7d09c3a..60a87f8 100644
|
||||
--- a/node_modules/rn-fetch-blob/ios/RNFetchBlobConst.h
|
||||
+++ b/node_modules/rn-fetch-blob/ios/RNFetchBlobConst.h
|
||||
@@ -22,6 +22,7 @@ extern NSString *const EVENT_PROGRESS;
|
||||
@@ -22,6 +22,8 @@ extern NSString *const EVENT_PROGRESS;
|
||||
extern NSString *const EVENT_SERVER_PUSH;
|
||||
extern NSString *const EVENT_PROGRESS_UPLOAD;
|
||||
extern NSString *const EVENT_STATE_CHANGE;
|
||||
+extern NSString *const EVENT_CERTIFICATE_NEEDED;
|
||||
+extern NSString *const EVENT_SSL_HANDSHAKE_PROBLEM;
|
||||
|
||||
extern NSString *const FILE_PREFIX;
|
||||
extern NSString *const ASSET_PREFIX;
|
||||
@@ -51,6 +52,4 @@ extern NSString *const RESP_TYPE_BASE64;
|
||||
@@ -51,6 +53,4 @@ extern NSString *const RESP_TYPE_BASE64;
|
||||
extern NSString *const RESP_TYPE_UTF8;
|
||||
extern NSString *const RESP_TYPE_PATH;
|
||||
|
||||
|
|
@ -7370,14 +7492,15 @@ index 7d09c3a..f2ddcdf 100644
|
|||
-
|
||||
#endif /* RNFetchBlobConst_h */
|
||||
diff --git a/node_modules/rn-fetch-blob/ios/RNFetchBlobConst.m b/node_modules/rn-fetch-blob/ios/RNFetchBlobConst.m
|
||||
index 1376d69..9608be3 100644
|
||||
index 1376d69..cd0832e 100644
|
||||
--- a/node_modules/rn-fetch-blob/ios/RNFetchBlobConst.m
|
||||
+++ b/node_modules/rn-fetch-blob/ios/RNFetchBlobConst.m
|
||||
@@ -26,6 +26,7 @@
|
||||
@@ -26,6 +26,8 @@
|
||||
NSString *const EVENT_PROGRESS = @"RNFetchBlobProgress";
|
||||
NSString *const EVENT_PROGRESS_UPLOAD = @"RNFetchBlobProgress-upload";
|
||||
NSString *const EVENT_EXPIRE = @"RNFetchBlobExpire";
|
||||
+NSString *const EVENT_CERTIFICATE_NEEDED = @"RNFetchBlobCertificate";
|
||||
+NSString *const EVENT_SSL_HANDSHAKE_PROBLEM = @"RNFetchBlobSslProblem";
|
||||
|
||||
NSString *const MSG_EVENT = @"RNFetchBlobMessage";
|
||||
NSString *const MSG_EVENT_LOG = @"log";
|
||||
|
|
@ -7478,7 +7601,7 @@ index b550ac2..b603f44 100644
|
|||
- (void) sendRequest:(NSDictionary * _Nullable )options
|
||||
contentLength:(long)contentLength
|
||||
diff --git a/node_modules/rn-fetch-blob/ios/RNFetchBlobRequest.m b/node_modules/rn-fetch-blob/ios/RNFetchBlobRequest.m
|
||||
index cdbe6b1..094c506 100644
|
||||
index cdbe6b1..6ad5846 100644
|
||||
--- a/node_modules/rn-fetch-blob/ios/RNFetchBlobRequest.m
|
||||
+++ b/node_modules/rn-fetch-blob/ios/RNFetchBlobRequest.m
|
||||
@@ -51,7 +51,6 @@ @implementation RNFetchBlobRequest
|
||||
|
|
@ -7529,7 +7652,22 @@ index cdbe6b1..094c506 100644
|
|||
NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
|
||||
NSString * respType = @"";
|
||||
respStatus = statusCode;
|
||||
@@ -453,6 +471,30 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSen
|
||||
@@ -381,6 +399,14 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCom
|
||||
}
|
||||
|
||||
if (error) {
|
||||
+ if ([error.domain isEqualToString:NSURLErrorDomain] && (error.code == NSURLErrorServerCertificateUntrusted) ) {
|
||||
+ [self.bridge.eventDispatcher
|
||||
+ sendDeviceEventWithName:EVENT_SSL_HANDSHAKE_PROBLEM
|
||||
+ body: nil
|
||||
+ ];
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
if (error.domain == NSURLErrorDomain && error.code == NSURLErrorCancelled) {
|
||||
errMsg = @"task cancelled";
|
||||
} else {
|
||||
@@ -453,6 +479,30 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSen
|
||||
|
||||
- (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable credantial))completionHandler
|
||||
{
|
||||
|
|
@ -7560,7 +7698,7 @@ index cdbe6b1..094c506 100644
|
|||
if ([[options valueForKey:CONFIG_TRUSTY] boolValue]) {
|
||||
completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
|
||||
} else {
|
||||
@@ -480,5 +522,4 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPe
|
||||
@@ -480,5 +530,4 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPe
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue