@@ -433,39 +433,134 @@ static void got_selection_utf8(rfbClient *cl, const char *buf, int len)
433433}
434434
435435
436+ static int hex_to_bytes (const char * hex_str , unsigned char * bytes , size_t max_bytes ) {
437+ size_t i = 0 ;
438+ size_t byte_index = 0 ;
439+
440+ while (hex_str [i ] != '\0' && byte_index < max_bytes ) {
441+ // Skip colons
442+ if (hex_str [i ] == ':' ) {
443+ i ++ ;
444+ continue ;
445+ }
446+
447+ // Read two hex characters
448+ char hex_byte [3 ] = {hex_str [i ], hex_str [i + 1 ], '\0' };
449+
450+ // Convert to byte
451+ unsigned char byte = (unsigned char )strtol (hex_byte , NULL , 16 );
452+ bytes [byte_index ++ ] = byte ;
453+
454+ // Move to next pair
455+ i += 2 ;
456+ }
457+
458+ return byte_index ;
459+ }
460+
461+
436462static rfbCredential * get_credential (rfbClient * cl , int credentialType ){
437- rfbCredential * c = malloc (sizeof (rfbCredential ));
438- if (!c ) {
439- return NULL ;
440- }
441- c -> userCredential .username = malloc (RFB_BUF_SIZE );
442- if (!c -> userCredential .username ) {
443- free (c );
444- return NULL ;
445- }
446- c -> userCredential .password = malloc (RFB_BUF_SIZE );
447- if (!c -> userCredential .password ) {
448- free (c -> userCredential .username );
449- free (c );
450- return NULL ;
451- }
463+ rfbCredential * c = calloc (1 , sizeof (rfbCredential ));
464+
465+ if (c && credentialType == rfbCredentialTypeUser ) {
466+ c -> userCredential .username = malloc (RFB_BUF_SIZE );
467+ if (!c -> userCredential .username ) {
468+ free (c );
469+ return NULL ;
470+ }
471+ c -> userCredential .password = malloc (RFB_BUF_SIZE );
472+ if (!c -> userCredential .password ) {
473+ free (c -> userCredential .username );
474+ free (c );
475+ return NULL ;
476+ }
477+
478+ rfbClientLog ("username and password required for authentication!\n" );
479+ printf ("user: " );
480+ fgets (c -> userCredential .username , RFB_BUF_SIZE , stdin );
481+ printf ("pass: " );
482+ fgets (c -> userCredential .password , RFB_BUF_SIZE , stdin );
483+
484+ /* remove trailing newlines */
485+ c -> userCredential .username [strcspn (c -> userCredential .username , "\n" )] = 0 ;
486+ c -> userCredential .password [strcspn (c -> userCredential .password , "\n" )] = 0 ;
487+
488+ return c ;
489+ }
452490
453- if (credentialType != rfbCredentialTypeUser ) {
454- rfbClientErr ("something else than username and password required for authentication\n" );
455- return NULL ;
456- }
491+ if (c && credentialType == rfbCredentialTypeX509 ) {
492+ // We do intentionally not give any cert files here,
493+ // only the fingerprint we expect in case there is a
494+ // self-signed cert.
495+ rfbClientLog ("X.509 required for authentication!\n" );
496+ c -> x509Credential .x509ExpectedFingerprint = malloc (32 );
497+ if (!c -> x509Credential .x509ExpectedFingerprint ) {
498+ free (c );
499+ return NULL ;
500+ }
501+
502+ printf ("Expected SHA256 fingerprint of the server: " );
503+ char buf [RFB_BUF_SIZE ];
504+ fgets (buf , RFB_BUF_SIZE , stdin );
505+ /* remove trailing newline */
506+ buf [strcspn (buf , "\n" )] = 0 ;
507+ // is a colon-separated hex string
508+ if (hex_to_bytes (buf , c -> x509Credential .x509ExpectedFingerprint , 32 ) != 32 ) {
509+ fprintf (stderr , "Given SHA256 fingerprint has wrong length, not setting expected SHA256 fingerprint\n" );
510+ free (c -> x509Credential .x509ExpectedFingerprint );
511+ c -> x509Credential .x509ExpectedFingerprint = NULL ;
512+ }
513+
514+ return c ;
515+ }
516+
517+ return NULL ;
518+ }
519+
520+ static int bytes_to_colon_hex (const uint8_t * bytes , size_t len , char * out_str , size_t out_str_size ) {
521+ if (!bytes || !out_str ) return -1 ;
522+
523+ // Required buffer: len*3 - 1 + 1 = len*3
524+ if (out_str_size < len * 3 ) return -1 ;
525+
526+ size_t pos = 0 ;
527+ for (size_t i = 0 ; i < len ; i ++ ) {
528+ if (i > 0 ) {
529+ out_str [pos ++ ] = ':' ; // add colon between bytes
530+ }
531+ pos += snprintf (out_str + pos , out_str_size - pos , "%02X" , bytes [i ]);
532+ }
533+
534+ out_str [pos ] = '\0' ; // null-terminate
535+ return 0 ;
536+ }
537+
538+ static rfbBool get_x509_cert_mismatch_decision (rfbClient * client ,
539+ const char * remote_cert_subject ,
540+ time_t remote_cert_valid_from ,
541+ time_t remote_cert_valid_until ,
542+ const uint8_t * remote_cert_sha256_fingerprint ,
543+ size_t remote_cert_sha256_fingerprint_len ) {
544+ char buf [RFB_BUF_SIZE ];
545+
546+ printf ("Could not validate server certificate by system CA or known fingerprint — review details and accept?\n" );
547+ printf ("Subject: %s\n" , remote_cert_subject );
548+
549+ strftime (buf , sizeof (buf ), "%Y-%m-%d %H:%M:%S UTC" , gmtime (& remote_cert_valid_from ));
550+ printf ("Valid From: %s\n" , buf );
551+
552+ strftime (buf , sizeof (buf ), "%Y-%m-%d %H:%M:%S UTC" , gmtime (& remote_cert_valid_until ));
553+ printf ("Valid Until: %s\n" , buf );
457554
458- rfbClientLog ("username and password required for authentication!\n" );
459- printf ("user: " );
460- fgets (c -> userCredential .username , RFB_BUF_SIZE , stdin );
461- printf ("pass: " );
462- fgets (c -> userCredential .password , RFB_BUF_SIZE , stdin );
555+ bytes_to_colon_hex (remote_cert_sha256_fingerprint , remote_cert_sha256_fingerprint_len , buf , RFB_BUF_SIZE );
463556
464- /* remove trailing newlines */
465- c -> userCredential .username [strcspn (c -> userCredential .username , "\n" )] = 0 ;
466- c -> userCredential .password [strcspn (c -> userCredential .password , "\n" )] = 0 ;
557+ printf ("SHA-256 Fingerprint: %s\n" , buf );
467558
468- return c ;
559+ // User knows expected fingerprint
560+ printf ("Trust? y/n: " );
561+ char answer [RFB_BUF_SIZE ];
562+ fgets (answer , RFB_BUF_SIZE , stdin );
563+ return answer [0 ] == 'y' ;
469564}
470565
471566
@@ -519,6 +614,7 @@ int main(int argc,char** argv) {
519614 cl -> GotXCutText = got_selection_latin1 ;
520615 cl -> GotXCutTextUTF8 = got_selection_utf8 ;
521616 cl -> GetCredential = get_credential ;
617+ cl -> GetX509CertFingerprintMismatchDecision = get_x509_cert_mismatch_decision ;
522618 cl -> listenPort = LISTEN_PORT_OFFSET ;
523619 cl -> listen6Port = LISTEN_PORT_OFFSET ;
524620 if (!rfbInitClient (cl ,& argc ,argv ))
0 commit comments