Skip to content

Commit fed96b5

Browse files
committed
examples/client: add expected X.509 cert handling
...for self-signed certs.
1 parent e86ce3c commit fed96b5

File tree

1 file changed

+124
-28
lines changed

1 file changed

+124
-28
lines changed

examples/client/SDLvncviewer.c

Lines changed: 124 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
436462
static 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

Comments
 (0)