-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
SSL Certificate Hot-Reload for Mumble Server #6974
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -214,3 +214,84 @@ void Server::initializeCert() { | |
| const QString Server::getDigest() const { | ||
| return QString::fromLatin1(qscCert.digest(QCryptographicHash::Sha1).toHex()); | ||
| } | ||
|
|
||
| void Server::initCertMonitoring() { | ||
| connect(&qtCertCheck, SIGNAL(timeout()), this, SLOT(checkCertExpiry())); | ||
| // Check certificate expiry every hour | ||
| qtCertCheck.start(60 * 60 * 1000); | ||
| } | ||
|
|
||
| void Server::checkCertExpiry() { | ||
| if (qscCert.isNull()) { | ||
| return; | ||
| } | ||
|
|
||
| QDateTime now = QDateTime::currentDateTime(); | ||
| QDateTime expiryDate = qscCert.expiryDate(); | ||
| qint64 daysUntilExpiry = now.daysTo(expiryDate); | ||
|
|
||
| // Check if certificate expires within 7 days or has already expired | ||
| if (daysUntilExpiry <= 7 && daysUntilExpiry >= 0) { | ||
| log(QString("Certificate expires in %1 days, attempting to reload from disk").arg(daysUntilExpiry)); | ||
|
|
||
| if (reloadCertFromDisk()) { | ||
| log("Successfully reloaded certificate from disk"); | ||
| // Update expiry date after reload | ||
| expiryDate = qscCert.expiryDate(); | ||
| daysUntilExpiry = now.daysTo(expiryDate); | ||
| log(QString("New certificate expires in %1 days").arg(daysUntilExpiry)); | ||
| } else { | ||
| log("Failed to reload certificate from disk, keeping current certificate"); | ||
| } | ||
| } else if (daysUntilExpiry < 0) { | ||
| log(QString("Certificate has expired %1 days ago, attempting to reload from disk").arg(-daysUntilExpiry)); | ||
|
|
||
| if (reloadCertFromDisk()) { | ||
| log("Successfully reloaded certificate from disk"); | ||
| } else { | ||
| log("Failed to reload certificate from disk, keeping current expired certificate"); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| bool Server::reloadCertFromDisk() { | ||
| // Only reload if we're using Meta certificate (which is loaded from file) | ||
| if (!bUsingMetaCert) { | ||
| // This server has a database-stored certificate, cannot reload from disk | ||
| return false; | ||
| } | ||
|
|
||
| // Store current certificate as backup | ||
| QSslCertificate oldCert = qscCert; | ||
| QSslKey oldKey = qskKey; | ||
| QList< QSslCertificate > oldInter = qlIntermediates; | ||
|
|
||
| // Force Meta to reload its SSL settings from disk | ||
| if (!Meta::mp->loadSSLSettings()) { | ||
| log("Failed to reload SSL settings from murmur.ini"); | ||
| return false; | ||
| } | ||
|
|
||
| // Try to reload from Meta's certificate | ||
| qskKey = Meta::mp->qskKey; | ||
| qscCert = Meta::mp->qscCert; | ||
| qlIntermediates = Meta::mp->qlIntermediates; | ||
|
|
||
| // Verify that the reloaded certificate is valid and different | ||
| if (qscCert.isNull() || qskKey.isNull()) { | ||
| // Reload failed, restore old certificate | ||
| qscCert = oldCert; | ||
| qskKey = oldKey; | ||
| qlIntermediates = oldInter; | ||
| return false; | ||
| } | ||
|
|
||
| // Check if certificate actually changed | ||
| if (qscCert == oldCert) { | ||
| // Certificate hasn't changed on disk | ||
| return false; | ||
| } | ||
|
|
||
| // Successfully reloaded new certificate | ||
| return true; | ||
| } | ||
|
Comment on lines
+268
to
+348
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Critical: Add validation and null pointer checks. Multiple safety issues in the certificate reload logic:
Apply these fixes: bool Server::reloadCertFromDisk() {
// Only reload if we're using Meta certificate (which is loaded from file)
if (!bUsingMetaCert) {
// This server has a database-stored certificate, cannot reload from disk
return false;
}
+ if (!Meta::mp) {
+ log("Meta not initialized, cannot reload certificate");
+ return false;
+ }
+
// Store current certificate as backup
QSslCertificate oldCert = qscCert;
QSslKey oldKey = qskKey;
QList< QSslCertificate > oldInter = qlIntermediates;
// Force Meta to reload its SSL settings from disk
if (!Meta::mp->loadSSLSettings()) {
log("Failed to reload SSL settings from murmur.ini");
return false;
}
+ QWriteLocker lock(&qrwlVoiceThread);
+
// Try to reload from Meta's certificate
qskKey = Meta::mp->qskKey;
qscCert = Meta::mp->qscCert;
qlIntermediates = Meta::mp->qlIntermediates;
// Verify that the reloaded certificate is valid and different
if (qscCert.isNull() || qskKey.isNull()) {
// Reload failed, restore old certificate
qscCert = oldCert;
qskKey = oldKey;
qlIntermediates = oldInter;
return false;
}
+ // Verify the key matches the certificate
+ if (!isKeyForCert(qskKey, qscCert)) {
+ log("Reloaded certificate and key do not match");
+ qscCert = oldCert;
+ qskKey = oldKey;
+ qlIntermediates = oldInter;
+ return false;
+ }
+
+ // Check if new certificate is already expired
+ if (qscCert.expiryDate() < QDateTime::currentDateTime()) {
+ log("Reloaded certificate is already expired");
+ qscCert = oldCert;
+ qskKey = oldKey;
+ qlIntermediates = oldInter;
+ return false;
+ }
+
// Check if certificate actually changed
if (qscCert == oldCert) {
// Certificate hasn't changed on disk
return false;
}
// Successfully reloaded new certificate
return true;
}🤖 Prompt for AI Agents |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Add thread synchronization for certificate access.
This function reads and updates
qscCert,qskKey, andqlIntermediates(viareloadCertFromDisk()) without holding any locks. Given thatServerinherits fromQThreadand hasqrwlVoiceThreadfor synchronizing access to shared data, certificate members should also be protected.Consider:
newClient()around line 1435-1436) readqscCertandqskKeyto configure SSLcheckCertExpiry()could cause data racesWrap certificate access with appropriate locking:
void Server::checkCertExpiry() { + QReadLocker lock(&qrwlVoiceThread); + if (qscCert.isNull()) { return; } QDateTime now = QDateTime::currentDateTime(); QDateTime expiryDate = qscCert.expiryDate(); qint64 daysUntilExpiry = now.daysTo(expiryDate); + lock.unlock(); + // Check if certificate expires within 7 days or has already expired if (daysUntilExpiry <= 7 && daysUntilExpiry >= 0) {And update
reloadCertFromDisk()to acquire a write lock when modifying certificates.🤖 Prompt for AI Agents