[Date Prev][Date Next] [Chronological] [Thread] [Top]

PGP Backend design/implementation



Hi there!

I just want to describe my implementation of PGP backend as requested by
Kurt Zeilenga.

First of all - pgp backend was designed with one main target - accept and
process calls from PGP GUI by PGP Inc. All information about those calls was
obtained by reverse engineering of PGP 5.5.x freeware source code and
digging in ldap://certserver.pgp.com . All PGP-related procedures in pgp
backend was written using PGP SDK from PGP Inc.
 PGP backend was designed to store PGP keys generated by any software, that
compatible with PGP SDK 1.1.1.
 Currently  pgp backend works fine with PGP but not limited to this task
only. Pgp backend supports all of LDAP calls to it's database.

Entries:

Incoming PGP key stored in two entries: PGPcertificate - that contain PGP
key itself as PGPKEY attribute, and PGPUserID that contain all valuable
information about PGP key for fast, indexed search. PGPUserID is a children
of PGPCertificate. ie:

Incoming key of "Alex Iliynsky <starder@rosinter.ru> with "long" KeyID
"A5FF344FA9AF6B3F" will be stored in directory as:
1st entry:
PGPCERTID=A5FF344FA9AF6B3F,<BASEKEYSPACEDN>
PGPKey:: ....
objectclass: pgpcertificate

2nd entry:
PGPUserID=Alex Iliynsky <starder@rosinter.ru>,
PGPCERTID=A5FF344FA9AF6B3F,<BASEKEYSPACEDN>
pgpuserid: ...
pgpkeytype:
pgpkeysize:...
...
objectclass: pgpuserid

where BASEKEYSPACEDN  is DN where all keys are stored.

PGPUserID may have a following attributes:
pgpuserid - userid of key owner
pgprevoked - revokation status of key
pgpdisabled - disabled on server if == 1
pgpkeytype - type of key - DSS/DH or RSA
pgpkeyid - "short" key id
pgpsignerid - "long" key id
pgpkeysize - size of key in bits
pgpsubkeyid - keyid of subkey
pgpsignerid - "long" key id of signer's key
pgpcreatetime - key creation time in format YYYYMMDDHHmmssZ
pgpexpiretime - key expiration date (if not exist - key will never expired)

Several words about pgpdisabled attribute. It's not a actual key attribute
like all others. This attribute is used for disabling key on server. All
search calls from PGP GUI comes with filter (&(userfilter)(pgpdisabled=0)),
except when user looking for disabled keys on server.

There is another special entry in database. It can be only one per server
and contain a two important attrbutes. This entry is ServerInfo

dn: cn=pgpserverinfo
basekeyspacedn: ou=active, o=pgp keyspace, c=US
basependingdn: ou=pending, o=pgp keyspace, c=US
objectclass: serverinfo

Where - basekeyspacedn is a pointer to place in directory where all new keys
will be stored, and basependingdn is a pointer to where temporary denied
keys are stored. To be exact - I don't know real means of basependingdn :)
It's not used in my version of PGP sdk/GUI. But as I understand - pendingDN
can be used for storing keys, that failed for some reasons such as required
authorization. PGP GUI (pgpkey) have a "partial" implementation of such
behavior, but does not support it.

Calls from PGP to PGP Keyserver:

PGP issues two type of requests  to Keyserver: add and search.

Search is usual ldap search request except for one thing: PGP Key is stored
in PGPCertificate entry, but all searches proceeds on PGPUserID entries. PGP
doesn't interested in PGPkey details - it asks always for PGPKey attribute
that is absent in PGPUserID entry. Because of this, pgp backend's search
procedure checks before sending entry to client - is client requested pgpkey
attribute? If so, it returns _parent_ entry of founded entry.

Before any operation with server, pgpkeys sends a search request for
ServerInfo object and extracts from it baskeyspace/basependingdn that are
used in all following calls to server.


Add request - is a main interface between pgp client and server.

All Add requests from PGP client comes with dn: "pgpcertid=virtual".

There are two significant attributes of incoming entries:
PGPKey - contain ASCII armored PGP key for storing on server, and
PGPRequest - contain PGP-signed request to server.
It can be only one attribute per incoming entry - PGPkey or PGPRequest.

PGPRequest may contain one of 3 possible requests: delete, disable and add.
The add request is implemented in pgpkeys, but never used for unknown reason
(may be a simple bug in parsing errors from ldap_add).

