1 /* vi: set sw=4 ts=4:
2  *
3  * Copyright (C) 2001 - 2012 Christian Hohnstaedt.
4  *
5  * All rights reserved.
6  */
7 
8 #include "x509rev.h"
9 #include "db.h"
10 #include "pki_base.h"
11 #include "func.h"
12 #include "exception.h"
13 #include <openssl/x509v3.h>
14 #include <QStringList>
15 #include <QVariant>
16 #include <QSqlQuery>
17 
18 #include "openssl_compat.h"
19 
20 #ifndef CRL_REASON_UNSPECIFIED
21 #define CRL_REASON_UNSPECIFIED                  0
22 #define CRL_REASON_KEY_COMPROMISE               1
23 #define CRL_REASON_CA_COMPROMISE                2
24 #define CRL_REASON_AFFILIATION_CHANGED          3
25 #define CRL_REASON_SUPERSEDED                   4
26 #define CRL_REASON_CESSATION_OF_OPERATION       5
27 #define CRL_REASON_CERTIFICATE_HOLD             6
28 #define CRL_REASON_REMOVE_FROM_CRL              8
29 #define CRL_REASON_PRIVILEGE_WITHDRAWN          9
30 #define CRL_REASON_AA_COMPROMISE                10
31 #endif
32 
33 static ENUMERATED_NAMES crl_reasons[] = {
34 {CRL_REASON_UNSPECIFIED,         "Unspecified", "unspecified"},
35 {CRL_REASON_KEY_COMPROMISE,      "Key Compromise", "keyCompromise"},
36 {CRL_REASON_CA_COMPROMISE,       "CA Compromise", "CACompromise"},
37 {CRL_REASON_AFFILIATION_CHANGED, "Affiliation Changed", "affiliationChanged"},
38 {CRL_REASON_SUPERSEDED,          "Superseded", "superseded"},
39 {CRL_REASON_CESSATION_OF_OPERATION,
40                         "Cessation Of Operation", "cessationOfOperation"},
41 {CRL_REASON_CERTIFICATE_HOLD,    "Certificate Hold", "certificateHold"},
42 {CRL_REASON_REMOVE_FROM_CRL,     "Remove From CRL", "removeFromCRL"},
43 {CRL_REASON_PRIVILEGE_WITHDRAWN, "Privilege Withdrawn", "privilegeWithdrawn"},
44 {CRL_REASON_AA_COMPROMISE,       "AA Compromise", "AACompromise"},
45 {-1, NULL, NULL}
46 };
47 
crlreasons()48 QStringList x509rev::crlreasons()
49 {
50 	QStringList l;
51 	for (int i=0; crl_reasons[i].lname; i++)
52 		l << crl_reasons[i].lname;
53 	return l;
54 }
55 
getReason() const56 QString x509rev::getReason() const
57 {
58 	return crl_reasons[reason_idx].lname;
59 }
60 
reasonBit2Idx(int bit)61 static int reasonBit2Idx(int bit)
62 {
63 	for (int i=0; crl_reasons[i].lname; i++) {
64 		if (bit == crl_reasons[i].bitnum) {
65 			return i;
66 		}
67 	}
68 	return 0;
69 }
70 
fromREVOKED(const X509_REVOKED * rev)71 void x509rev::fromREVOKED(const X509_REVOKED *rev)
72 {
73 	ASN1_ENUMERATED *reason;
74 	ASN1_TIME *at;
75 	int j = -1, r;
76 
77 	if (!rev)
78 		return;
79 	serial = a1int(X509_REVOKED_get0_serialNumber(rev));
80 	date = a1time(X509_REVOKED_get0_revocationDate(rev));
81 
82 	reason = (ASN1_ENUMERATED *)X509_REVOKED_get_ext_d2i(
83 			(X509_REVOKED *)rev, NID_crl_reason, &j, NULL);
84 	openssl_error();
85 	reason_idx = 0;
86 	if (reason) {
87 		r = ASN1_ENUMERATED_get(reason);
88 		openssl_error();
89 		reason_idx = reasonBit2Idx(r);
90 		ASN1_ENUMERATED_free(reason);
91 	}
92 	ivalDate.setUndefined();
93 	at = (ASN1_TIME *)X509_REVOKED_get_ext_d2i((X509_REVOKED *)rev,
94 			NID_invalidity_date, &j, NULL);
95 	openssl_error();
96 	if (at) {
97 		ivalDate = a1time(at);
98 		ASN1_GENERALIZEDTIME_free(at);
99 	}
100 	qDebug() << *this;
101 }
102 
toREVOKED(bool withReason) const103 X509_REVOKED *x509rev::toREVOKED(bool withReason) const
104 {
105 	a1time i = ivalDate;
106 	a1time d = date;
107 	X509_REVOKED *rev = X509_REVOKED_new();
108 	check_oom(rev);
109 	X509_REVOKED_set_serialNumber(rev, serial.get());
110 	X509_REVOKED_set_revocationDate(rev, d.get_utc());
111 	X509_REVOKED_add1_ext_i2d(rev, NID_invalidity_date, i.get(), 0, 0);
112 
113 	/* RFC says to not add the extension if it is "unspecified" */
114 	if (reason_idx != 0 && withReason) {
115 		ASN1_ENUMERATED *a = ASN1_ENUMERATED_new();
116 		ASN1_ENUMERATED_set(a, crl_reasons[reason_idx].bitnum);
117 		X509_REVOKED_add1_ext_i2d(rev, NID_crl_reason, a, 0, 0);
118 		ASN1_ENUMERATED_free(a);
119 	}
120 	openssl_error();
121 	qDebug() << *this;
122 	return rev;
123 }
124 
d2i(QByteArray & ba)125 void x509rev::d2i(QByteArray &ba)
126 {
127 	X509_REVOKED *r;
128 	r = (X509_REVOKED *)d2i_bytearray(D2I_VOID(d2i_X509_REVOKED), ba);
129 	if (!r)
130 		return;
131 	fromREVOKED(r);
132 	X509_REVOKED_free(r);
133 }
134 
i2d() const135 QByteArray x509rev::i2d() const
136 {
137 	QByteArray ba;
138 	X509_REVOKED *r = toREVOKED();
139 	ba = i2d_bytearray(I2D_VOID(i2d_X509_REVOKED), r);
140 	X509_REVOKED_free(r);
141 	return ba;
142 }
143 
set(const x509rev & x)144 void x509rev::set(const x509rev &x)
145 {
146 	serial = x.serial;
147 	date = x.date;
148 	ivalDate = x.ivalDate;
149 	reason_idx = x.reason_idx;
150 	crlNo = x.crlNo;
151 }
152 
identical(const x509rev & x) const153 bool x509rev::identical(const x509rev &x) const
154 {
155 	return	serial == x.serial &&
156 		date == x.date &&
157 		ivalDate == x.ivalDate &&
158 		reason_idx == x.reason_idx;
159 }
160 
operator QString() const161 x509rev::operator QString() const
162 {
163 	return QString("Rev: %1 D:%2 I:%3 Reason: %4 '%5'\n")
164 		.arg(serial.toHex(), date.toSortable(), ivalDate.toSortable())
165 		.arg(reason_idx).arg(crl_reasons[reason_idx].lname);
166 }
167 
x509rev(QSqlRecord rec,int offset)168 x509rev::x509rev(QSqlRecord rec, int offset)
169 {
170 	serial.setHex(rec.value(offset).toString());
171 	date.fromPlain(rec.value(offset +1).toString());
172 	ivalDate.fromPlain(rec.value(offset +2).toString());
173 	crlNo = rec.value(offset +3).toInt();
174 	reason_idx = reasonBit2Idx(rec.value(offset +4).toInt());
175 	qDebug() << *this;
176 }
177 
executeQuery(XSqlQuery & q)178 void x509rev::executeQuery(XSqlQuery &q)
179 {
180 	// 0 is the caId
181 	q.bindValue(1, serial.toHex());
182 	q.bindValue(2, date.toPlain());
183 	q.bindValue(3, ivalDate.toPlain());
184 	q.bindValue(4, crlNo ? QVariant(crlNo) : QVariant());
185 	q.bindValue(5, crl_reasons[reason_idx].bitnum);
186 	q.exec();
187 }
188 
fromBA(QByteArray & ba)189 void x509revList::fromBA(QByteArray &ba)
190 {
191 	int i, num = db::intFromData(ba);
192 	x509rev r;
193 	clear();
194 	merged = false;
195 	for (i=0; i<num; i++) {
196 		r.d2i(ba);
197 		append(r);
198 	}
199 }
200 
toBA()201 QByteArray x509revList::toBA()
202 {
203 	int i, len = size();
204 	QByteArray ba(db::intToData(len));
205 
206 	for (i=0; i<len; i++) {
207 		ba += at(i).i2d();
208 	}
209 	return ba;
210 }
211 
merge(const x509revList & other)212 void x509revList::merge(const x509revList &other)
213 {
214 	foreach(x509rev r, other) {
215 		if (r.isValid() && !contains(r)) {
216 			merged = true;
217 			append(r);
218 		}
219 	}
220 }
221 
identical(const x509revList & other) const222 bool x509revList::identical(const x509revList &other) const
223 {
224 	if (size() != other.size())
225 		return false;
226 	for (int i=0; i<size(); i++) {
227 		x509rev r = at(i);
228 		int c = other.indexOf(r);
229 		if (c == -1)
230 			return false;
231 		if (!r.identical(other.at(c)))
232 			return false;
233 	}
234 	return true;
235 }
236 
fromSql(QVariant caId)237 x509revList x509revList::fromSql(QVariant caId)
238 {
239 	XSqlQuery q;
240 	x509revList list;
241 
242 	SQL_PREPARE(q, "SELECT serial, date, invaldate, crlNo, reasonBit "
243 			"FROM revocations WHERE caId=?");
244 	q.bindValue(0, caId);
245 	q.exec();
246 	if (q.lastError().isValid())
247 		return list;
248 	while (q.next()) {
249 		x509rev r(q.record());
250 		list.append(r);
251 	}
252 	list.merged = false;
253 	return list;
254 }
255 
sqlUpdate(QVariant caId)256 bool x509revList::sqlUpdate(QVariant caId)
257 {
258 	XSqlQuery q;
259 	Transaction;
260 
261 	if (!TransBegin())
262 		return false;
263 
264 	x509revList oldList = fromSql(caId);
265 
266 	SQL_PREPARE(q, "DELETE FROM revocations WHERE caId=?");
267 	q.bindValue(0, caId);
268 	q.exec();
269 	if (q.lastError().isValid())
270 		return false;
271 
272 	SQL_PREPARE(q, "INSERT INTO revocations "
273 			"(caId, serial, date, invaldate, crlNo, reasonBit) "
274 			"VALUES (?,?,?,?,?,?)");
275 	q.bindValue(0, caId);
276 	foreach(x509rev r, *this) {
277 		if (r.getCrlNo() == 0) {
278 			int idx = oldList.indexOf(r);
279 			if (idx != -1) {
280 				x509rev old = oldList.takeAt(idx);
281 				r.setCrlNo(old.getCrlNo());
282 				qDebug() << "RECOVER OLD CRL NO" << r ;
283 			}
284 		}
285 		r.executeQuery(q);
286 		if (q.lastError().isValid())
287 			return false;
288 	}
289 
290         merged = false;
291 	TransCommit();
292 	return true;
293 }
294