From a0e4f1e0f5989505f4aab10d64194b635f9af53c Mon Sep 17 00:00:00 2001 From: Lars-Dominik Braun Date: Fri, 11 Nov 2011 14:45:21 +0100 Subject: waitress: Fingerprint check Reduces memory usage, protects against 0wned CA's and avoids ca-bundle confusion. Closes #175 --- contrib/pianobar.1 | 5 ++--- src/libwaitress/waitress.c | 40 +++++++++++----------------------------- src/libwaitress/waitress.h | 4 ++-- src/main.c | 17 +++-------------- src/settings.c | 18 +++++++++++++----- src/settings.h | 2 +- 6 files changed, 32 insertions(+), 54 deletions(-) diff --git a/contrib/pianobar.1 b/contrib/pianobar.1 index dbff073..c5d3347 100644 --- a/contrib/pianobar.1 +++ b/contrib/pianobar.1 @@ -282,9 +282,8 @@ sorts by name from a to z, quickmix_01_name_za by type (quickmix at the bottom) and name from z to a. .TP -.B tls_ca_path = /etc/ssl/certs/ca-certificates.crt -File that contains the root certificate (and possibly intermediate -certificates) of Pandora’s CA. +.B tls_fingerprint = D9980BA2CC0F97BB03822C6211EAEA4A06EEF427 +Hex-encoded SHA1 fingerprint of Pandora’s TLS certificate. .TP .B user = your@user.name diff --git a/src/libwaitress/waitress.c b/src/libwaitress/waitress.c index 8bb519a..7082ffd 100644 --- a/src/libwaitress/waitress.c +++ b/src/libwaitress/waitress.c @@ -53,21 +53,11 @@ typedef struct { size_t pos; } WaitressFetchBufCbBuffer_t; -WaitressReturn_t WaitressInit (WaitressHandle_t *waith, const char *caPath) { +void WaitressInit (WaitressHandle_t *waith) { assert (waith != NULL); memset (waith, 0, sizeof (*waith)); waith->timeout = 30000; - if (caPath != NULL) { - gnutls_certificate_allocate_credentials (&waith->tlsCred); - if (gnutls_certificate_set_x509_trust_file (waith->tlsCred, caPath, - GNUTLS_X509_FMT_PEM) <= 0) { - return WAITRESS_RET_TLS_TRUSTFILE_ERR; - } - waith->tlsInitialized = true; - } - - return WAITRESS_RET_OK; } void WaitressFree (WaitressHandle_t *waith) { @@ -75,9 +65,6 @@ void WaitressFree (WaitressHandle_t *waith) { free (waith->url.url); free (waith->proxy.url); - if (waith->tlsInitialized) { - gnutls_certificate_free_credentials (waith->tlsCred); - } memset (waith, 0, sizeof (*waith)); } @@ -709,22 +696,10 @@ static int WaitressTlsVerify (gnutls_session_t session) { waith = gnutls_session_get_ptr (session); assert (waith != NULL); - if (gnutls_certificate_verify_peers2 (session, &status) != GNUTLS_E_SUCCESS) { - return GNUTLS_E_CERTIFICATE_ERROR; - } - - /* don't accept invalid certs */ - if (status & (GNUTLS_CERT_INVALID | GNUTLS_CERT_SIGNER_NOT_FOUND | - GNUTLS_CERT_REVOKED | GNUTLS_CERT_EXPIRED | - GNUTLS_CERT_NOT_ACTIVATED)) { - return GNUTLS_E_CERTIFICATE_ERROR; - } - if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509) { return GNUTLS_E_CERTIFICATE_ERROR; } - /* check hostname */ if ((certList = gnutls_certificate_get_peers (session, &certListSize)) == NULL) { return GNUTLS_E_CERTIFICATE_ERROR; @@ -739,7 +714,14 @@ static int WaitressTlsVerify (gnutls_session_t session) { return GNUTLS_E_CERTIFICATE_ERROR; } - if (gnutls_x509_crt_check_hostname (cert, waith->url.host) == 0) { + char fingerprint[20]; + size_t fingerprintSize = sizeof (fingerprint); + if (gnutls_x509_crt_get_fingerprint (cert, GNUTLS_DIG_SHA1, fingerprint, + &fingerprintSize) != 0) { + return GNUTLS_E_CERTIFICATE_ERROR; + } + + if (memcmp (fingerprint, waith->tlsFingerprint, sizeof (fingerprint)) != 0) { return GNUTLS_E_CERTIFICATE_ERROR; } @@ -1036,8 +1018,6 @@ WaitressReturn_t WaitressFetchCall (WaitressHandle_t *waith) { waith->request.write = WaitressOrdinaryWrite; if (waith->url.tls) { - assert (waith->tlsInitialized); - waith->request.read = WaitressGnutlsRead; waith->request.write = WaitressGnutlsWrite; gnutls_init (&waith->request.tlsSession, GNUTLS_CLIENT); @@ -1046,6 +1026,7 @@ WaitressReturn_t WaitressFetchCall (WaitressHandle_t *waith) { "PERFORMANCE", &err) != GNUTLS_E_SUCCESS) { return WAITRESS_RET_ERR; } + gnutls_certificate_allocate_credentials (&waith->tlsCred); if (gnutls_credentials_set (waith->request.tlsSession, GNUTLS_CRD_CERTIFICATE, waith->tlsCred) != GNUTLS_E_SUCCESS) { @@ -1083,6 +1064,7 @@ WaitressReturn_t WaitressFetchCall (WaitressHandle_t *waith) { if (waith->url.tls) { gnutls_bye (waith->request.tlsSession, GNUTLS_SHUT_RDWR); gnutls_deinit (waith->request.tlsSession); + gnutls_certificate_free_credentials (waith->tlsCred); } close (waith->request.sockfd); diff --git a/src/libwaitress/waitress.h b/src/libwaitress/waitress.h index e1cf303..7e4401a 100644 --- a/src/libwaitress/waitress.h +++ b/src/libwaitress/waitress.h @@ -92,8 +92,8 @@ typedef struct { void *data; WaitressCbReturn_t (*callback) (void *, size_t, void *); int timeout; + const char *tlsFingerprint; gnutls_certificate_credentials_t tlsCred; - bool tlsInitialized; /* per-request data */ struct { @@ -110,7 +110,7 @@ typedef struct { } request; } WaitressHandle_t; -WaitressReturn_t WaitressInit (WaitressHandle_t *, const char *); +void WaitressInit (WaitressHandle_t *); void WaitressFree (WaitressHandle_t *); bool WaitressSetProxy (WaitressHandle_t *, const char *); char *WaitressUrlEncode (const char *); diff --git a/src/main.c b/src/main.c index e14a88a..afa75da 100644 --- a/src/main.c +++ b/src/main.c @@ -192,7 +192,7 @@ static void BarMainStartPlayback (BarApp_t *app, pthread_t *playerThread) { /* setup player */ memset (&app->player, 0, sizeof (app->player)); - WaitressInit (&app->player.waith, NULL); + WaitressInit (&app->player.waith); WaitressSetUrl (&app->player.waith, app->playlist->audioUrl); /* set up global proxy, player is NULLed on songfinish */ @@ -328,7 +328,6 @@ int main (int argc, char **argv) { static BarApp_t app; /* terminal attributes _before_ we started messing around with ~ECHO */ struct termios termOrig; - WaitressReturn_t wRet; memset (&app, 0, sizeof (app)); @@ -355,19 +354,10 @@ int main (int argc, char **argv) { app.settings.keys[BAR_KS_HELP]); } - if ((wRet = WaitressInit (&app.waith, app.settings.tlsCaPath)) != WAITRESS_RET_OK) { - if (wRet == WAITRESS_RET_TLS_TRUSTFILE_ERR) { - BarUiMsg (&app.settings, MSG_ERR, "Can't load root certificates. " - "Please check the tls_ca_path setting in your config file.\n"); - } else { - BarUiMsg (&app.settings, MSG_ERR, "Can't initialize HTTP library: " - "%s\n", WaitressErrorToStr (wRet)); - } - goto die; - } - + WaitressInit (&app.waith); app.waith.url.host = strdup (PIANO_RPC_HOST); app.waith.url.tls = true; + app.waith.tlsFingerprint = app.settings.tlsFingerprint; /* init fds */ FD_ZERO(&app.input.set); @@ -388,7 +378,6 @@ int main (int argc, char **argv) { BarMainLoop (&app); -die: if (app.input.fds[1] != -1) { close (app.input.fds[1]); } diff --git a/src/settings.c b/src/settings.c index f29fcfa..ee332cc 100644 --- a/src/settings.c +++ b/src/settings.c @@ -93,7 +93,6 @@ void BarSettingsDestroy (BarSettings_t *settings) { free (settings->npStationFormat); free (settings->listSongFormat); free (settings->fifo); - free (settings->tlsCaPath); for (size_t i = 0; i < MSG_COUNT; i++) { free (settings->msgFormat[i].prefix); free (settings->msgFormat[i].postfix); @@ -132,7 +131,9 @@ void BarSettingsRead (BarSettings_t *settings) { settings->listSongFormat = strdup ("%i) %a - %t%r"); settings->fifo = malloc (PATH_MAX * sizeof (*settings->fifo)); BarGetXdgConfigDir (PACKAGE "/ctl", settings->fifo, PATH_MAX); - settings->tlsCaPath = strdup ("/etc/ssl/certs/ca-certificates.crt"); + memcpy (settings->tlsFingerprint, "\xD9\x98\x0B\xA2\xCC\x0F\x97\xBB" + "\x03\x82\x2C\x62\x11\xEA\xEA\x4A\x06\xEE\xF4\x27", + sizeof (settings->tlsFingerprint)); settings->msgFormat[MSG_NONE].prefix = NULL; settings->msgFormat[MSG_NONE].postfix = NULL; @@ -241,9 +242,16 @@ void BarSettingsRead (BarSettings_t *settings) { } else if (streq ("fifo", key)) { free (settings->fifo); settings->fifo = strdup (val); - } else if (streq ("tls_ca_path", key)) { - free (settings->tlsCaPath); - settings->tlsCaPath = strdup (val); + } else if (streq ("tls_fingerprint", key)) { + /* expects 40 byte hex-encoded sha1 */ + if (strlen (val) == 40) { + for (size_t i = 0; i < 20; i++) { + char hex[3]; + memcpy (hex, &val[i*2], 2); + hex[2] = '\0'; + settings->tlsFingerprint[i] = strtol (hex, NULL, 16); + } + } } else if (strncmp (formatMsgPrefix, key, strlen (formatMsgPrefix)) == 0) { static const char *mapping[] = {"none", "info", "nowplaying", diff --git a/src/settings.h b/src/settings.h index 6cb4cb2..8ce1225 100644 --- a/src/settings.h +++ b/src/settings.h @@ -96,7 +96,7 @@ typedef struct { char *npStationFormat; char *listSongFormat; char *fifo; - char *tlsCaPath; + char tlsFingerprint[20]; BarMsgFormatStr_t msgFormat[MSG_COUNT]; } BarSettings_t; -- cgit v1.2.3