LCOV - code coverage report
Current view: top level - lang/qt/src - dn.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 119 219 54.3 %
Date: 2018-11-15 08:49:49 Functions: 20 34 58.8 %

          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 : }

Generated by: LCOV version 1.13