Format of PGPRequest (after PGPDecode call):

Request: delete[disable|add] Location: active[pending]
[long keyid]
[long keyid]
[long keyid]
[long keyid]
...

On PGPRequest, server must find requested key(s) and do one of : delete this
key from server, set pgpdisabled to 1 in this key, or add this key to server
(unimplemented in pgpkey but possible)
If some error is occured, server must respond with error string like " Error
in : [long keyid] [long eyid] ..... It;s required, but not used in pgpkeys.


PGPKey entry must be simply placed in directory as new key.


Implementation.


pgp backend implemented over SLAPD LDBM backend. It own a 5 standard backend
functions:
pgp_back_add
pgp_back_search
pgp_back_init
pgp_back_config
pgp_back_close

All other functions  from ldbm backend.
pgp_back_search is slightly modified ldbm_back_search (to return parent of
founded entry, if pgpkey attribute us requested)

Example of databse definition in slapd.conf (non standard definitions only)
# new database type - pgpdb
database    pgpdb

#indexed attributes
index pgpuserid eq,sub
index pgpkeyid eq
index pgpkeysize eq
index pgpcertid eq
index pgpkeytype eq
index pgpkeycreatetime eq
index pgpkeyexpiretime eq
index pgpdisabled eq
index pgprevoked eq

#basekeyspacedn and pasependingdn definition (can be obtained from server)
basedn "OU=ACTIVE,O=PGP KEYSPACE,C=RU"
pendingdn "OU=PENDING,O=PGP KEYSPACE,C=RU"

# "long" keyid of Administrator's  - user's that can issue Delete/Disable
requests to server
admincertid A5FF344FA9AF6B3F
admincertid 0000000000000002
admincertid 0000000000000003

# last line in database definition - invokes initialization of all pgp
backend's internals
# [temporary]
lastline

Most important modifications in core modules (slapd)
1. Added new pointer in Backend structure : be_pgp that holds all internals
of pgp backend
2. Added a piece of code to send_ldap_result (file result.c) procedure, to
suppress return of LDAP operation result to client in some cases (see below)


How it works:

pgp_back_init/config - reads and initialize all vars during server init.

When pgp_back_add receive a request it:
    checks request for dn == "pgpcertid=virtual"
     => not equal - pass this request to ldbm_back_add
     => equal - check for presence of attributes PGPKey or PGPRequest
        => not found - exit
        => found - call corresponding Process for entry

    PGPKey Processing:
    import incoming PGPKey to KeySet
    Create PGPCertificate entry
    Create PGPUserID entry
    Check for presence PGPCertificate in database
        => not present - add PGPCertificate and PGPUserID to database using
ldbm_back_add
        => present - Decide what to do with new key
            => key already exist on server - return LDAP_ALREADY_EXISTS to
client
            => replace old key with new one
                    Create LDAPMod for changed attributes
                    Call ldbm_back_modify with this MOD to PGPUserIDEntry
                    Create LDAPMod for PGPKey
                    Call ldbm_back_modify PGPCertificate
                    Check is pgpuserid changed ?
                        => no - return LDAP_SUCCESS to client
                        => yes - call ldap_back_modrdn PGPUserID entry
                    return result to client

    PGPRequest processing:
    Decode incoming PGPRequest using Administration's keys and check for
validity
        => Not valid - return error to client
        => Valid
            Perform operation on each key, specified in PGPRequest
            = delete - delete this key (PGPCertificate and PGPUserID) from
server with ldbm_back_delete
            = disable - modify "pgpdisabled" attribute in specified key
using ldbm_back_modify
    return result to client


To suppres return errors to client during calls of ldbm_back_*, pgp backend
uses a op_private field in Operation structure. If it is not NULL,
send_ldap_result assigns error values ( error_num, matched, error) to
corresponding field of structure pointed by op_private.


pgp backend was first implemented under WindowsNT with Eudora QSLAPD server,
and now it ported to FreeBSD 3.0 with OpenLDAP 1.1.1.

PGP SDK is available for free from PGP corporate homepage
http://www.pgpi.com and is free for all non-commercial projects.

P.S. Windows NT version of pgp backend implemented as SLAPD server you can
get on http://rednest.rosinter.ru/apps/pgpldapnt.zip