@@ -33,6 +33,51 @@ static gnutls_dh_params_t rfbDHParams;
3333
3434static rfbBool rfbTLSInitialized = FALSE;
3535
36+ /* Struct to hold callback data */
37+ typedef struct {
38+ rfbClient * rfbClient ;
39+ rfbCredential * rfbCredential ;
40+ } TLSCallbackData ;
41+
42+ /**
43+ * Callback to handle fingerprint mismatch
44+ */
45+ static int cert_fingerprint_mismatch_callback (rfbClient * client , gnutls_x509_crt_t cert )
46+ {
47+ if (!cert ) {
48+ rfbClientErr ("No cert given in fingerprint mismatch handling\n" );
49+ return 0 ;
50+ }
51+
52+ if (!client || !client -> GetX509CertFingerprintMismatchDecision ) {
53+ rfbClientErr ("No client callback given in fingerprint mismatch handling\n" );
54+ return 0 ;
55+ }
56+
57+ /* Subject */
58+ char subject [1024 ];
59+ size_t subject_size = sizeof (subject );
60+ if (gnutls_x509_crt_get_dn (cert , subject , & subject_size ) < 0 ) {
61+ return 0 ;
62+ }
63+
64+ /* Validity */
65+ time_t not_before = (time_t )gnutls_x509_crt_get_activation_time (cert );
66+ time_t not_after = (time_t )gnutls_x509_crt_get_expiration_time (cert );
67+
68+ /* SHA-256 fingerprint */
69+ unsigned char fingerprint [32 ];
70+ size_t fingerprint_size = sizeof (fingerprint );
71+ if (gnutls_x509_crt_get_fingerprint (cert , GNUTLS_DIG_SHA256 , fingerprint , & fingerprint_size ) < 0 ) {
72+ return 0 ;
73+ }
74+
75+ /* Call the callback (just reads values) */
76+ rfbBool decision = client -> GetX509CertFingerprintMismatchDecision (client , subject , not_before , not_after , fingerprint , 32 );
77+
78+ return decision ? 1 : 0 ;
79+ }
80+
3681static int
3782verify_certificate_callback (gnutls_session_t session )
3883{
@@ -43,8 +88,17 @@ verify_certificate_callback (gnutls_session_t session)
4388 gnutls_x509_crt_t cert ;
4489 rfbClient * sptr ;
4590 char * hostname ;
91+ rfbCredential * cred = NULL ;
92+
93+ TLSCallbackData * callback_data = (TLSCallbackData * )gnutls_session_get_ptr (session );
94+ if (!callback_data ) {
95+ rfbClientErr ("Failed to validate certificate - missing callback data\n" );
96+ return GNUTLS_E_CERTIFICATE_ERROR ;
97+ }
98+
99+ sptr = callback_data -> rfbClient ;
100+ cred = callback_data -> rfbCredential ;
46101
47- sptr = (rfbClient * )gnutls_session_get_ptr (session );
48102 if (!sptr ) {
49103 rfbClientLog ("Failed to validate certificate - missing client data\n" );
50104 return GNUTLS_E_CERTIFICATE_ERROR ;
@@ -81,13 +135,59 @@ verify_certificate_callback (gnutls_session_t session)
81135 if (status & GNUTLS_CERT_NOT_ACTIVATED )
82136 rfbClientLog ("The certificate is not yet activated\n" );
83137
138+ /* status would be 0 if cert was trusted */
84139 if (status )
85- return GNUTLS_E_CERTIFICATE_ERROR ;
140+ {
141+ if (gnutls_certificate_type_get (session ) != GNUTLS_CRT_X509 ) {
142+ rfbClientErr ("The certificate was not X509\n" );
143+ return GNUTLS_E_CERTIFICATE_ERROR ;
144+ }
86145
87- /* Up to here the process is the same for X.509 certificates and
88- * OpenPGP keys. From now on X.509 certificates are assumed. This can
89- * be easily extended to work with openpgp keys as well.
90- */
146+ if (gnutls_x509_crt_init (& cert ) < 0 )
147+ {
148+ rfbClientErr ("Error initialising certificate structure\n" );
149+ return GNUTLS_E_CERTIFICATE_ERROR ;
150+ }
151+
152+ cert_list = gnutls_certificate_get_peers (session , & cert_list_size );
153+ if (cert_list == NULL )
154+ {
155+ rfbClientErr ("No certificate was found!\n" );
156+ return GNUTLS_E_CERTIFICATE_ERROR ;
157+ }
158+
159+ if (gnutls_x509_crt_import (cert , & cert_list [0 ], GNUTLS_X509_FMT_DER ) < 0 )
160+ {
161+ rfbClientErr ("Error parsing certificate\n" );
162+ return GNUTLS_E_CERTIFICATE_ERROR ;
163+ }
164+
165+ /* Check if we have an expected fingerprint */
166+ if (cred && cred -> x509Credential .x509ExpectedFingerprint ) {
167+ unsigned char remote_fingerprint [32 ];
168+ size_t fingerprint_size = sizeof (remote_fingerprint );
169+
170+ if (gnutls_x509_crt_get_fingerprint (cert , GNUTLS_DIG_SHA256 , remote_fingerprint , & fingerprint_size ) == 0 ) {
171+ if (memcmp (remote_fingerprint , cred -> x509Credential .x509ExpectedFingerprint , 32 ) == 0 ) {
172+ rfbClientLog ("The certificate's fingerprint matched the expected one - accepting.\n" );
173+ gnutls_x509_crt_deinit (cert );
174+ return 0 ;
175+ }
176+ }
177+ }
178+
179+ /* Fingerprint didn't match or no expected fingerprint - ask user */
180+ if (cert_fingerprint_mismatch_callback (sptr , cert )) {
181+ rfbClientLog ("User decided to trust certificate - accepting.\n" );
182+ gnutls_x509_crt_deinit (cert );
183+ return 0 ;
184+ }
185+
186+ gnutls_x509_crt_deinit (cert );
187+ return GNUTLS_E_CERTIFICATE_ERROR ;
188+ }
189+
190+ /* Certificate is trusted by system CA or via given rfbCredential.x509Credential.x509CACertfile */
91191 if (gnutls_certificate_type_get (session ) != GNUTLS_CRT_X509 ) {
92192 rfbClientLog ("The certificate was not X509\n" );
93193 return GNUTLS_E_CERTIFICATE_ERROR ;
@@ -400,6 +500,7 @@ FreeX509Credential(rfbCredential *cred)
400500 if (cred -> x509Credential .x509CACrlFile ) free (cred -> x509Credential .x509CACrlFile );
401501 if (cred -> x509Credential .x509ClientCertFile ) free (cred -> x509Credential .x509ClientCertFile );
402502 if (cred -> x509Credential .x509ClientKeyFile ) free (cred -> x509Credential .x509ClientKeyFile );
503+ if (cred -> x509Credential .x509ExpectedFingerprint ) free (cred -> x509Credential .x509ExpectedFingerprint );
403504 free (cred );
404505}
405506
@@ -545,10 +646,9 @@ HandleVeNCryptAuth(rfbClient* client)
545646 if (!InitializeTLS ()) return FALSE;
546647
547648 /* Get X509 Credentials if it's not anonymous */
649+ rfbCredential * cred = NULL ;
548650 if (!anonTLS )
549651 {
550- rfbCredential * cred ;
551-
552652 if (!client -> GetCredential )
553653 {
554654 rfbClientLog ("GetCredential callback is not set.\n" );
@@ -562,8 +662,10 @@ HandleVeNCryptAuth(rfbClient* client)
562662 }
563663
564664 x509_cred = CreateX509CertCredential (cred );
565- FreeX509Credential (cred );
566- if (!x509_cred ) return FALSE;
665+ if (!x509_cred ) {
666+ FreeX509Credential (cred );
667+ return FALSE;
668+ }
567669 }
568670
569671 /* Start up the TLS session */
@@ -577,12 +679,28 @@ HandleVeNCryptAuth(rfbClient* client)
577679 {
578680 /* Set the certificate verification callback. */
579681 gnutls_certificate_set_verify_function (x509_cred , verify_certificate_callback );
580- gnutls_session_set_ptr ((gnutls_session_t )client -> tlsSession , (void * )client );
682+
683+ /* We need the client plus the rfbCredential in the verification callback */
684+ TLSCallbackData * callback_data = malloc (sizeof (TLSCallbackData ));
685+ if (!callback_data ) {
686+ rfbClientErr ("Cannot allocate callback data\n" );
687+ FreeTLS (client );
688+ gnutls_certificate_free_credentials (x509_cred );
689+ FreeX509Credential (cred );
690+ return FALSE;
691+ }
692+ callback_data -> rfbClient = client ;
693+ callback_data -> rfbCredential = cred ;
694+
695+ /* Store the callback data in the session pointer so the callback can access them */
696+ gnutls_session_set_ptr ((gnutls_session_t )client -> tlsSession , (void * )callback_data );
581697
582698 if ((ret = gnutls_credentials_set ((gnutls_session_t )client -> tlsSession , GNUTLS_CRD_CERTIFICATE , x509_cred )) < 0 )
583699 {
584700 rfbClientLog ("Cannot set x509 credential: %s.\n" , gnutls_strerror (ret ));
585701 FreeTLS (client );
702+ gnutls_certificate_free_credentials (x509_cred );
703+ free (callback_data );
586704 return FALSE;
587705 }
588706 }
@@ -645,6 +763,16 @@ void FreeTLS(rfbClient* client)
645763{
646764 if (client -> tlsSession )
647765 {
766+ // Get the callback data from the session pointer before deinitializing
767+ TLSCallbackData * callback_data = (TLSCallbackData * )gnutls_session_get_ptr ((gnutls_session_t )client -> tlsSession );
768+ // Free the callback data and credentials
769+ if (callback_data ) {
770+ if (callback_data -> rfbCredential ) {
771+ FreeX509Credential (callback_data -> rfbCredential );
772+ }
773+ free (callback_data );
774+ }
775+
648776 gnutls_deinit ((gnutls_session_t )client -> tlsSession );
649777 client -> tlsSession = NULL ;
650778 TINI_MUTEX (client -> tlsRwMutex );
0 commit comments