]> the.earth.li Git - onak.git/blob - keydb_keyring.c
Remove unused worddb_cmp() from DB4 backend
[onak.git] / keydb_keyring.c
1 /*
2  * keydb_keyring.c - Routines to fetch keys from a PGP keyring file.
3  *
4  * Copyright 2019 Jonathan McDowell <noodles@earth.li>
5  *
6  * This program is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program.  If not, see <https://www.gnu.org/licenses/>.
17  */
18
19 #include <sys/mman.h>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22 #include <sys/uio.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <inttypes.h>
26 #include <stdbool.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31
32 #include "charfuncs.h"
33 #include "keyarray.h"
34 #include "keydb.h"
35 #include "keyid.h"
36 #include "keystructs.h"
37 #include "log.h"
38 #include "mem.h"
39 #include "onak.h"
40 #include "onak-conf.h"
41 #include "parsekey.h"
42
43 struct onak_keyring_dbctx {
44         uint8_t *file;
45         size_t   length;
46         unsigned int space;
47         unsigned int count;
48         struct {
49                 struct openpgp_fingerprint fp;
50                 uint8_t *start;
51                 size_t len;
52         } *keys;
53 };
54
55 /**
56  *      starttrans - Start a transaction.
57  *
58  *      This is just a no-op for keyring file access.
59  */
60 static bool keyring_starttrans(struct onak_dbctx *dbctx)
61 {
62         return true;
63 }
64
65 /**
66  *      endtrans - End a transaction.
67  *
68  *      This is just a no-op for keyring file access.
69  */
70 static void keyring_endtrans(struct onak_dbctx *dbctx)
71 {
72         return;
73 }
74
75 /**
76  * keyring_fetch_key - fetch a key given its index
77  */
78 static int keyring_fetch_key(struct onak_keyring_dbctx *privctx,
79                 unsigned int index,
80                 struct openpgp_publickey **publickey)
81 {
82         struct openpgp_packet_list *packets = NULL;
83         struct buffer_ctx buf;
84
85         if (index > privctx->count)
86                 return 0;
87
88         buf.buffer = privctx->keys[index].start;
89         buf.size = privctx->keys[index].len;
90         buf.offset = 0;
91
92         read_openpgp_stream(buffer_fetchchar, &buf, &packets, 0);
93         parse_keys(packets, publickey);
94         free_packet_list(packets);
95         packets = NULL;
96
97         return 1;
98 }
99
100 static int keyring_fetch_key_fp(struct onak_dbctx *dbctx,
101                         struct openpgp_fingerprint *fingerprint,
102                         struct openpgp_publickey **publickey,
103                         bool intrans)
104 {
105         struct onak_keyring_dbctx *privctx =
106                 (struct onak_keyring_dbctx *) dbctx->priv;
107         int i;
108
109         for (i = 0; i < privctx->count; i++) {
110                 if (fingerprint_cmp(fingerprint, &privctx->keys[i].fp) == 0)
111                         break;
112         }
113
114         if (i < privctx->count) {
115                 return keyring_fetch_key(privctx, i, publickey);
116         }
117
118         return 0;
119 }
120
121 /**
122  *      fetch_key_id - Given a keyid fetch the key from storage.
123  *      @keyid: The keyid to fetch.
124  *      @publickey: A pointer to a structure to return the key in.
125  *      @intrans: If we're already in a transaction.
126  */
127 static int keyring_fetch_key_id(struct onak_dbctx *dbctx,
128                 uint64_t keyid,
129                 struct openpgp_publickey **publickey,
130                 bool intrans)
131 {
132         struct onak_keyring_dbctx *privctx =
133                 (struct onak_keyring_dbctx *) dbctx->priv;
134         int count, i;
135
136         count = 0;
137         for (i = 0; i < privctx->count; i++) {
138                 if (fingerprint2keyid(&privctx->keys[i].fp) == keyid) {
139                         if (keyring_fetch_key(privctx, i, publickey))
140                                 count++;
141                 }
142         }
143
144         return count;
145 }
146
147 /**
148  *      store_key - Takes a key and stores it.
149  *      @publickey: A pointer to the public key to store.
150  *      @intrans: If we're already in a transaction.
151  *      @update: If true the key exists and should be updated.
152  *
153  *      We don't support storing keys into a keyring file.
154  */
155 static int keyring_store_key(struct onak_dbctx *dbctx,
156                 struct openpgp_publickey *publickey, bool intrans,
157                 bool update)
158 {
159         return 0;
160 }
161
162 /**
163  *      delete_key - Given a keyid delete the key from storage.
164  *      @fp: The fingerprint of the key to delete.
165  *      @intrans: If we're already in a transaction.
166  *
167  *      We don't support removing keys from a keyring file.
168  */
169 static int keyring_delete_key(struct onak_dbctx *dbctx,
170                 struct openpgp_fingerprint *fp, bool intrans)
171 {
172         return 1;
173 }
174
175 /**
176  *      fetch_key_text - Trys to find the keys that contain the supplied text.
177  *      @search: The text to search for.
178  *      @publickey: A pointer to a structure to return the key in.
179  *
180  *      This function searches for the supplied text and returns the keys that
181  *      contain it.
182  *
183  *      TODO: Write for flat file access. Some sort of grep?
184  */
185 static int keyring_fetch_key_text(struct onak_dbctx *dbctx,
186                 const char *search,
187                 struct openpgp_publickey **publickey)
188 {
189         return 0;
190 }
191
192 /**
193  *      iterate_keys - call a function once for each key in the db.
194  *      @iterfunc: The function to call.
195  *      @ctx: A context pointer
196  *
197  *      Calls iterfunc once for each key in the database. ctx is passed
198  *      unaltered to iterfunc. This function is intended to aid database dumps
199  *      and statistic calculations.
200  *
201  *      Returns the number of keys we iterated over.
202  */
203 static int keyring_iterate_keys(struct onak_dbctx *dbctx,
204                 void (*iterfunc)(void *ctx, struct openpgp_publickey *key),
205                 void *ctx)
206 {
207         struct onak_keyring_dbctx *privctx =
208                 (struct onak_keyring_dbctx *) dbctx->priv;
209         struct openpgp_publickey  *key = NULL;
210         int count, i;
211
212         count = 0;
213         for (i = 0; i < privctx->count; i++) {
214                 if (keyring_fetch_key(privctx, i, &key)) {
215                         iterfunc(ctx, key);
216                         free_publickey(key);
217                         key = NULL;
218                 }
219         }
220
221         return count;
222 }
223
224 static int keyring_update_keys(struct onak_dbctx *dbctx,
225                 struct openpgp_publickey **keys, bool sendsync)
226 {
227         return 0;
228 }
229
230 /*
231  * Include the basic keydb routines.
232  */
233 #define NEED_KEYID2UID 1
234 #define NEED_GETKEYSIGS 1
235 #define NEED_GET_FP 1
236 #include "keydb.c"
237
238 static int keyring_parse_keys(struct onak_keyring_dbctx *privctx)
239 {
240         size_t len, pos, start, totlen;
241         struct openpgp_publickey *key;
242         uint8_t tag;
243
244         if (privctx == NULL) {
245                 return 0;
246         }
247
248         if (privctx->file == NULL) {
249                 return 0;
250         }
251
252         /*
253          * Walk the keyring file, noting the start of each public key and the
254          * total length of packets associated with it.
255          */
256         len = pos = start = totlen = 0;
257         while (((privctx->length - pos) > 5) && (privctx->file[pos] & 0x80)) {
258                 if (privctx->file[pos] & 0x40) {
259                         tag = privctx->file[pos] & 0x3F;
260                         len = privctx->file[pos + 1];
261                         if (len > 191 && len < 224) {
262                                 len -= 192;
263                                 len <<= 8;
264                                 len += privctx->file[pos + 2];
265                                 len += 192;
266                                 len += 1; /* Header */
267                         } else if (len > 223 & len < 255) {
268                                 // Unsupported
269                         } else if (len == 255) {
270                                 len = privctx->file[pos + 2];
271                                 len <<= 8;
272                                 len += privctx->file[pos + 3];
273                                 len <<= 8;
274                                 len += privctx->file[pos + 4];
275                                 len <<= 8;
276                                 len += privctx->file[pos + 5];
277                                 len += 4; /* Header */
278                         }
279                         len += 2; /* Header */
280                 } else {
281                         tag = (privctx->file[pos] & 0x3C) >> 2;
282                         switch (privctx->file[pos] & 3) {
283                         case 0:
284                                 len = privctx->file[pos + 1];
285                                 len += 2; /* Header */
286                                 break;
287                         case 1:
288                                 len = privctx->file[pos + 1];
289                                 len <<= 8;
290                                 len += privctx->file[pos + 2];
291                                 len += 3; /* Header */
292                                 break;
293                         case 2:
294                                 len = privctx->file[pos + 1];
295                                 len <<= 8;
296                                 len += privctx->file[pos + 2];
297                                 len <<= 8;
298                                 len += privctx->file[pos + 3];
299                                 len <<= 8;
300                                 len += privctx->file[pos + 4];
301                                 len += 5; /* Header */
302                                 break;
303                         case 3:
304                                 // Unsupported
305                                 break;
306                         }
307                 }
308                 if (tag == OPENPGP_PACKET_PUBLICKEY) {
309                         if (totlen > 0) {
310                                 /* Expand the array of keys if necessary */
311                                 if (privctx->count == privctx->space) {
312                                         privctx->space *= 2;
313                                         privctx->keys = realloc(privctx->keys,
314                                                 privctx->space *
315                                                 sizeof(*privctx->keys));
316                                 }
317
318                                 /* TODO: Sort by fingerprint? */
319                                 privctx->keys[privctx->count].start =
320                                         &privctx->file[start];
321                                 privctx->keys[privctx->count].len = totlen;
322                                 privctx->count++;
323
324                                 /*
325                                  * We need to fetch the key to calculate the
326                                  * fingerprint.
327                                  */
328                                 keyring_fetch_key(privctx, privctx->count - 1,
329                                                 &key);
330                                 get_fingerprint(key->publickey,
331                                         &privctx->keys[privctx->count - 1].fp);
332                                 free_publickey(key);
333                                 key = NULL;
334                         }
335                         start = pos;
336                         totlen = 0;
337                 }
338                 totlen += len;
339                 pos += len;
340         }
341
342         return privctx->count;
343 }
344
345 /**
346  *      cleanupdb - De-initialize the key database.
347  *
348  *      This is just a no-op for flat file access.
349  */
350 static void keyring_cleanupdb(struct onak_dbctx *dbctx)
351 {
352         struct onak_keyring_dbctx *privctx =
353                 (struct onak_keyring_dbctx *) dbctx->priv;
354
355         if (dbctx->priv != NULL) {
356                 if (privctx->file != NULL) {
357                         munmap(privctx->file, privctx->length);
358                 }
359                 free(privctx->keys);
360                 free(dbctx->priv);
361                 dbctx->priv = NULL;
362         }
363
364         if (dbctx != NULL) {
365                 free(dbctx);
366         }
367 };
368
369 /**
370  *      initdb - Initialize the key database.
371  *
372  *      This is just a no-op for flat file access.
373  */
374 struct onak_dbctx *keydb_keyring_init(struct onak_db_config *dbcfg,
375                 bool readonly)
376 {
377         struct onak_keyring_dbctx *privctx;
378         struct onak_dbctx *dbctx;
379         struct stat sb;
380         int fd;
381
382         dbctx = malloc(sizeof(struct onak_dbctx));
383         if (dbctx == NULL) {
384                 return NULL;
385         }
386         dbctx->config = dbcfg;
387         dbctx->priv = privctx = calloc(1, sizeof(*privctx));
388         if (privctx == NULL) {
389                 free(dbctx);
390                 return NULL;
391         }
392         privctx->space = 16;
393         privctx->keys = calloc(privctx->space, sizeof(*privctx->keys));
394
395         fd = open(dbcfg->location, O_RDONLY);
396         if (fd < 0) {
397                 logthing(LOGTHING_CRITICAL,
398                                 "Couldn't open keyring file %s: %s (%d)",
399                                 dbcfg->location,
400                                 strerror(errno),
401                                 errno);
402                 keyring_cleanupdb(dbctx);
403                 return NULL;
404         }
405         if (fstat(fd, &sb) < 0) {
406                 logthing(LOGTHING_CRITICAL,
407                                 "Couldn't stat keyring file %s: %s (%d)",
408                                 dbcfg->location,
409                                 strerror(errno),
410                                 errno);
411                 close(fd);
412                 keyring_cleanupdb(dbctx);
413                 return NULL;
414         }
415         privctx->file = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
416         if (privctx->file == MAP_FAILED) {
417                 logthing(LOGTHING_CRITICAL,
418                                 "Couldn't mmap keyring file %s: %s (%d)",
419                                 dbcfg->location,
420                                 strerror(errno),
421                                 errno);
422                 privctx->file = NULL;
423                 close(fd);
424                 keyring_cleanupdb(dbctx);
425                 return NULL;
426         }
427         privctx->length = sb.st_size;
428         close(fd);
429
430         if (keyring_parse_keys(privctx) == 0) {
431                 logthing(LOGTHING_CRITICAL,
432                                 "Failed to load any keys from keyring file %s",
433                                 dbcfg->location);
434                 keyring_cleanupdb(dbctx);
435                 return NULL;
436         }
437
438         dbctx->cleanupdb                = keyring_cleanupdb;
439         dbctx->starttrans               = keyring_starttrans;
440         dbctx->endtrans                 = keyring_endtrans;
441         dbctx->fetch_key_id             = keyring_fetch_key_id;
442         dbctx->fetch_key_fp             = keyring_fetch_key_fp;
443         dbctx->fetch_key_text           = keyring_fetch_key_text;
444         dbctx->store_key                = keyring_store_key;
445         dbctx->update_keys              = keyring_update_keys;
446         dbctx->delete_key               = keyring_delete_key;
447         dbctx->getkeysigs               = generic_getkeysigs;
448         dbctx->cached_getkeysigs        = generic_cached_getkeysigs;
449         dbctx->keyid2uid                = generic_keyid2uid;
450         dbctx->iterate_keys             = keyring_iterate_keys;
451
452         return dbctx;
453 }