| 1 | diff --git a/contrib/pianobar.1 b/contrib/pianobar.1 |
| 2 | index dbff073..c5d3347 100644 |
| 3 | --- a/contrib/pianobar.1 |
| 4 | +++ b/contrib/pianobar.1 |
| 5 | @@ -282,9 +282,8 @@ sorts by name from a to z, quickmix_01_name_za by type (quickmix at the |
| 6 | bottom) and name from z to a. |
| 7 | |
| 8 | .TP |
| 9 | -.B tls_ca_path = /etc/ssl/certs/ca-certificates.crt |
| 10 | -File that contains the root certificate (and possibly intermediate |
| 11 | -certificates) of Pandora’s CA. |
| 12 | +.B tls_fingerprint = D9980BA2CC0F97BB03822C6211EAEA4A06EEF427 |
| 13 | +Hex-encoded SHA1 fingerprint of Pandora’s TLS certificate. |
| 14 | |
| 15 | .TP |
| 16 | .B user = your@user.name |
| 17 | diff --git a/src/libwaitress/waitress.c b/src/libwaitress/waitress.c |
| 18 | index 8bb519a..7082ffd 100644 |
| 19 | --- a/src/libwaitress/waitress.c |
| 20 | +++ b/src/libwaitress/waitress.c |
| 21 | @@ -53,21 +53,11 @@ typedef struct { |
| 22 | size_t pos; |
| 23 | } WaitressFetchBufCbBuffer_t; |
| 24 | |
| 25 | -WaitressReturn_t WaitressInit (WaitressHandle_t *waith, const char *caPath) { |
| 26 | +void WaitressInit (WaitressHandle_t *waith) { |
| 27 | assert (waith != NULL); |
| 28 | |
| 29 | memset (waith, 0, sizeof (*waith)); |
| 30 | waith->timeout = 30000; |
| 31 | - if (caPath != NULL) { |
| 32 | - gnutls_certificate_allocate_credentials (&waith->tlsCred); |
| 33 | - if (gnutls_certificate_set_x509_trust_file (waith->tlsCred, caPath, |
| 34 | - GNUTLS_X509_FMT_PEM) <= 0) { |
| 35 | - return WAITRESS_RET_TLS_TRUSTFILE_ERR; |
| 36 | - } |
| 37 | - waith->tlsInitialized = true; |
| 38 | - } |
| 39 | - |
| 40 | - return WAITRESS_RET_OK; |
| 41 | } |
| 42 | |
| 43 | void WaitressFree (WaitressHandle_t *waith) { |
| 44 | @@ -75,9 +65,6 @@ void WaitressFree (WaitressHandle_t *waith) { |
| 45 | |
| 46 | free (waith->url.url); |
| 47 | free (waith->proxy.url); |
| 48 | - if (waith->tlsInitialized) { |
| 49 | - gnutls_certificate_free_credentials (waith->tlsCred); |
| 50 | - } |
| 51 | memset (waith, 0, sizeof (*waith)); |
| 52 | } |
| 53 | |
| 54 | @@ -709,22 +696,10 @@ static int WaitressTlsVerify (gnutls_session_t session) { |
| 55 | waith = gnutls_session_get_ptr (session); |
| 56 | assert (waith != NULL); |
| 57 | |
| 58 | - if (gnutls_certificate_verify_peers2 (session, &status) != GNUTLS_E_SUCCESS) { |
| 59 | - return GNUTLS_E_CERTIFICATE_ERROR; |
| 60 | - } |
| 61 | - |
| 62 | - /* don't accept invalid certs */ |
| 63 | - if (status & (GNUTLS_CERT_INVALID | GNUTLS_CERT_SIGNER_NOT_FOUND | |
| 64 | - GNUTLS_CERT_REVOKED | GNUTLS_CERT_EXPIRED | |
| 65 | - GNUTLS_CERT_NOT_ACTIVATED)) { |
| 66 | - return GNUTLS_E_CERTIFICATE_ERROR; |
| 67 | - } |
| 68 | - |
| 69 | if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509) { |
| 70 | return GNUTLS_E_CERTIFICATE_ERROR; |
| 71 | } |
| 72 | |
| 73 | - /* check hostname */ |
| 74 | if ((certList = gnutls_certificate_get_peers (session, |
| 75 | &certListSize)) == NULL) { |
| 76 | return GNUTLS_E_CERTIFICATE_ERROR; |
| 77 | @@ -739,7 +714,14 @@ static int WaitressTlsVerify (gnutls_session_t session) { |
| 78 | return GNUTLS_E_CERTIFICATE_ERROR; |
| 79 | } |
| 80 | |
| 81 | - if (gnutls_x509_crt_check_hostname (cert, waith->url.host) == 0) { |
| 82 | + char fingerprint[20]; |
| 83 | + size_t fingerprintSize = sizeof (fingerprint); |
| 84 | + if (gnutls_x509_crt_get_fingerprint (cert, GNUTLS_DIG_SHA1, fingerprint, |
| 85 | + &fingerprintSize) != 0) { |
| 86 | + return GNUTLS_E_CERTIFICATE_ERROR; |
| 87 | + } |
| 88 | + |
| 89 | + if (memcmp (fingerprint, waith->tlsFingerprint, sizeof (fingerprint)) != 0) { |
| 90 | return GNUTLS_E_CERTIFICATE_ERROR; |
| 91 | } |
| 92 | |
| 93 | @@ -1036,8 +1018,6 @@ WaitressReturn_t WaitressFetchCall (WaitressHandle_t *waith) { |
| 94 | waith->request.write = WaitressOrdinaryWrite; |
| 95 | |
| 96 | if (waith->url.tls) { |
| 97 | - assert (waith->tlsInitialized); |
| 98 | - |
| 99 | waith->request.read = WaitressGnutlsRead; |
| 100 | waith->request.write = WaitressGnutlsWrite; |
| 101 | gnutls_init (&waith->request.tlsSession, GNUTLS_CLIENT); |
| 102 | @@ -1046,6 +1026,7 @@ WaitressReturn_t WaitressFetchCall (WaitressHandle_t *waith) { |
| 103 | "PERFORMANCE", &err) != GNUTLS_E_SUCCESS) { |
| 104 | return WAITRESS_RET_ERR; |
| 105 | } |
| 106 | + gnutls_certificate_allocate_credentials (&waith->tlsCred); |
| 107 | if (gnutls_credentials_set (waith->request.tlsSession, |
| 108 | GNUTLS_CRD_CERTIFICATE, |
| 109 | waith->tlsCred) != GNUTLS_E_SUCCESS) { |
| 110 | @@ -1083,6 +1064,7 @@ WaitressReturn_t WaitressFetchCall (WaitressHandle_t *waith) { |
| 111 | if (waith->url.tls) { |
| 112 | gnutls_bye (waith->request.tlsSession, GNUTLS_SHUT_RDWR); |
| 113 | gnutls_deinit (waith->request.tlsSession); |
| 114 | + gnutls_certificate_free_credentials (waith->tlsCred); |
| 115 | } |
| 116 | close (waith->request.sockfd); |
| 117 | |
| 118 | diff --git a/src/libwaitress/waitress.h b/src/libwaitress/waitress.h |
| 119 | index e1cf303..7e4401a 100644 |
| 120 | --- a/src/libwaitress/waitress.h |
| 121 | +++ b/src/libwaitress/waitress.h |
| 122 | @@ -92,8 +92,8 @@ typedef struct { |
| 123 | void *data; |
| 124 | WaitressCbReturn_t (*callback) (void *, size_t, void *); |
| 125 | int timeout; |
| 126 | + const char *tlsFingerprint; |
| 127 | gnutls_certificate_credentials_t tlsCred; |
| 128 | - bool tlsInitialized; |
| 129 | |
| 130 | /* per-request data */ |
| 131 | struct { |
| 132 | @@ -110,7 +110,7 @@ typedef struct { |
| 133 | } request; |
| 134 | } WaitressHandle_t; |
| 135 | |
| 136 | -WaitressReturn_t WaitressInit (WaitressHandle_t *, const char *); |
| 137 | +void WaitressInit (WaitressHandle_t *); |
| 138 | void WaitressFree (WaitressHandle_t *); |
| 139 | bool WaitressSetProxy (WaitressHandle_t *, const char *); |
| 140 | char *WaitressUrlEncode (const char *); |
| 141 | diff --git a/src/main.c b/src/main.c |
| 142 | index e14a88a..afa75da 100644 |
| 143 | --- a/src/main.c |
| 144 | +++ b/src/main.c |
| 145 | @@ -192,7 +192,7 @@ static void BarMainStartPlayback (BarApp_t *app, pthread_t *playerThread) { |
| 146 | /* setup player */ |
| 147 | memset (&app->player, 0, sizeof (app->player)); |
| 148 | |
| 149 | - WaitressInit (&app->player.waith, NULL); |
| 150 | + WaitressInit (&app->player.waith); |
| 151 | WaitressSetUrl (&app->player.waith, app->playlist->audioUrl); |
| 152 | |
| 153 | /* set up global proxy, player is NULLed on songfinish */ |
| 154 | @@ -328,7 +328,6 @@ int main (int argc, char **argv) { |
| 155 | static BarApp_t app; |
| 156 | /* terminal attributes _before_ we started messing around with ~ECHO */ |
| 157 | struct termios termOrig; |
| 158 | - WaitressReturn_t wRet; |
| 159 | |
| 160 | memset (&app, 0, sizeof (app)); |
| 161 | |
| 162 | @@ -355,19 +354,10 @@ int main (int argc, char **argv) { |
| 163 | app.settings.keys[BAR_KS_HELP]); |
| 164 | } |
| 165 | |
| 166 | - if ((wRet = WaitressInit (&app.waith, app.settings.tlsCaPath)) != WAITRESS_RET_OK) { |
| 167 | - if (wRet == WAITRESS_RET_TLS_TRUSTFILE_ERR) { |
| 168 | - BarUiMsg (&app.settings, MSG_ERR, "Can't load root certificates. " |
| 169 | - "Please check the tls_ca_path setting in your config file.\n"); |
| 170 | - } else { |
| 171 | - BarUiMsg (&app.settings, MSG_ERR, "Can't initialize HTTP library: " |
| 172 | - "%s\n", WaitressErrorToStr (wRet)); |
| 173 | - } |
| 174 | - goto die; |
| 175 | - } |
| 176 | - |
| 177 | + WaitressInit (&app.waith); |
| 178 | app.waith.url.host = strdup (PIANO_RPC_HOST); |
| 179 | app.waith.url.tls = true; |
| 180 | + app.waith.tlsFingerprint = app.settings.tlsFingerprint; |
| 181 | |
| 182 | /* init fds */ |
| 183 | FD_ZERO(&app.input.set); |
| 184 | @@ -388,7 +378,6 @@ int main (int argc, char **argv) { |
| 185 | |
| 186 | BarMainLoop (&app); |
| 187 | |
| 188 | -die: |
| 189 | if (app.input.fds[1] != -1) { |
| 190 | close (app.input.fds[1]); |
| 191 | } |
| 192 | diff --git a/src/settings.c b/src/settings.c |
| 193 | index f29fcfa..ee332cc 100644 |
| 194 | --- a/src/settings.c |
| 195 | +++ b/src/settings.c |
| 196 | @@ -93,7 +93,6 @@ void BarSettingsDestroy (BarSettings_t *settings) { |
| 197 | free (settings->npStationFormat); |
| 198 | free (settings->listSongFormat); |
| 199 | free (settings->fifo); |
| 200 | - free (settings->tlsCaPath); |
| 201 | for (size_t i = 0; i < MSG_COUNT; i++) { |
| 202 | free (settings->msgFormat[i].prefix); |
| 203 | free (settings->msgFormat[i].postfix); |
| 204 | @@ -132,7 +131,9 @@ void BarSettingsRead (BarSettings_t *settings) { |
| 205 | settings->listSongFormat = strdup ("%i) %a - %t%r"); |
| 206 | settings->fifo = malloc (PATH_MAX * sizeof (*settings->fifo)); |
| 207 | BarGetXdgConfigDir (PACKAGE "/ctl", settings->fifo, PATH_MAX); |
| 208 | - settings->tlsCaPath = strdup ("/etc/ssl/certs/ca-certificates.crt"); |
| 209 | + memcpy (settings->tlsFingerprint, "\xD9\x98\x0B\xA2\xCC\x0F\x97\xBB" |
| 210 | + "\x03\x82\x2C\x62\x11\xEA\xEA\x4A\x06\xEE\xF4\x27", |
| 211 | + sizeof (settings->tlsFingerprint)); |
| 212 | |
| 213 | settings->msgFormat[MSG_NONE].prefix = NULL; |
| 214 | settings->msgFormat[MSG_NONE].postfix = NULL; |
| 215 | @@ -241,9 +242,16 @@ void BarSettingsRead (BarSettings_t *settings) { |
| 216 | } else if (streq ("fifo", key)) { |
| 217 | free (settings->fifo); |
| 218 | settings->fifo = strdup (val); |
| 219 | - } else if (streq ("tls_ca_path", key)) { |
| 220 | - free (settings->tlsCaPath); |
| 221 | - settings->tlsCaPath = strdup (val); |
| 222 | + } else if (streq ("tls_fingerprint", key)) { |
| 223 | + /* expects 40 byte hex-encoded sha1 */ |
| 224 | + if (strlen (val) == 40) { |
| 225 | + for (size_t i = 0; i < 20; i++) { |
| 226 | + char hex[3]; |
| 227 | + memcpy (hex, &val[i*2], 2); |
| 228 | + hex[2] = '\0'; |
| 229 | + settings->tlsFingerprint[i] = strtol (hex, NULL, 16); |
| 230 | + } |
| 231 | + } |
| 232 | } else if (strncmp (formatMsgPrefix, key, |
| 233 | strlen (formatMsgPrefix)) == 0) { |
| 234 | static const char *mapping[] = {"none", "info", "nowplaying", |
| 235 | diff --git a/src/settings.h b/src/settings.h |
| 236 | index 6cb4cb2..8ce1225 100644 |
| 237 | --- a/src/settings.h |
| 238 | +++ b/src/settings.h |
| 239 | @@ -96,7 +96,7 @@ typedef struct { |
| 240 | char *npStationFormat; |
| 241 | char *listSongFormat; |
| 242 | char *fifo; |
| 243 | - char *tlsCaPath; |
| 244 | + char tlsFingerprint[20]; |
| 245 | BarMsgFormatStr_t msgFormat[MSG_COUNT]; |
| 246 | } BarSettings_t; |
| 247 | |