Line data Source code
1 : /*
2 : dn.cpp
3 :
4 : This file is part of qgpgme, the Qt API binding for gpgme
5 : Copyright (c) 2004 Klarälvdalens Datakonsult AB
6 : Copyright (c) 2016 by Bundesamt für Sicherheit in der Informationstechnik
7 : Software engineering by Intevation GmbH
8 :
9 : QGpgME is free software; you can redistribute it and/or
10 : modify it under the terms of the GNU General Public License as
11 : published by the Free Software Foundation; either version 2 of the
12 : License, or (at your option) any later version.
13 :
14 : QGpgME is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 : General Public License for more details.
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with this program; if not, write to the Free Software
21 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 :
23 : In addition, as a special exception, the copyright holders give
24 : permission to link the code of this program with any edition of
25 : the Qt library by Trolltech AS, Norway (or with modified versions
26 : of Qt that use the same license as Qt), and distribute linked
27 : combinations including the two. You must obey the GNU General
28 : Public License in all respects for all of the code used other than
29 : Qt. If you modify this file, you may extend this exception to
30 : your version of the file, but you are not obligated to do so. If
31 : you do not wish to do so, delete this exception statement from
32 : your version.
33 : */
34 :
35 : #ifdef HAVE_CONFIG_H
36 : #include "config.h"
37 : #endif
38 :
39 : #include "dn.h"
40 :
41 : #include <gpg-error.h>
42 :
43 : static const struct {
44 : const char *name;
45 : const char *oid;
46 : } oidmap[] = {
47 : // keep them ordered by oid:
48 : { "SP", "ST" }, // hack to show the Sphinx-required/desired SP for
49 : // StateOrProvince, otherwise known as ST or even S
50 : { "NameDistinguisher", "0.2.262.1.10.7.20" },
51 : { "EMAIL", "1.2.840.113549.1.9.1" },
52 : { "SN", "2.5.4.4" },
53 : { "SerialNumber", "2.5.4.5" },
54 : { "T", "2.5.4.12" },
55 : { "D", "2.5.4.13" },
56 : { "BC", "2.5.4.15" },
57 : { "ADDR", "2.5.4.16" },
58 : { "PC", "2.5.4.17" },
59 : { "GN", "2.5.4.42" },
60 : { "Pseudo", "2.5.4.65" },
61 : };
62 : static const unsigned int numOidMaps = sizeof oidmap / sizeof * oidmap;
63 :
64 1 : class QGpgME::DN::Private
65 : {
66 : public:
67 1 : Private() : mRefCount(0) {}
68 0 : Private(const Private &other)
69 0 : : attributes(other.attributes),
70 : reorderedAttributes(other.reorderedAttributes),
71 : order{"CN", "L", "_X_", "OU", "O", "C"},
72 0 : mRefCount(0)
73 : {
74 0 : }
75 :
76 1 : int ref()
77 : {
78 1 : return ++mRefCount;
79 : }
80 :
81 1 : int unref()
82 : {
83 1 : if (--mRefCount <= 0) {
84 1 : delete this;
85 1 : return 0;
86 : } else {
87 0 : return mRefCount;
88 : }
89 : }
90 :
91 0 : int refCount() const
92 : {
93 0 : return mRefCount;
94 : }
95 :
96 : DN::Attribute::List attributes;
97 : DN::Attribute::List reorderedAttributes;
98 : QStringList order;
99 : private:
100 : int mRefCount;
101 : };
102 :
103 : namespace
104 : {
105 : struct DnPair {
106 : char *key;
107 : char *value;
108 : };
109 : }
110 :
111 : // copied from CryptPlug and adapted to work on DN::Attribute::List:
112 :
113 : #define digitp(p) (*(p) >= '0' && *(p) <= '9')
114 : #define hexdigitp(a) (digitp (a) \
115 : || (*(a) >= 'A' && *(a) <= 'F') \
116 : || (*(a) >= 'a' && *(a) <= 'f'))
117 : #define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \
118 : *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
119 : #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1))
120 :
121 : static char *
122 5 : trim_trailing_spaces(char *string)
123 : {
124 : char *p, *mark;
125 :
126 15 : for (mark = NULL, p = string; *p; p++) {
127 10 : if (isspace(*p)) {
128 0 : if (!mark) {
129 0 : mark = p;
130 : }
131 : } else {
132 10 : mark = NULL;
133 : }
134 : }
135 5 : if (mark) {
136 0 : *mark = '\0';
137 : }
138 :
139 5 : return string;
140 : }
141 :
142 : /* Parse a DN and return an array-ized one. This is not a validating
143 : parser and it does not support any old-stylish syntax; gpgme is
144 : expected to return only rfc2253 compatible strings. */
145 : static const unsigned char *
146 5 : parse_dn_part(DnPair *array, const unsigned char *string)
147 : {
148 : const unsigned char *s, *s1;
149 : size_t n;
150 : char *p;
151 :
152 : /* parse attributeType */
153 5 : for (s = string + 1; *s && *s != '='; s++)
154 : ;
155 5 : if (!*s) {
156 0 : return NULL; /* error */
157 : }
158 5 : n = s - string;
159 5 : if (!n) {
160 0 : return NULL; /* empty key */
161 : }
162 5 : p = (char *)malloc(n + 1);
163 :
164 5 : memcpy(p, string, n);
165 5 : p[n] = 0;
166 5 : trim_trailing_spaces((char *)p);
167 : // map OIDs to their names:
168 65 : for (unsigned int i = 0; i < numOidMaps; ++i)
169 60 : if (!strcasecmp((char *)p, oidmap[i].oid)) {
170 0 : free(p);
171 0 : gpgrt_asprintf(&p, "%s", oidmap[i].name);
172 0 : break;
173 : }
174 5 : array->key = p;
175 5 : string = s + 1;
176 :
177 5 : if (*string == '#') {
178 : /* hexstring */
179 0 : string++;
180 0 : for (s = string; hexdigitp(s); s++) {
181 0 : s++;
182 : }
183 0 : n = s - string;
184 0 : if (!n || (n & 1)) {
185 0 : return NULL; /* empty or odd number of digits */
186 : }
187 0 : n /= 2;
188 0 : array->value = p = (char *)malloc(n + 1);
189 :
190 0 : for (s1 = string; n; s1 += 2, n--) {
191 0 : *p++ = xtoi_2(s1);
192 : }
193 0 : *p = 0;
194 : } else {
195 : /* regular v3 quoted string */
196 45 : for (n = 0, s = string; *s; s++) {
197 44 : if (*s == '\\') {
198 : /* pair */
199 1 : s++;
200 1 : if (*s == ',' || *s == '=' || *s == '+'
201 1 : || *s == '<' || *s == '>' || *s == '#' || *s == ';'
202 1 : || *s == '\\' || *s == '\"' || *s == ' ') {
203 0 : n++;
204 1 : } else if (hexdigitp(s) && hexdigitp(s + 1)) {
205 1 : s++;
206 1 : n++;
207 : } else {
208 0 : return NULL; /* invalid escape sequence */
209 : }
210 43 : } else if (*s == '\"') {
211 0 : return NULL; /* invalid encoding */
212 43 : } else if (*s == ',' || *s == '=' || *s == '+'
213 39 : || *s == '<' || *s == '>' || *s == '#' || *s == ';') {
214 : break;
215 : } else {
216 39 : n++;
217 : }
218 : }
219 :
220 5 : array->value = p = (char *)malloc(n + 1);
221 :
222 45 : for (s = string; n; s++, n--) {
223 40 : if (*s == '\\') {
224 1 : s++;
225 1 : if (hexdigitp(s)) {
226 1 : *p++ = xtoi_2(s);
227 1 : s++;
228 : } else {
229 0 : *p++ = *s;
230 : }
231 : } else {
232 39 : *p++ = *s;
233 : }
234 : }
235 5 : *p = 0;
236 : }
237 5 : return s;
238 : }
239 :
240 : /* Parse a DN and return an array-ized one. This is not a validating
241 : parser and it does not support any old-stylish syntax; gpgme is
242 : expected to return only rfc2253 compatible strings. */
243 : static QGpgME::DN::Attribute::List
244 1 : parse_dn(const unsigned char *string)
245 : {
246 1 : if (!string) {
247 0 : return QVector<QGpgME::DN::Attribute>();
248 : }
249 :
250 2 : QVector<QGpgME::DN::Attribute> result;
251 11 : while (*string) {
252 5 : while (*string == ' ') {
253 0 : string++;
254 : }
255 5 : if (!*string) {
256 0 : break; /* ready */
257 : }
258 :
259 5 : DnPair pair = { 0, 0 };
260 5 : string = parse_dn_part(&pair, string);
261 5 : if (!string) {
262 0 : goto failure;
263 : }
264 5 : if (pair.key && pair.value)
265 10 : result.push_back(QGpgME::DN::Attribute(QString::fromUtf8(pair.key),
266 15 : QString::fromUtf8(pair.value)));
267 5 : free(pair.key);
268 5 : free(pair.value);
269 :
270 5 : while (*string == ' ') {
271 0 : string++;
272 : }
273 5 : if (*string && *string != ',' && *string != ';' && *string != '+') {
274 0 : goto failure; /* invalid delimiter */
275 : }
276 5 : if (*string) {
277 4 : string++;
278 : }
279 : }
280 1 : return result;
281 :
282 : failure:
283 0 : return QVector<QGpgME::DN::Attribute>();
284 : }
285 :
286 : static QVector<QGpgME::DN::Attribute>
287 1 : parse_dn(const QString &dn)
288 : {
289 1 : return parse_dn((const unsigned char *)dn.toUtf8().data());
290 : }
291 :
292 10 : static QString dn_escape(const QString &s)
293 : {
294 10 : QString result;
295 90 : for (unsigned int i = 0, end = s.length(); i != end; ++i) {
296 80 : const QChar ch = s[i];
297 80 : switch (ch.unicode()) {
298 : case ',':
299 : case '+':
300 : case '"':
301 : case '\\':
302 : case '<':
303 : case '>':
304 : case ';':
305 0 : result += QLatin1Char('\\');
306 : // fall through
307 : default:
308 80 : result += ch;
309 : }
310 : }
311 10 : return result;
312 : }
313 :
314 : static QString
315 2 : serialise(const QVector<QGpgME::DN::Attribute> &dn, const QString &sep)
316 : {
317 4 : QStringList result;
318 12 : for (QVector<QGpgME::DN::Attribute>::const_iterator it = dn.begin(); it != dn.end(); ++it)
319 10 : if (!(*it).name().isEmpty() && !(*it).value().isEmpty()) {
320 10 : result.push_back((*it).name().trimmed() + QLatin1Char('=') + dn_escape((*it).value().trimmed()));
321 : }
322 4 : return result.join(sep);
323 : }
324 :
325 : static QGpgME::DN::Attribute::List
326 1 : reorder_dn(const QGpgME::DN::Attribute::List &dn, const QStringList &attrOrder)
327 : {
328 2 : QGpgME::DN::Attribute::List unknownEntries;
329 1 : QGpgME::DN::Attribute::List result;
330 1 : unknownEntries.reserve(dn.size());
331 1 : result.reserve(dn.size());
332 :
333 : // find all unknown entries in their order of appearance
334 6 : for (QGpgME::DN::const_iterator it = dn.begin(); it != dn.end(); ++it)
335 5 : if (!attrOrder.contains((*it).name())) {
336 0 : unknownEntries.push_back(*it);
337 : }
338 :
339 : // process the known attrs in the desired order
340 4 : for (QStringList::const_iterator oit = attrOrder.begin(); oit != attrOrder.end(); ++oit)
341 3 : if (*oit == QLatin1String("_X_")) {
342 : // insert the unknown attrs
343 : std::copy(unknownEntries.begin(), unknownEntries.end(),
344 0 : std::back_inserter(result));
345 0 : unknownEntries.clear(); // don't produce dup's
346 : } else {
347 18 : for (QGpgME::DN::const_iterator dnit = dn.begin(); dnit != dn.end(); ++dnit)
348 15 : if ((*dnit).name() == *oit) {
349 5 : result.push_back(*dnit);
350 : }
351 : }
352 :
353 2 : return result;
354 : }
355 :
356 : //
357 : //
358 : // class DN
359 : //
360 : //
361 :
362 0 : QGpgME::DN::DN()
363 : {
364 0 : d = new Private();
365 0 : d->ref();
366 0 : }
367 :
368 1 : QGpgME::DN::DN(const QString &dn)
369 : {
370 1 : d = new Private();
371 1 : d->ref();
372 1 : d->attributes = parse_dn(dn);
373 1 : }
374 :
375 0 : QGpgME::DN::DN(const char *utf8DN)
376 : {
377 0 : d = new Private();
378 0 : d->ref();
379 0 : if (utf8DN) {
380 0 : d->attributes = parse_dn((const unsigned char *)utf8DN);
381 : }
382 0 : }
383 :
384 0 : QGpgME::DN::DN(const DN &other)
385 0 : : d(other.d)
386 : {
387 0 : if (d) {
388 0 : d->ref();
389 : }
390 0 : }
391 :
392 2 : QGpgME::DN::~DN()
393 : {
394 1 : if (d) {
395 1 : d->unref();
396 : }
397 1 : }
398 :
399 0 : const QGpgME::DN &QGpgME::DN::operator=(const DN &that)
400 : {
401 0 : if (this->d == that.d) {
402 0 : return *this;
403 : }
404 :
405 0 : if (that.d) {
406 0 : that.d->ref();
407 : }
408 0 : if (this->d) {
409 0 : this->d->unref();
410 : }
411 :
412 0 : this->d = that.d;
413 :
414 0 : return *this;
415 : }
416 :
417 1 : QString QGpgME::DN::prettyDN() const
418 : {
419 1 : if (!d) {
420 0 : return QString();
421 : }
422 1 : if (d->reorderedAttributes.empty()) {
423 1 : d->reorderedAttributes = reorder_dn(d->attributes, d->order);
424 : }
425 2 : return serialise(d->reorderedAttributes, QStringLiteral(","));
426 : }
427 :
428 1 : QString QGpgME::DN::dn() const
429 : {
430 2 : return d ? serialise(d->attributes, QStringLiteral(",")) : QString();
431 : }
432 :
433 0 : QString QGpgME::DN::dn(const QString &sep) const
434 : {
435 0 : return d ? serialise(d->attributes, sep) : QString();
436 : }
437 :
438 : // static
439 0 : QString QGpgME::DN::escape(const QString &value)
440 : {
441 0 : return dn_escape(value);
442 : }
443 :
444 0 : void QGpgME::DN::detach()
445 : {
446 0 : if (!d) {
447 0 : d = new QGpgME::DN::Private();
448 0 : d->ref();
449 0 : } else if (d->refCount() > 1) {
450 0 : QGpgME::DN::Private *d_save = d;
451 0 : d = new QGpgME::DN::Private(*d);
452 0 : d->ref();
453 0 : d_save->unref();
454 : }
455 0 : }
456 :
457 0 : void QGpgME::DN::append(const Attribute &attr)
458 : {
459 0 : detach();
460 0 : d->attributes.push_back(attr);
461 0 : d->reorderedAttributes.clear();
462 0 : }
463 :
464 0 : QString QGpgME::DN::operator[](const QString &attr) const
465 : {
466 0 : if (!d) {
467 0 : return QString();
468 : }
469 0 : const QString attrUpper = attr.toUpper();
470 0 : for (QVector<Attribute>::const_iterator it = d->attributes.constBegin();
471 0 : it != d->attributes.constEnd(); ++it)
472 0 : if ((*it).name() == attrUpper) {
473 0 : return (*it).value();
474 : }
475 0 : return QString();
476 : }
477 :
478 8 : static QVector<QGpgME::DN::Attribute> empty;
479 :
480 0 : QGpgME::DN::const_iterator QGpgME::DN::begin() const
481 : {
482 0 : return d ? d->attributes.constBegin() : empty.constBegin();
483 : }
484 :
485 0 : QGpgME::DN::const_iterator QGpgME::DN::end() const
486 : {
487 0 : return d ? d->attributes.constEnd() : empty.constEnd();
488 : }
489 :
490 1 : void QGpgME::DN::setAttributeOrder (const QStringList &order) const
491 : {
492 1 : d->order = order;
493 1 : }
494 :
495 0 : const QStringList & QGpgME::DN::attributeOrder () const
496 : {
497 0 : return d->order;
498 24 : }
|