ac1266147c208b0e8b73d3d98f7f85ba194812af
[gnupg.git] / g10 / tdbio.c
1 /* tdbio.c
2  *      Copyright (C) 1998 Free Software Foundation, Inc.
3  *
4  * This file is part of GNUPG.
5  *
6  * GNUPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GNUPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <assert.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31
32 #include "errors.h"
33 #include "iobuf.h"
34 #include "memory.h"
35 #include "util.h"
36 #include "options.h"
37 #include "main.h"
38 #include "i18n.h"
39 #include "trustdb.h"
40 #include "tdbio.h"
41
42
43
44 static char *db_name;
45 static int  db_fd = -1;
46
47
48
49 static void create_db( const char *fname );
50 static void open_db(void);
51
52 /**************************************************
53  ************** read and write helpers ************
54  **************************************************/
55
56 static void
57 fwrite_8(FILE *fp, byte a)
58 {
59     if( putc( a & 0xff, fp ) == EOF )
60         log_fatal("error writing byte to trustdb: %s\n", strerror(errno) );
61 }
62
63
64 static void
65 fwrite_32( FILE*fp, ulong a)
66 {
67     putc( (a>>24) & 0xff, fp );
68     putc( (a>>16) & 0xff, fp );
69     putc( (a>> 8) & 0xff, fp );
70     if( putc( a & 0xff, fp ) == EOF )
71         log_fatal("error writing ulong to trustdb: %s\n", strerror(errno) );
72 }
73
74 static void
75 fwrite_zeros( FILE *fp, size_t n)
76 {
77     while( n-- )
78         if( putc( 0, fp ) == EOF )
79             log_fatal("error writing zeros to trustdb: %s\n", strerror(errno) );
80 }
81
82
83
84
85 /**************************************************
86  ************** read and write stuff **************
87  **************************************************/
88
89 int
90 tdbio_set_dbname( const char *new_dbname, int create )
91 {
92     char *fname;
93
94     fname = new_dbname? m_strdup( new_dbname )
95                       : make_filename(opt.homedir, "trustdb.gpg", NULL );
96
97     if( access( fname, R_OK ) ) {
98         if( errno != ENOENT ) {
99             log_error_f( fname, _("can't access: %s\n"), strerror(errno) );
100             m_free(fname);
101             return G10ERR_TRUSTDB;
102         }
103         if( create ) {
104             char *p = strrchr( fname, '/' );
105             assert(p);
106             *p = 0;
107             if( access( fname, F_OK ) ) {
108                 if( strlen(fname) >= 7
109                     && !strcmp(fname+strlen(fname)-7, "/.gnupg" ) ) {
110                   #if __MINGW32__
111                     if( mkdir( fname ) )
112                   #else
113                     if( mkdir( fname, S_IRUSR|S_IWUSR|S_IXUSR ) )
114                   #endif
115                         log_fatal_f( fname, _("can't create directory: %s\n"),
116                                                             strerror(errno) );
117                 }
118                 else
119                     log_fatal_f(fname, _("directory does not exist!\n") );
120             }
121             *p = '/';
122             create_db( fname );
123         }
124     }
125     m_free(db_name);
126     db_name = fname;
127     return 0;
128 }
129
130
131 const char *
132 tdbio_get_dbname()
133 {
134     return db_name;
135 }
136
137
138
139 /****************
140  * Create a new trustdb
141  */
142 static void
143 create_db( const char *fname )
144 {
145     FILE *fp;
146
147     fp =fopen( fname, "w" );
148     if( !fp )
149         log_fatal_f( fname, _("can't create %s: %s\n"), strerror(errno) );
150     fwrite_8( fp, 1 );  /* record type */
151     fwrite_8( fp, 'g' );
152     fwrite_8( fp, 'p' );
153     fwrite_8( fp, 'g' );
154     fwrite_8( fp, 2 );  /* version */
155     fwrite_zeros( fp, 3 ); /* reserved */
156     fwrite_32( fp, 0 ); /* not locked */
157     fwrite_32( fp, make_timestamp() ); /* created */
158     fwrite_32( fp, 0 ); /* not yet modified */
159     fwrite_32( fp, 0 ); /* not yet validated*/
160     fwrite_32( fp, 0 ); /* reserved */
161     fwrite_8( fp, 3 );  /* marginals needed */
162     fwrite_8( fp, 1 );  /* completes needed */
163     fwrite_8( fp, 4 );  /* max_cet_depth */
164     fwrite_zeros( fp, 9 ); /* filler */
165     fclose(fp);
166 }
167
168
169
170 static void
171 open_db()
172 {
173     TRUSTREC rec;
174     assert( db_fd == -1 );
175
176     db_fd = open( db_name, O_RDWR );
177     if( db_fd == -1 )
178         log_fatal_f( db_name, _("can't open: %s\n"), strerror(errno) );
179     if( tdbio_read_record( 0, &rec, RECTYPE_VER ) )
180         log_fatal_f( db_name, _("invalid trust-db\n") );
181     /* fixme: check ->locked and other stuff */
182 }
183
184
185 void
186 tdbio_dump_record( TRUSTREC *rec, FILE *fp  )
187 {
188     int i, any;
189     ulong rnum = rec->recnum;
190
191     fprintf(fp, "rec %5lu, ", rnum );
192
193     switch( rec->rectype ) {
194       case 0: fprintf(fp, "free\n");
195         break;
196       case RECTYPE_VER: fprintf(fp, "version\n");
197         break;
198       case RECTYPE_DIR:
199         fprintf(fp, "dir %lu, keys=%lu, uids=%lu, cach=%lu, ot=%02x",
200                     rec->r.dir.lid,
201                     rec->r.dir.keylist,
202                     rec->r.dir.uidlist,
203                     rec->r.dir.cacherec,
204                     rec->r.dir.ownertrust );
205         if( rec->r.dir.dirflags & DIRF_ERROR )
206             fputs(", error", fp );
207         if( rec->r.dir.dirflags & DIRF_CHECKED )
208             fputs(", checked", fp );
209         if( rec->r.dir.dirflags & DIRF_REVOKED )
210             fputs(", revoked", fp );
211         if( rec->r.dir.dirflags & DIRF_MISKEY )
212             fputs(", miskey", fp );
213         putc('\n', fp);
214         break;
215       case RECTYPE_KEY:
216         fprintf(fp, "key %lu, next=%lu, algo=%d, flen=%d",
217                    rec->r.key.lid,
218                    rec->r.key.next,
219                    rec->r.key.pubkey_algo,
220                    rec->r.key.fingerprint_len );
221         if( rec->r.key.keyflags & KEYF_REVOKED )
222             fputs(", revoked", fp );
223         putc('\n', fp);
224         break;
225       case RECTYPE_UID:
226         fprintf(fp, "uid %lu, next=%lu, pref=%lu, sig=%lu, hash=%02X%02X",
227                     rec->r.uid.lid,
228                     rec->r.uid.next,
229                     rec->r.uid.prefrec,
230                     rec->r.uid.siglist,
231                     rec->r.uid.namehash[18], rec->r.uid.namehash[19]);
232         if( rec->r.uid.uidflags & UIDF_REVOKED )
233             fputs(", revoked", fp );
234         putc('\n', fp);
235         break;
236       case RECTYPE_PREF:
237         fprintf(fp, "pref %lu, next=%lu\n",
238                     rec->r.uid.lid,
239                     rec->r.uid.next);
240         break;
241       case RECTYPE_SIG:
242         fprintf(fp, "sig %lu, next=%lu\n",
243                          rec->r.sig.lid, rec->r.sig.next );
244         for(i=any=0; i < SIGS_PER_RECORD; i++ ) {
245             if( rec->r.sig.sig[i].lid ) {
246                 if( !any ) {
247                     putc('\t', fp);
248                     any++;
249                 }
250                 fprintf(fp, "  %lu:%02x", rec->r.sig.sig[i].lid,
251                                           rec->r.sig.sig[i].flag );
252             }
253         }
254         if( any )
255             putc('\n', fp);
256         break;
257       case RECTYPE_CACH:
258         fprintf(fp, "cach\n");
259         break;
260       case RECTYPE_HTBL:
261         fprintf(fp, "htbl\n");
262         break;
263       case RECTYPE_HLST:
264         fprintf(fp, "hlst\n");
265         break;
266       default:
267         fprintf(fp, "unknown type %d\n", rec->rectype );
268         break;
269     }
270 }
271
272 /****************
273  * read the record with number recnum
274  * returns: -1 on error, 0 on success
275  */
276 int
277 tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected )
278 {
279     byte buf[TRUST_RECORD_LEN], *p;
280     int rc = 0;
281     int n, i;
282
283     if( db_fd == -1 )
284         open_db();
285     if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) {
286         log_error(_("trustdb: lseek failed: %s\n"), strerror(errno) );
287         return G10ERR_READ_FILE;
288     }
289     n = read( db_fd, buf, TRUST_RECORD_LEN);
290     if( !n ) {
291         return -1; /* eof */
292     }
293     else if( n != TRUST_RECORD_LEN ) {
294         log_error(_("trustdb: read failed (n=%d): %s\n"), n, strerror(errno) );
295         return G10ERR_READ_FILE;
296     }
297     rec->recnum = recnum;
298     p = buf;
299     rec->rectype = *p++;
300     if( expected && rec->rectype != expected ) {
301         log_error("%lu: read expected rec type %d, got %d\n",
302                     recnum, expected, rec->rectype );
303         return G10ERR_TRUSTDB;
304     }
305     p++;
306     switch( rec->rectype ) {
307       case 0:  /* unused record */
308         break;
309       case RECTYPE_VER: /* version record */
310         if( memcmp(buf+1, "gpg", 3 ) ) {
311             log_error_f( db_name, _("not a trustdb file\n") );
312             rc = G10ERR_TRUSTDB;
313         }
314         p += 2; /* skip magic */
315         rec->r.ver.version  = *p++;
316         rec->r.ver.locked   = buftoulong(p); p += 4;
317         rec->r.ver.created  = buftoulong(p); p += 4;
318         rec->r.ver.modified = buftoulong(p); p += 4;
319         rec->r.ver.validated= buftoulong(p); p += 4;
320         rec->r.ver.marginals_needed = *p++;
321         rec->r.ver.completes_needed = *p++;
322         rec->r.ver.max_cert_depth = *p++;
323         if( recnum ) {
324             log_error_f( db_name, "version record with recnum %lu\n",
325                                                              (ulong)recnum );
326             rc = G10ERR_TRUSTDB;
327         }
328         if( rec->r.ver.version != 2 ) {
329             log_error_f( db_name, "invalid file version %d\n",
330                                                         rec->r.ver.version );
331             rc = G10ERR_TRUSTDB;
332         }
333         break;
334       case RECTYPE_DIR:   /*directory record */
335         rec->r.dir.lid      = buftoulong(p); p += 4;
336         rec->r.dir.keylist  = buftoulong(p); p += 4;
337         rec->r.dir.uidlist  = buftoulong(p); p += 4;
338         rec->r.dir.cacherec = buftoulong(p); p += 4;
339         rec->r.dir.ownertrust = *p++;
340         rec->r.dir.dirflags   = *p++;
341         if( rec->r.dir.lid != recnum ) {
342             log_error_f( db_name, "dir LID != recnum (%lu,%lu)\n",
343                                          rec->r.dir.lid, (ulong)recnum );
344             rc = G10ERR_TRUSTDB;
345         }
346         break;
347       case RECTYPE_KEY:   /* public key record */
348         rec->r.key.lid      = buftoulong(p); p += 4;
349         rec->r.key.next     = buftoulong(p); p += 4;
350         p += 7;
351         rec->r.key.keyflags = *p++;
352         rec->r.key.pubkey_algo = *p++;
353         rec->r.key.fingerprint_len = *p++;
354         if( rec->r.key.fingerprint_len < 1 || rec->r.key.fingerprint_len > 20 )
355             rec->r.key.fingerprint_len = 20;
356         memcpy( rec->r.key.fingerprint, p, 20);
357         break;
358       case RECTYPE_UID:   /* user id record */
359         rec->r.uid.lid      = buftoulong(p); p += 4;
360         rec->r.uid.next     = buftoulong(p); p += 4;
361         rec->r.uid.prefrec  = buftoulong(p); p += 4;
362         rec->r.uid.siglist  = buftoulong(p); p += 4;
363         rec->r.uid.uidflags = *p++;
364         p ++;
365         memcpy( rec->r.uid.namehash, p, 20);
366         break;
367       case RECTYPE_PREF:  /* preference record */
368         rec->r.pref.lid     = buftoulong(p); p += 4;
369         rec->r.pref.next    = buftoulong(p); p += 4;
370         break;
371       case RECTYPE_SIG:
372         rec->r.sig.lid     = buftoulong(p); p += 4;
373         rec->r.sig.next    = buftoulong(p); p += 4;
374         for(i=0; i < SIGS_PER_RECORD; i++ ) {
375             rec->r.sig.sig[i].lid  = buftoulong(p); p += 4;
376             rec->r.sig.sig[i].flag = *p++;
377         }
378         break;
379       case RECTYPE_CACH:   /* cache record (FIXME)*/
380         rec->r.cache.lid    = buftoulong(p); p += 4;
381         memcpy(rec->r.cache.blockhash, p, 20); p += 20;
382         rec->r.cache.trustlevel = *p++;
383         break;
384       default:
385         log_error_f( db_name, "invalid record type %d at recnum %lu\n",
386                                               rec->rectype, (ulong)recnum );
387         rc = G10ERR_TRUSTDB;
388         break;
389     }
390
391     return rc;
392 }
393
394 /****************
395  * Write the record at RECNUM
396  * FIXME: create/update keyhash record.
397  */
398 int
399 tdbio_write_record( TRUSTREC *rec )
400 {
401     byte buf[TRUST_RECORD_LEN], *p;
402     int rc = 0;
403     int i, n;
404     ulong recnum = rec->recnum;
405
406     if( db_fd == -1 )
407         open_db();
408
409     memset(buf, 0, TRUST_RECORD_LEN);
410     p = buf;
411     *p++ = rec->rectype; p++;
412     switch( rec->rectype ) {
413       case 0:  /* unused record */
414         break;
415       case 1: /* version record */
416         BUG();
417         break;
418
419       case RECTYPE_DIR:   /*directory record */
420         ulongtobuf(p, rec->r.dir.lid); p += 4;
421         ulongtobuf(p, rec->r.dir.keylist); p += 4;
422         ulongtobuf(p, rec->r.dir.uidlist); p += 4;
423         ulongtobuf(p, rec->r.dir.cacherec); p += 4;
424         *p++ = rec->r.dir.ownertrust;
425         *p++ = rec->r.dir.dirflags;
426         assert( rec->r.dir.lid == recnum );
427         break;
428
429       case RECTYPE_KEY:
430         ulongtobuf(p, rec->r.key.lid); p += 4;
431         ulongtobuf(p, rec->r.key.next); p += 4;
432         p += 7;
433         *p++ = rec->r.key.keyflags;
434         *p++ = rec->r.key.pubkey_algo;
435         *p++ = rec->r.key.fingerprint_len;
436         memcpy( p, rec->r.key.fingerprint, 20); p += 20;
437         break;
438
439       case RECTYPE_UID:   /* user id record */
440         ulongtobuf(p, rec->r.uid.lid); p += 4;
441         ulongtobuf(p, rec->r.uid.next); p += 4;
442         ulongtobuf(p, rec->r.uid.prefrec); p += 4;
443         ulongtobuf(p, rec->r.uid.siglist); p += 4;
444         *p++ = rec->r.uid.uidflags;
445         p++;
446         memcpy( p, rec->r.uid.namehash, 20 ); p += 20;
447         break;
448
449       case RECTYPE_PREF:
450         ulongtobuf(p, rec->r.pref.lid); p += 4;
451         ulongtobuf(p, rec->r.pref.next); p += 4;
452         break;
453
454       case RECTYPE_SIG:
455         ulongtobuf(p, rec->r.sig.lid); p += 4;
456         ulongtobuf(p, rec->r.sig.next); p += 4;
457         for(i=0; i < SIGS_PER_RECORD; i++ ) {
458             ulongtobuf(p, rec->r.sig.sig[i].lid); p += 4;
459             *p++ = rec->r.sig.sig[i].flag;
460         }
461         break;
462
463       case RECTYPE_CACH:   /* FIXME*/
464         ulongtobuf(p, rec->r.cache.lid); p += 4;
465         memcpy(p, rec->r.cache.blockhash, 20); p += 20;
466         *p++ = rec->r.cache.trustlevel;
467         break;
468
469       default:
470         BUG();
471     }
472
473     if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) {
474         log_error(_("trustdb: lseek failed: %s\n"), strerror(errno) );
475         return G10ERR_WRITE_FILE;
476     }
477     n = write( db_fd, buf, TRUST_RECORD_LEN);
478     if( n != TRUST_RECORD_LEN ) {
479         log_error(_("trustdb: write failed (n=%d): %s\n"), n, strerror(errno) );
480         return G10ERR_WRITE_FILE;
481     }
482
483     return rc;
484 }
485
486 int
487 tdbio_delete_record( ulong recnum )
488 {
489     TRUSTREC rec;
490
491     rec.recnum = recnum;
492     rec.rectype = 0;
493     return tdbio_write_record( &rec );
494 }
495
496 /****************
497  * create a new record and return its record number
498  */
499 ulong
500 tdbio_new_recnum()
501 {
502     off_t offset;
503     ulong recnum;
504     TRUSTREC rec;
505     int rc;
506
507     /* fixme: look for unused records */
508     offset = lseek( db_fd, 0, SEEK_END );
509     if( offset == -1 )
510         log_fatal("trustdb: lseek to end failed: %s\n", strerror(errno) );
511     recnum = offset / TRUST_RECORD_LEN;
512     assert(recnum); /* this is will never be the first record */
513
514     /* we must write a record, so that the next call to this function
515      * returns another recnum */
516     memset( &rec, 0, sizeof rec );
517     rec.rectype = 0; /* free record */
518     rec.recnum = recnum;
519     rc = tdbio_write_record( &rec );
520     if( rc )
521         log_fatal_f(db_name,_("failed to append a record: %s\n"),
522                                             g10_errstr(rc));
523     return recnum ;
524 }
525
526
527
528 /****************
529  * Search the trustdb for a key which matches PK and return the dir record
530  * The local_id of PK is set to the correct value
531  *
532  * Note: To increase performance, we could use a index search here.
533  *       tdbio_write_record shoudl create this index automagically
534  */
535 int
536 tdbio_search_dir_record( PKT_public_key *pk, TRUSTREC *rec )
537 {
538     ulong recnum;
539     u32 keyid[2];
540     byte *fingerprint;
541     size_t fingerlen;
542     int rc;
543
544     keyid_from_pk( pk, keyid );
545     fingerprint = fingerprint_from_pk( pk, NULL, &fingerlen );
546     assert( fingerlen == 20 || fingerlen == 16 );
547
548     for(recnum=1; !(rc=tdbio_read_record( recnum, rec, 0)); recnum++ ) {
549         if( rec->rectype != RECTYPE_KEY )
550             continue;
551         if( rec->r.key.pubkey_algo == pk->pubkey_algo
552             && !memcmp(rec->r.key.fingerprint, fingerprint, fingerlen) ) {
553             /* found: read the dir record for this key */
554             recnum = rec->r.key.lid;
555             rc = tdbio_read_record( recnum, rec, RECTYPE_DIR);
556             if( rc )
557                 break;
558             if( pk->local_id && pk->local_id != recnum )
559                 log_error_f(db_name,
560                            "found record, but LID from memory does "
561                            "not match recnum (%lu,%lu)\n",
562                                                 pk->local_id, recnum );
563             pk->local_id = recnum;
564             return 0;
565         }
566     }
567     if( rc != -1 )
568         log_error_f( db_name, _("search_db failed: %s\n"), g10_errstr(rc) );
569     return rc;
570 }
571
572
573 /****************
574  * Delete the Userid UIDLID from DIRLID
575  */
576 int
577 tdbio_delete_uidrec( ulong dirlid, ulong uidlid )
578 {
579     return G10ERR_GENERAL; /* not implemented */
580 }
581
582