]> the.earth.li Git - onak.git/blob - keydb/keydb_keyring.c
Provide key_fetch routine that will not search subkey fingerprints
[onak.git] / keydb / 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_idx(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 = (char *) 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 /*
101  * We only index the primary fingerprint of the key, so will only return one
102  * key at most from this function.
103  */
104 static int keyring_fetch_key(struct onak_dbctx *dbctx,
105                         struct openpgp_fingerprint *fingerprint,
106                         struct openpgp_publickey **publickey,
107                         bool intrans)
108 {
109         struct onak_keyring_dbctx *privctx =
110                 (struct onak_keyring_dbctx *) dbctx->priv;
111         int i;
112
113         for (i = 0; i < privctx->count; i++) {
114                 if (fingerprint_cmp(fingerprint, &privctx->keys[i].fp) == 0)
115                         break;
116         }
117
118         if (i < privctx->count) {
119                 return keyring_fetch_key_idx(privctx, i, publickey);
120         }
121
122         return 0;
123 }
124
125 /**
126  *      fetch_key_id - Given a keyid fetch the key from storage.
127  *      @keyid: The keyid to fetch.
128  *      @publickey: A pointer to a structure to return the key in.
129  *      @intrans: If we're already in a transaction.
130  */
131 static int keyring_fetch_key_id(struct onak_dbctx *dbctx,
132                 uint64_t keyid,
133                 struct openpgp_publickey **publickey,
134                 bool intrans)
135 {
136         struct onak_keyring_dbctx *privctx =
137                 (struct onak_keyring_dbctx *) dbctx->priv;
138         int count, i;
139
140         count = 0;
141         for (i = 0; i < privctx->count; i++) {
142                 if (fingerprint2keyid(&privctx->keys[i].fp) == keyid) {
143                         if (keyring_fetch_key_idx(privctx, i, publickey))
144                                 count++;
145                 }
146         }
147
148         return count;
149 }
150
151 /**
152  *      store_key - Takes a key and stores it.
153  *      @publickey: A pointer to the public key to store.
154  *      @intrans: If we're already in a transaction.
155  *      @update: If true the key exists and should be updated.
156  *
157  *      We don't support storing keys into a keyring file.
158  */
159 static int keyring_store_key(struct onak_dbctx *dbctx,
160                 struct openpgp_publickey *publickey, bool intrans,
161                 bool update)
162 {
163         return 0;
164 }
165
166 /**
167  *      delete_key - Given a keyid delete the key from storage.
168  *      @fp: The fingerprint of the key to delete.
169  *      @intrans: If we're already in a transaction.
170  *
171  *      We don't support removing keys from a keyring file.
172  */
173 static int keyring_delete_key(struct onak_dbctx *dbctx,
174                 struct openpgp_fingerprint *fp, bool intrans)
175 {
176         return 1;
177 }
178
179 /**
180  *      fetch_key_text - Trys to find the keys that contain the supplied text.
181  *      @search: The text to search for.
182  *      @publickey: A pointer to a structure to return the key in.
183  *
184  *      This function searches for the supplied text and returns the keys that
185  *      contain it.
186  *
187  *      TODO: Write for flat file access. Some sort of grep?
188  */
189 static int keyring_fetch_key_text(struct onak_dbctx *dbctx,
190                 const char *search,
191                 struct openpgp_publickey **publickey)
192 {
193         return 0;
194 }
195
196 /**
197  *      iterate_keys - call a function once for each key in the db.
198  *      @iterfunc: The function to call.
199  *      @ctx: A context pointer
200  *
201  *      Calls iterfunc once for each key in the database. ctx is passed
202  *      unaltered to iterfunc. This function is intended to aid database dumps
203  *      and statistic calculations.
204  *
205  *      Returns the number of keys we iterated over.
206  */
207 static int keyring_iterate_keys(struct onak_dbctx *dbctx,
208                 void (*iterfunc)(void *ctx, struct openpgp_publickey *key),
209                 void *ctx)
210 {
211         struct onak_keyring_dbctx *privctx =
212                 (struct onak_keyring_dbctx *) dbctx->priv;
213         struct openpgp_publickey  *key = NULL;
214         int count, i;
215
216         count = 0;
217         for (i = 0; i < privctx->count; i++) {
218                 if (keyring_fetch_key_idx(privctx, i, &key)) {
219                         iterfunc(ctx, key);
220                         free_publickey(key);
221                         key = NULL;
222                 }
223         }
224
225         return count;
226 }
227
228 static int keyring_update_keys(struct onak_dbctx *dbctx,
229                 struct openpgp_publickey **keys,
230                 struct keyarray *blacklist,
231                 bool updateonly,
232                 bool sendsync)
233 {
234         return 0;
235 }
236
237 /*
238  * Include the basic keydb routines.
239  */
240 #define NEED_KEYID2UID 1
241 #define NEED_GETKEYSIGS 1
242 #include "keydb.c"
243
244 static int keyring_parse_keys(struct onak_keyring_dbctx *privctx)
245 {
246         size_t len, pos, start, totlen;
247         struct openpgp_publickey *key;
248         uint8_t tag;
249
250         if (privctx == NULL) {
251                 return 0;
252         }
253
254         if (privctx->file == NULL) {
255                 return 0;
256         }
257
258         /*
259          * Walk the keyring file, noting the start of each public key and the
260          * total length of packets associated with it.
261          */
262         len = pos = start = totlen = 0;
263         while (((privctx->length - pos) > 5) && (privctx->file[pos] & 0x80)) {
264                 if (privctx->file[pos] & 0x40) {
265                         tag = privctx->file[pos] & 0x3F;
266                         len = privctx->file[pos + 1];
267                         if (len > 191 && len < 224) {
268                                 len -= 192;
269                                 len <<= 8;
270                                 len += privctx->file[pos + 2];
271                                 len += 192;
272                                 len += 1; /* Header */
273                         } else if (len > 223 && len < 255) {
274                                 // Unsupported
275                         } else if (len == 255) {
276                                 len = privctx->file[pos + 2];
277                                 len <<= 8;
278                                 len += privctx->file[pos + 3];
279                                 len <<= 8;
280                                 len += privctx->file[pos + 4];
281                                 len <<= 8;
282                                 len += privctx->file[pos + 5];
283                                 len += 4; /* Header */
284                         }
285                         len += 2; /* Header */
286                 } else {
287                         tag = (privctx->file[pos] & 0x3C) >> 2;
288                         switch (privctx->file[pos] & 3) {
289                         case 0:
290                                 len = privctx->file[pos + 1];
291                                 len += 2; /* Header */
292                                 break;
293                         case 1:
294                                 len = privctx->file[pos + 1];
295                                 len <<= 8;
296                                 len += privctx->file[pos + 2];
297                                 len += 3; /* Header */
298                                 break;
299                         case 2:
300                                 len = privctx->file[pos + 1];
301                                 len <<= 8;
302                                 len += privctx->file[pos + 2];
303                                 len <<= 8;
304                                 len += privctx->file[pos + 3];
305                                 len <<= 8;
306                                 len += privctx->file[pos + 4];
307                                 len += 5; /* Header */
308                                 break;
309                         case 3:
310                                 // Unsupported
311                                 break;
312                         }
313                 }
314                 if (tag == OPENPGP_PACKET_PUBLICKEY) {
315                         if (totlen > 0) {
316                                 /* Expand the array of keys if necessary */
317                                 if (privctx->count == privctx->space) {
318                                         privctx->space *= 2;
319                                         privctx->keys = realloc(privctx->keys,
320                                                 privctx->space *
321                                                 sizeof(*privctx->keys));
322                                 }
323
324                                 /* TODO: Sort by fingerprint? */
325                                 privctx->keys[privctx->count].start =
326                                         &privctx->file[start];
327                                 privctx->keys[privctx->count].len = totlen;
328                                 privctx->count++;
329
330                                 /*
331                                  * We need to fetch the key to calculate the
332                                  * fingerprint.
333                                  */
334                                 keyring_fetch_key_idx(privctx,
335                                                 privctx->count - 1,
336                                                 &key);
337                                 get_fingerprint(key->publickey,
338                                         &privctx->keys[privctx->count - 1].fp);
339                                 free_publickey(key);
340                                 key = NULL;
341                         }
342                         start = pos;
343                         totlen = 0;
344                 }
345                 totlen += len;
346                 pos += len;
347         }
348
349         return privctx->count;
350 }
351
352 /**
353  *      cleanupdb - De-initialize the key database.
354  *
355  *      This is just a no-op for flat file access.
356  */
357 static void keyring_cleanupdb(struct onak_dbctx *dbctx)
358 {
359         struct onak_keyring_dbctx *privctx =
360                 (struct onak_keyring_dbctx *) dbctx->priv;
361
362         if (dbctx->priv != NULL) {
363                 if (privctx->file != NULL) {
364                         munmap(privctx->file, privctx->length);
365                 }
366                 free(privctx->keys);
367                 free(dbctx->priv);
368                 dbctx->priv = NULL;
369         }
370
371         if (dbctx != NULL) {
372                 free(dbctx);
373         }
374 };
375
376 /**
377  *      initdb - Initialize the key database.
378  *
379  *      This is just a no-op for flat file access.
380  */
381 struct onak_dbctx *keydb_keyring_init(struct onak_db_config *dbcfg,
382                 bool readonly)
383 {
384         struct onak_keyring_dbctx *privctx;
385         struct onak_dbctx *dbctx;
386         struct stat sb;
387         int fd;
388
389         dbctx = malloc(sizeof(struct onak_dbctx));
390         if (dbctx == NULL) {
391                 return NULL;
392         }
393         dbctx->config = dbcfg;
394         dbctx->priv = privctx = calloc(1, sizeof(*privctx));
395         if (privctx == NULL) {
396                 free(dbctx);
397                 return NULL;
398         }
399         privctx->space = 16;
400         privctx->keys = calloc(privctx->space, sizeof(*privctx->keys));
401
402         fd = open(dbcfg->location, O_RDONLY);
403         if (fd < 0) {
404                 logthing(LOGTHING_CRITICAL,
405                                 "Couldn't open keyring file %s: %s (%d)",
406                                 dbcfg->location,
407                                 strerror(errno),
408                                 errno);
409                 keyring_cleanupdb(dbctx);
410                 return NULL;
411         }
412         if (fstat(fd, &sb) < 0) {
413                 logthing(LOGTHING_CRITICAL,
414                                 "Couldn't stat keyring file %s: %s (%d)",
415                                 dbcfg->location,
416                                 strerror(errno),
417                                 errno);
418                 close(fd);
419                 keyring_cleanupdb(dbctx);
420                 return NULL;
421         }
422         privctx->file = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
423         if (privctx->file == MAP_FAILED) {
424                 logthing(LOGTHING_CRITICAL,
425                                 "Couldn't mmap keyring file %s: %s (%d)",
426                                 dbcfg->location,
427                                 strerror(errno),
428                                 errno);
429                 privctx->file = NULL;
430                 close(fd);
431                 keyring_cleanupdb(dbctx);
432                 return NULL;
433         }
434         privctx->length = sb.st_size;
435         close(fd);
436
437         if (keyring_parse_keys(privctx) == 0) {
438                 logthing(LOGTHING_CRITICAL,
439                                 "Failed to load any keys from keyring file %s",
440                                 dbcfg->location);
441                 keyring_cleanupdb(dbctx);
442                 return NULL;
443         }
444
445         dbctx->cleanupdb                = keyring_cleanupdb;
446         dbctx->starttrans               = keyring_starttrans;
447         dbctx->endtrans                 = keyring_endtrans;
448         dbctx->fetch_key                = keyring_fetch_key;
449         /* We don't index by subkey fingerprint, so fallback to fetch_key */
450         dbctx->fetch_key_fp             = keyring_fetch_key;
451         dbctx->fetch_key_id             = keyring_fetch_key_id;
452         dbctx->fetch_key_text           = keyring_fetch_key_text;
453         dbctx->store_key                = keyring_store_key;
454         dbctx->update_keys              = keyring_update_keys;
455         dbctx->delete_key               = keyring_delete_key;
456         dbctx->getkeysigs               = generic_getkeysigs;
457         dbctx->cached_getkeysigs        = generic_cached_getkeysigs;
458         dbctx->keyid2uid                = generic_keyid2uid;
459         dbctx->iterate_keys             = keyring_iterate_keys;
460
461         return dbctx;
462 }