Implement certificate checking.

This commit is contained in:
Florian Zeitz 2011-11-22 23:07:01 +01:00
parent cfb0c5f9bd
commit 4aaa754a4d
3 changed files with 86 additions and 39 deletions

View file

@ -31,6 +31,7 @@
@class XMPPAuthenticator;
@class XMPPRoster;
@class XMPPRosterItem;
@class SSLSocket;
@protocol XMPPConnectionDelegate
#ifndef XMPP_CONNECTION_M
@ -64,10 +65,11 @@
<OFXMLParserDelegate, OFXMLElementBuilderDelegate>
#endif
{
OFTCPSocket *sock;
SSLSocket *sock;
OFXMLParser *parser, *oldParser;
OFXMLElementBuilder *elementBuilder, *oldElementBuilder;
OFString *username, *password, *server, *domain, *resource;
OFString *username, *password, *server, *resource;
OFString *domain, *domainToASCII;
XMPPJID *JID;
uint16_t port;
id <XMPPConnectionDelegate, OFObject> delegate;
@ -100,6 +102,12 @@
*/
- (void)connect;
/**
* Checks the certificate presented by the server.
* Throws SSLInvalidCertificateException on failure.
*/
- (void)checkCertificate;
/**
* Starts a loop handling incomming data.
*/
@ -184,6 +192,7 @@
- (void)XMPP_handleResourceBind: (XMPPIQ*)iq;
- (void)XMPP_sendSession;
- (void)XMPP_handleSession: (XMPPIQ*)iq;
- (OFString*)XMPP_IDNAToASCII: (OFString*)domain;
@end
@interface OFObject (XMPPConnectionDelegate) <XMPPConnectionDelegate>

View file

@ -33,6 +33,8 @@
#include <idna.h>
#import <ObjOpenSSL/SSLSocket.h>
#import <ObjOpenSSL/SSLInvalidCertificateException.h>
#import <ObjOpenSSL/X509Certificate.h>
#import "XMPPConnection.h"
#import "XMPPSRVLookup.h"
@ -148,23 +150,7 @@
- (void)setServer: (OFString*)server_
{
OFString *old = server;
char *srv;
Idna_rc rc;
if ((rc = idna_to_ascii_8z([server_ UTF8String],
&srv, IDNA_USE_STD3_ASCII_RULES)) != IDNA_SUCCESS)
@throw [XMPPIDNATranslationFailedException
exceptionWithClass: isa
connection: self
operation: @"ToASCII"
string: server_];
@try {
server = [[OFString alloc] initWithUTF8String: srv];
} @finally {
free(srv);
}
server = [self XMPP_IDNAToASCII: server_];
[old release];
}
@ -175,7 +161,8 @@
- (void)setDomain: (OFString*)domain_
{
OFString *old = domain;
OFString *oldDomain = domain;
OFString *oldDomainToASCII = domainToASCII;
char *srv;
Stringprep_rc rc;
@ -192,8 +179,10 @@
} @finally {
free(srv);
}
[oldDomain release];
[old release];
domainToASCII = [self XMPP_IDNAToASCII: domain];
[oldDomainToASCII release];
}
- (OFString*)domain
@ -235,29 +224,11 @@
XMPPSRVEntry *candidate = nil;
XMPPSRVLookup *SRVLookup = nil;
OFEnumerator *enumerator;
OFString *domainToASCII;
char *cDomainToASCII;
Idna_rc rc;
if (server)
[sock connectToHost: server
port: port];
else {
if ((rc = idna_to_ascii_8z([domain UTF8String], &cDomainToASCII,
IDNA_USE_STD3_ASCII_RULES)) != IDNA_SUCCESS)
@throw [XMPPIDNATranslationFailedException
exceptionWithClass: isa
connection: self
operation: @"ToASCII"
string: domain];
@try {
domainToASCII = [OFString
stringWithUTF8String: cDomainToASCII];
} @finally {
free(cDomainToASCII);
}
@try {
SRVLookup = [XMPPSRVLookup
lookupWithDomain: domainToASCII];
@ -343,6 +314,34 @@
return encrypted;
}
- (void)checkCertificate
{
X509Certificate *cert;
OFDictionary *SANs;
BOOL serviceSpecific = NO;
[sock verifyPeerCertificate];
cert = [sock peerCertificate];
SANs = [cert subjectAlternativeName];
if ([[SANs objectForKey: @"otherName"]
objectForKey: OID_SRVName] ||
[SANs objectForKey: @"dNSName"] ||
[SANs objectForKey: @"uniformResourceIdentifier"])
serviceSpecific = YES;
if ([cert hasSRVNameMatchingDomain: domainToASCII
service: @"xmpp-client"] ||
[cert hasDNSNameMatchingDomain: domainToASCII])
return;
if (serviceSpecific ||
![cert hasCommonNameMatchingDomain: domainToASCII])
@throw [SSLInvalidCertificateException
exceptionWithClass: isa
reason: @"No matching identifier"];
}
- (void)sendStanza: (OFXMLElement*)element
{
of_log(@"Out: %@", element);
@ -877,6 +876,29 @@
sessionID = nil;
}
- (OFString*)XMPP_IDNAToASCII: (OFString*)domain_
{
OFString *ret;
char *cDomain;
Idna_rc rc;
if ((rc = idna_to_ascii_8z([domain_ UTF8String],
&cDomain, IDNA_USE_STD3_ASCII_RULES)) != IDNA_SUCCESS)
@throw [XMPPIDNATranslationFailedException
exceptionWithClass: isa
connection: self
operation: @"ToASCII"
string: domain_];
@try {
ret = [[OFString alloc] initWithUTF8String: cDomain];
} @finally {
free(cDomain);
}
return ret;
}
- (XMPPJID*)JID
{
return [[JID copy] autorelease];

View file

@ -24,6 +24,7 @@
#include <assert.h>
#import <ObjFW/ObjFW.h>
#import <ObjOpenSSL/SSLInvalidCertificateException.h>
#import "XMPPConnection.h"
#import "XMPPJID.h"
@ -136,6 +137,21 @@ OF_APPLICATION_DELEGATE(AppDelegate)
[conn sendStanza: pres];
}
- (void)connectionDidUpgradeToTLS: (XMPPConnection*)conn
{
@try {
[conn checkCertificate];
} @catch (SSLInvalidCertificateException *e) {
OFString *answer;
[of_stdout writeString: @"Couldn't verify certificate: "];
[of_stdout writeFormat: @"%@\n", e];
[of_stdout writeString: @"Do you want to continue [y/N]? "];
answer = [of_stdin readLine];
if (![answer hasPrefix: @"y"])
@throw e;
}
}
- (void)connection: (XMPPConnection*)conn
didReceiveRosterItem: (XMPPRosterItem*)rosterItem
{