PoC: Check server's certificate
This commit is contained in:
parent
78afb19536
commit
d7d55cf3f7
1
proto.h
1
proto.h
@ -138,6 +138,7 @@ int load_licence(unsigned char **data);
|
|||||||
void save_licence(unsigned char *data, int length);
|
void save_licence(unsigned char *data, int length);
|
||||||
void rd_create_ui(void);
|
void rd_create_ui(void);
|
||||||
RD_BOOL rd_pstcache_mkdir(void);
|
RD_BOOL rd_pstcache_mkdir(void);
|
||||||
|
RD_BOOL rd_certcache_mkdir(void);
|
||||||
int rd_open_file(char *filename);
|
int rd_open_file(char *filename);
|
||||||
void rd_close_file(int fd);
|
void rd_close_file(int fd);
|
||||||
int rd_read_file(int fd, void *ptr, int len);
|
int rd_read_file(int fd, void *ptr, int len);
|
||||||
|
46
rdesktop.c
46
rdesktop.c
@ -2025,6 +2025,52 @@ rd_create_ui()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO: Replace with recursive mkdir */
|
||||||
|
RD_BOOL rd_certcache_mkdir(void)
|
||||||
|
{
|
||||||
|
char *home;
|
||||||
|
char certcache_dir[PATH_MAX];
|
||||||
|
|
||||||
|
home = getenv("HOME");
|
||||||
|
|
||||||
|
if (home == NULL)
|
||||||
|
return False;
|
||||||
|
|
||||||
|
snprintf(certcache_dir, sizeof(certcache_dir) - 1, "%s/%s", home, ".local");
|
||||||
|
|
||||||
|
if ((mkdir(certcache_dir, S_IRWXU) == -1) && errno != EEXIST)
|
||||||
|
{
|
||||||
|
logger(Core, Error, "%s: mkdir() failed: %s", __func__, strerror(errno));
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(certcache_dir, sizeof(certcache_dir) - 1, "%s/%s", home, ".local/share");
|
||||||
|
|
||||||
|
if ((mkdir(certcache_dir, S_IRWXU) == -1) && errno != EEXIST)
|
||||||
|
{
|
||||||
|
logger(Core, Error, "%s: mkdir() failed: %s", __func__, strerror(errno));
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(certcache_dir, sizeof(certcache_dir) - 1, "%s/%s", home, ".local/share/rdesktop");
|
||||||
|
|
||||||
|
if ((mkdir(certcache_dir, S_IRWXU) == -1) && errno != EEXIST)
|
||||||
|
{
|
||||||
|
logger(Core, Error, "%s: mkdir() failed: %s", __func__, strerror(errno));
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(certcache_dir, sizeof(certcache_dir) - 1, "%s/%s", home, ".local/share/rdesktop/certs");
|
||||||
|
|
||||||
|
if ((mkdir(certcache_dir, S_IRWXU) == -1) && errno != EEXIST)
|
||||||
|
{
|
||||||
|
logger(Core, Error, "%s: mkdir() failed: %s", __func__, strerror(errno));
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
|
||||||
/* Create the bitmap cache directory */
|
/* Create the bitmap cache directory */
|
||||||
RD_BOOL
|
RD_BOOL
|
||||||
rd_pstcache_mkdir(void)
|
rd_pstcache_mkdir(void)
|
||||||
|
163
tcp.c
163
tcp.c
@ -24,6 +24,7 @@
|
|||||||
#include <unistd.h> /* select read write close */
|
#include <unistd.h> /* select read write close */
|
||||||
#include <sys/socket.h> /* socket connect setsockopt */
|
#include <sys/socket.h> /* socket connect setsockopt */
|
||||||
#include <sys/time.h> /* timeval */
|
#include <sys/time.h> /* timeval */
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <netdb.h> /* gethostbyname */
|
#include <netdb.h> /* gethostbyname */
|
||||||
#include <netinet/in.h> /* sockaddr_in */
|
#include <netinet/in.h> /* sockaddr_in */
|
||||||
#include <netinet/tcp.h> /* TCP_NODELAY */
|
#include <netinet/tcp.h> /* TCP_NODELAY */
|
||||||
@ -279,7 +280,161 @@ tcp_recv(STREAM s, uint32 length)
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Establish a SSL/TLS 1.0-1-2 connection */
|
int check_cert(gnutls_session_t session)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
char *home;
|
||||||
|
char certcache_dir[PATH_MAX];
|
||||||
|
char certcache_fn[PATH_MAX];
|
||||||
|
|
||||||
|
struct stat sb;
|
||||||
|
|
||||||
|
int type;
|
||||||
|
int c;
|
||||||
|
time_t exp_time;
|
||||||
|
gnutls_x509_crt_t cert;
|
||||||
|
gnutls_datum_t cinfo;
|
||||||
|
const gnutls_datum_t *cert_list;
|
||||||
|
unsigned int cert_list_size = 0;
|
||||||
|
|
||||||
|
size_t size;
|
||||||
|
char dn[256];
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
home = getenv("HOME");
|
||||||
|
|
||||||
|
if (home == NULL)
|
||||||
|
return False;
|
||||||
|
|
||||||
|
snprintf(certcache_dir, sizeof(certcache_dir) - 1, "%s/%s", home, ".local/share/rdesktop/certs/");
|
||||||
|
|
||||||
|
if ((rv = stat(certcache_dir, &sb)) == -1) {
|
||||||
|
|
||||||
|
if (errno == ENOENT) {
|
||||||
|
if (rd_certcache_mkdir() == False) {
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((sb.st_mode & S_IFMT) != S_IFDIR) {
|
||||||
|
logger(Core, Error, "%s: %s exists but it's not a directory", __func__, certcache_dir);
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(certcache_fn, sizeof(certcache_fn) - 1, "%s/%s", certcache_dir, "known_certs");
|
||||||
|
|
||||||
|
type = gnutls_certificate_type_get(session);
|
||||||
|
|
||||||
|
if (type == GNUTLS_CRT_X509) {
|
||||||
|
|
||||||
|
cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
|
||||||
|
|
||||||
|
if (cert_list_size > 0) {
|
||||||
|
gnutls_x509_crt_init(&cert);
|
||||||
|
gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
|
||||||
|
|
||||||
|
size = sizeof(dn);
|
||||||
|
gnutls_x509_crt_get_dn(cert, dn, &size);
|
||||||
|
|
||||||
|
if (size > 0) {
|
||||||
|
name = strstr(dn, "CN=");
|
||||||
|
|
||||||
|
if (!name) {
|
||||||
|
logger(Core, Error, "%s: Failed to find CN in Distinguished Name part of certificate", __func__);
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
|
||||||
|
name += 3;
|
||||||
|
|
||||||
|
if (!strlen(name)) {
|
||||||
|
logger(Core, Error, "%s: DN length is 0", __func__);
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
logger(Core, Error, "%s: Failed to get DN from certificate", __func__);
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* uglym8: we can't rely on hostname being consistent here as we can connect
|
||||||
|
* to tunneled host (e.g. via ssh) so we're going to use DN as a hostname
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
rv = gnutls_verify_stored_pubkey(certcache_fn, NULL, name, "rdesktop", type, &cert_list[0], 0);
|
||||||
|
|
||||||
|
if (rv == GNUTLS_E_NO_CERTIFICATE_FOUND) {
|
||||||
|
logger(Core, Debug, "%s: %s: No previous stored certificate for the host '%s'. Storing it into the cache", __func__, name);
|
||||||
|
|
||||||
|
exp_time = gnutls_x509_crt_get_expiration_time(cert);
|
||||||
|
rv = gnutls_store_pubkey(certcache_fn, NULL, name, "rdesktop", type, &cert_list[0], exp_time, 0);
|
||||||
|
|
||||||
|
if (rv != GNUTLS_E_SUCCESS) {
|
||||||
|
logger(Core, Error, "%s: Failed to store certificate. error = 0x%x (%s)", __func__, rv, gnutls_strerror(rv));
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (rv == GNUTLS_E_CERTIFICATE_KEY_MISMATCH) {
|
||||||
|
//logger(Core, Debug, "%s: Host '%s' is known but has another key associated with it", __func__, name);
|
||||||
|
fprintf(stdout, "Host '%s' is known but has another key associated with it\nPlease review the certificate info:\n", name);
|
||||||
|
|
||||||
|
rv = gnutls_x509_crt_print(cert, GNUTLS_CRT_PRINT_ONELINE, &cinfo);
|
||||||
|
|
||||||
|
if (rv == 0) {
|
||||||
|
fprintf(stdout, "\t%s\n", cinfo.data);
|
||||||
|
gnutls_free(cinfo.data);
|
||||||
|
} else {
|
||||||
|
logger(Core, Error, "%s: Failed to print the certificate. error = 0x%x (%s)", __func__, rv, gnutls_strerror(rv));
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stdout, "Do you trust this certificate (y/n)? ");
|
||||||
|
|
||||||
|
/* TODO: PoC: will be replaced with proper handling */
|
||||||
|
while (1) {
|
||||||
|
c = getchar();
|
||||||
|
|
||||||
|
if (c == 'n' || c == 'N') {
|
||||||
|
goto bail;
|
||||||
|
} else if (c == 'y' || c == 'Y') {
|
||||||
|
break;
|
||||||
|
} else if (c == 0xa) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
fprintf(stdout, "\nPlease enter either 'y' or 'n'\n");
|
||||||
|
fprintf(stdout, "Do you trust this certificate (y/n)? ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//logger(Core, Debug, "%s: %s: Replacing certificate for the host '%s'.", __func__, name);
|
||||||
|
/* TODO: PoC: Replace instead of just adding the new certificate */
|
||||||
|
logger(Core, Debug, "%s: %s: Adding a new certificate for the host '%s'.", __func__, name);
|
||||||
|
|
||||||
|
exp_time = gnutls_x509_crt_get_expiration_time(cert);
|
||||||
|
rv = gnutls_store_pubkey(certcache_fn, NULL, name, "rdesktop", type, &cert_list[0], exp_time, 0);
|
||||||
|
|
||||||
|
if (rv != GNUTLS_E_SUCCESS) {
|
||||||
|
logger(Core, Error, "%s: Failed to store certificate. error = 0x%x (%s)", __func__, rv, gnutls_strerror(rv));
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (rv < 0) {
|
||||||
|
fprintf(stderr, "%s: gnutls_verify_stored_pubkey: %s\n", __func__, gnutls_strerror(rv));
|
||||||
|
logger(Core, Error, "%s: Verification for host '%s' certificate failed. Error = 0x%x (%s)", __func__, name, rv, gnutls_strerror(rv));
|
||||||
|
goto bail;
|
||||||
|
} else {
|
||||||
|
logger(Core, Debug, "%s: %s: Host %s is known and the key is OK.", __func__, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
bail:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Establish a SSL/TLS 1.0 connection */
|
||||||
RD_BOOL
|
RD_BOOL
|
||||||
tcp_tls_connect(void)
|
tcp_tls_connect(void)
|
||||||
{
|
{
|
||||||
@ -344,6 +499,12 @@ tcp_tls_connect(void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//if (check_cert(g_tls_session) != 0) goto fail;
|
||||||
|
if (check_cert(g_tls_session) != 0) {
|
||||||
|
fprintf(stdout, "%s: Failed to check certificate. Bailing out\n", __func__);
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
return True;
|
return True;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
Loading…
Reference in New Issue
Block a user