diff --git a/ChangeLog b/ChangeLog index 6df99a032..9f7e29955 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2017-05-19 Richard Frith-Macdonald + + * Headers/Foundation/NSFileHandle.h: + * Source/GSSocketStream.m: + * Source/GSTLS.h: + * Source/GSTLS.m: + * Source/NSFileHandle.m: + Add code to make the issuer and owner of a client certificate + available when we require/verify an incoming certificate. + 2017-05-10 Wolfgang Lux * Tools/gdomap.c (nameServer, donames): Fix incorrect use diff --git a/Headers/Foundation/NSFileHandle.h b/Headers/Foundation/NSFileHandle.h index 56f8f24dc..e4344472a 100644 --- a/Headers/Foundation/NSFileHandle.h +++ b/Headers/Foundation/NSFileHandle.h @@ -263,6 +263,18 @@ GS_EXPORT NSString * const NSFileHandleOperationException; */ - (BOOL) sslHandshakeEstablished: (BOOL*)result outgoing: (BOOL)isOutgoing; +/** If the session verified a certificate from the remote end, returns the + * name of the certificate issuer in the form "C=xxxx,O=yyyy,CN=zzzz" as + * described in RFC2253. Otherwise returns nil. + */ +- (NSString*) sslIssuer; + +/** If the session verified a certificate from the remote end, returns the + * name of the certificate owner in the form "C=xxxx,O=yyyy,CN=zzzz" as + * described in RFC2253. Otherwise returns nil. + */ +- (NSString*) sslOwner; + /** Deprecated ... use -sslSetOptions: instead */ - (void) sslSetCertificate: (NSString*)certFile diff --git a/Source/GSSocketStream.m b/Source/GSSocketStream.m index 06b6b0441..35515a44f 100644 --- a/Source/GSSocketStream.m +++ b/Source/GSSocketStream.m @@ -358,8 +358,7 @@ GSPrivateSockaddrSetup(NSString *machine, uint16_t port, GSTLSSession *session; } -/** - * Populates the dictionary 'dict', copying in all the properties +/** Populates the dictionary 'dict', copying in all the properties * of the supplied streams. If a property is set for both then * the output stream's one has precedence. */ @@ -368,6 +367,11 @@ GSPrivateSockaddrSetup(NSString *machine, uint16_t port, fromInputStream: (NSStream*)i orOutputStream: (NSStream*)o; +/** Called on verification of the remote end's certificate to tell the + * delegate of the input stream who the certificate issuer and owner are. + */ +- (void) stream: (NSStream*)stream issuer: (NSString*)i owner: (NSString*)o; + @end /* Callback to allow the TLS code to pull data from the remote system. @@ -573,6 +577,18 @@ static NSArray *keys = nil; } [self bye]; } + else + { + NSString *issuer = [session issuer]; + NSString *owner = [session owner]; + id del = [istream delegate]; + + if (nil != issuer && nil != owner + && [del respondsToSelector: @selector(stream:issuer:owner:)]) + { + [del stream: istream issuer: issuer owner: owner]; + } + } } } } @@ -718,6 +734,11 @@ static NSArray *keys = nil; } } +- (void) stream: (NSStream*)stream issuer: (NSString*)i owner: (NSString*)o +{ + return; +} + - (NSInteger) write: (const uint8_t *)buffer maxLength: (NSUInteger)len { return [session write: buffer length: len]; diff --git a/Source/GSTLS.h b/Source/GSTLS.h index 7c4ef99cf..327aee261 100644 --- a/Source/GSTLS.h +++ b/Source/GSTLS.h @@ -184,6 +184,8 @@ typedef ssize_t (*GSTLSIOW)(gnutls_transport_ptr_t, const void *, size_t); NSDictionary *opts; GSTLSCredentials *credentials; NSString *problem; + NSString *issuer; + NSString *owner; BOOL outgoing; BOOL active; BOOL handshake; @@ -230,6 +232,18 @@ typedef ssize_t (*GSTLSIOW)(gnutls_transport_ptr_t, const void *, size_t); */ - (BOOL) handshake; +/** If the session verified a certificate from the remote end, returns the + * name of the certificate issuer in the form "C=xxxx,O=yyyy,CN=zzzz" as + * described in RFC2253. Otherwise returns nil. + */ +- (NSString*) issuer; + +/** If the session verified a certificate from the remote end, returns the + * name of the certificate owner in the form "C=xxxx,O=yyyy,CN=zzzz" as + * described in RFC2253. Otherwise returns nil. + */ +- (NSString*) owner; + /* After a failed handshake, this should contain a description of the * failure reason. */ diff --git a/Source/GSTLS.m b/Source/GSTLS.m index 014de96e5..0278cf66f 100644 --- a/Source/GSTLS.m +++ b/Source/GSTLS.m @@ -160,7 +160,7 @@ static NSString *revokeFile = nil; // GSTLS/revoke.crl /* The verifyClient variable tells us if connections from a remote server * should (by default) require and verify a client certificate against - * trusted authorities. + * our trusted authorities. * The hard-coded value can be overridden by the GS_TLS_VERIFY_C environment * variable, which in turn will be overridden by the GSTLSVerifyClient user * default string. @@ -168,8 +168,9 @@ static NSString *revokeFile = nil; // GSTLS/revoke.crl */ static BOOL verifyClient = NO; -/* The verifyServer variable tells us if connections to a remote server should - * (by default) verify its certificate against trusted authorities. +/* The verifyServer variable tells us if outgoing connections (as a client) + * to a remote server should (by default) verify that server's certificate + * against trusted authorities. * The hard-coded value can be overridden by the GS_TLS_VERIFY_S environment * variable, which in turn will be overridden by the GSTLSVerifyServer user * default string. @@ -1430,6 +1431,8 @@ retrieve_callback(gnutls_session_t session, DESTROY(opts); DESTROY(credentials); DESTROY(problem); + DESTROY(issuer); + DESTROY(owner); [super dealloc]; } @@ -1803,6 +1806,16 @@ retrieve_callback(gnutls_session_t session, } } +- (NSString*) issuer +{ + return issuer; +} + +- (NSString*) owner +{ + return owner; +} + - (NSString*) problem { return problem; @@ -2113,13 +2126,15 @@ retrieve_callback(gnutls_session_t session, if (status & GNUTLS_CERT_REVOKED) NSLog(@"%@ TLS verification: certificate has been revoked.", self); - /* +#if defined(GNUTLS_CERT_EXPIRED) if (status & GNUTLS_CERT_EXPIRED) NSLog(@"%@ TLS verification: certificate has expired", self); +#endif +#if defined(GNUTLS_CERT_NOT_ACTIVATED) if (status & GNUTLS_CERT_NOT_ACTIVATED) NSLog(@"%@ TLS verification: certificate is not yet activated", self); - */ +#endif } if (status & GNUTLS_CERT_INVALID) @@ -2166,6 +2181,23 @@ retrieve_callback(gnutls_session_t session, if (YES == debug) NSLog(@"%@ %@", self, problem); return GNUTLS_E_CERTIFICATE_ERROR; } + else + { + char dn[1024]; + size_t dn_size; + + /* Get certificate owner and issuer + */ + dn_size = sizeof(dn); + gnutls_x509_crt_get_dn(cert, dn, &dn_size); + dn[dn_size - 1] = '\0'; + ASSIGN(owner, [NSString stringWithUTF8String: dn]); + + dn_size = sizeof(dn); + gnutls_x509_crt_get_issuer_dn(cert, dn, &dn_size); + dn[dn_size - 1] = '\0'; + ASSIGN(issuer, [NSString stringWithUTF8String: dn]); + } str = [opts objectForKey: GSTLSRemoteHosts]; if (nil == str) diff --git a/Source/NSFileHandle.m b/Source/NSFileHandle.m index 7346d7a41..8bd20ad33 100644 --- a/Source/NSFileHandle.m +++ b/Source/NSFileHandle.m @@ -848,6 +848,16 @@ NSString * const NSFileHandleOperationException return YES; } +- (NSString*) sslIssuer +{ + return nil; +} + +- (NSString*) sslOwner +{ + return nil; +} + - (void) sslSetCertificate: (NSString*)certFile privateKey: (NSString*)privateKey PEMpasswd: (NSString*)PEMpasswd @@ -1045,6 +1055,16 @@ GSTLSHandlePush(gnutls_transport_ptr_t handle, const void *buffer, size_t len) } } +- (NSString*) sslIssuer +{ + return [session issuer]; +} + +- (NSString*) sslOwner +{ + return [session owner]; +} + - (NSString*) sslSetOptions: (NSDictionary*)options { if (isStandardFile == YES)