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