1 // Copyright 2005-2019 The Mumble Developers. All rights reserved.
2 // Use of this source code is governed by a BSD-style license
3 // that can be found in the LICENSE file at the root of the
4 // Mumble source tree or at <https://www.mumble.info/LICENSE>.
5 
6 #include "mumble_pch.hpp"
7 
8 #include "ViewCert.h"
9 
decode_utf8_qssl_string(const QString & input)10 static QString decode_utf8_qssl_string(const QString &input) {
11 	QString i = input;
12 	return QUrl::fromPercentEncoding(i.replace(QLatin1String("\\x"), QLatin1String("%")).toLatin1());
13 }
14 
15 #if QT_VERSION >= 0x050000
processQSslCertificateInfo(QStringList in)16 static QStringList processQSslCertificateInfo(QStringList in) {
17 	QStringList list;
18 	foreach (QString str, in) {
19 		list << decode_utf8_qssl_string(str);
20 	}
21 	return list;
22 }
23 #else
processQSslCertificateInfo(QString in)24 static QStringList processQSslCertificateInfo(QString in) {
25 	QStringList out;
26 	out << decode_utf8_qssl_string(in);
27 	return out;
28 }
29 #endif
30 
addQSslCertificateInfo(QStringList & l,const QString & label,const QStringList & items)31 static void addQSslCertificateInfo(QStringList &l, const QString &label, const QStringList &items) {
32 	foreach (const QString &item, items) {
33 		l << QString(QLatin1String("%1: %2")).arg(label, item);
34 	}
35 }
36 
certificateFriendlyName(const QSslCertificate & cert)37 static QString certificateFriendlyName(const QSslCertificate &cert) {
38 	QStringList cnList = processQSslCertificateInfo(cert.subjectInfo(QSslCertificate::CommonName));
39 	QStringList orgList = processQSslCertificateInfo(cert.subjectInfo(QSslCertificate::Organization));
40 
41 	QString cn;
42 	if (cnList.count() > 0) {
43 		cn = cnList.at(0);
44 	}
45 
46 	QString org;
47 	if (orgList.count() > 0) {
48 		org = orgList.at(0);
49 	}
50 
51 	return QString(QLatin1String("%1 %2")).arg(cn, org);
52 }
53 
ViewCert(QList<QSslCertificate> cl,QWidget * p)54 ViewCert::ViewCert(QList<QSslCertificate> cl, QWidget *p) : QDialog(p) {
55 	qlCerts = cl;
56 
57 	setWindowTitle(tr("Certificate Chain Details"));
58 
59 	QHBoxLayout *h;
60 	QVBoxLayout *v;
61 	QGroupBox *qcbChain, *qcbDetails;
62 
63 	qcbChain=new QGroupBox(tr("Certificate chain"), this);
64 	h = new QHBoxLayout(qcbChain);
65 	qlwChain = new QListWidget(qcbChain);
66 	qlwChain->setObjectName(QLatin1String("Chain"));
67 
68 	// load certs into a set as a hacky fix to #2141
69 #if QT_VERSION >= 0x050400
70 	QSet<QSslCertificate> qlCertSet;
71 #else
72 	QList<QSslCertificate> qlCertSet;
73 #endif
74 	foreach(QSslCertificate c, qlCerts) {
75 		if(!qlCertSet.contains(c)) {
76 			qlwChain->addItem(certificateFriendlyName(c));
77 			qlCertSet << c;
78 		}
79 	}
80 	h->addWidget(qlwChain);
81 
82 	qcbDetails=new QGroupBox(tr("Certificate details"), this);
83 	h = new QHBoxLayout(qcbDetails);
84 	qlwCert = new QListWidget(qcbDetails);
85 	h->addWidget(qlwCert);
86 
87 	QDialogButtonBox *qdbb = new QDialogButtonBox(QDialogButtonBox::Ok, Qt::Horizontal, this);
88 
89 	v = new QVBoxLayout(this);
90 	v->addWidget(qcbChain);
91 	v->addWidget(qcbDetails);
92 	v->addWidget(qdbb);
93 
94 	QMetaObject::connectSlotsByName(this);
95 	connect(qdbb, SIGNAL(accepted()), this, SLOT(accept()));
96 
97 	resize(510,300);
98 }
99 
prettifyDigest(QString digest)100 QString ViewCert::prettifyDigest(QString digest) {
101 	QString pretty_digest = digest.toUpper();
102 	int step = 2;
103 	QChar separator = QChar::fromLatin1(':');
104 	for (int i = step; i < pretty_digest.size(); i += step + 1) {
105 		pretty_digest.insert(i, separator);
106 	}
107 	return pretty_digest;
108 }
109 
on_Chain_currentRowChanged(int idx)110 void ViewCert::on_Chain_currentRowChanged(int idx) {
111 	qlwCert->clear();
112 	if ((idx < 0) || (idx >= qlCerts.size()))
113 		return;
114 
115 	QStringList l;
116 	const QSslCertificate &c=qlCerts.at(idx);
117 
118 	addQSslCertificateInfo(l, tr("Common Name"), processQSslCertificateInfo(c.subjectInfo(QSslCertificate::CommonName)));
119 	addQSslCertificateInfo(l, tr("Organization"), processQSslCertificateInfo(c.subjectInfo(QSslCertificate::Organization)));
120 	addQSslCertificateInfo(l, tr("Subunit"), processQSslCertificateInfo(c.subjectInfo(QSslCertificate::OrganizationalUnitName)));
121 	addQSslCertificateInfo(l, tr("Country"), processQSslCertificateInfo(c.subjectInfo(QSslCertificate::CountryName)));
122 	addQSslCertificateInfo(l, tr("Locality"), processQSslCertificateInfo(c.subjectInfo(QSslCertificate::LocalityName)));
123 	addQSslCertificateInfo(l, tr("State"), processQSslCertificateInfo(c.subjectInfo(QSslCertificate::StateOrProvinceName)));
124 	l << tr("Valid from: %1").arg(c.effectiveDate().toString());
125 	l << tr("Valid to: %1").arg(c.expiryDate().toString());
126 	l << tr("Serial: %1").arg(QString::fromLatin1(c.serialNumber().toHex()));
127 	l << tr("Public Key: %1 bits %2").arg(c.publicKey().length()).arg((c.publicKey().algorithm() == QSsl::Rsa) ? tr("RSA") : tr("DSA"));
128 	l << tr("Digest (SHA-1): %1").arg(prettifyDigest(QString::fromLatin1(c.digest(QCryptographicHash::Sha1).toHex())));
129 #if QT_VERSION >= 0x050000
130 	l << tr("Digest (SHA-256): %1").arg(prettifyDigest(QString::fromLatin1(c.digest(QCryptographicHash::Sha256).toHex())));
131 #endif
132 
133 #if QT_VERSION >= 0x050000
134 	const QMultiMap<QSsl::AlternativeNameEntryType, QString> &alts = c.subjectAlternativeNames();
135 	QMultiMap<QSsl::AlternativeNameEntryType, QString>::const_iterator i;
136 #else
137 	const QMultiMap<QSsl::AlternateNameEntryType, QString> &alts = c.alternateSubjectNames();
138 	QMultiMap<QSsl::AlternateNameEntryType, QString>::const_iterator i;
139 #endif
140 
141 	for (i=alts.constBegin(); i != alts.constEnd(); ++i) {
142 		switch (i.key()) {
143 			case QSsl::EmailEntry: {
144 					l << tr("Email: %1").arg(i.value());
145 				}
146 				break;
147 			case QSsl::DnsEntry: {
148 					l << tr("DNS: %1").arg(i.value());
149 				}
150 				break;
151 			default:
152 				break;
153 		}
154 	}
155 
156 	l << QString();
157 	l << tr("Issued by:");
158 	addQSslCertificateInfo(l, tr("Common Name"), processQSslCertificateInfo(c.issuerInfo(QSslCertificate::CommonName)));
159 	addQSslCertificateInfo(l, tr("Organization"), processQSslCertificateInfo(c.issuerInfo(QSslCertificate::Organization)));
160 	addQSslCertificateInfo(l, tr("Unit Name"), processQSslCertificateInfo(c.issuerInfo(QSslCertificate::OrganizationalUnitName)));
161 	addQSslCertificateInfo(l, tr("Country"), processQSslCertificateInfo(c.issuerInfo(QSslCertificate::CountryName)));
162 	addQSslCertificateInfo(l, tr("Locality"), processQSslCertificateInfo(c.issuerInfo(QSslCertificate::LocalityName)));
163 	addQSslCertificateInfo(l, tr("State"), processQSslCertificateInfo(c.issuerInfo(QSslCertificate::StateOrProvinceName)));
164 
165 	qlwCert->addItems(l);
166 }
167