aboutsummaryrefslogtreecommitdiffstats
path: root/g10/tdbio.c
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>1998-07-09 13:37:17 +0000
committerWerner Koch <[email protected]>1998-07-09 13:37:17 +0000
commite143f23c237e523758173e65ff8fdd2966ed938b (patch)
treeb17a57e05ddb9680922b00a42bb5ebefe62d71cd /g10/tdbio.c
parentfixed clearsig stuff (diff)
downloadgnupg-e143f23c237e523758173e65ff8fdd2966ed938b.tar.gz
gnupg-e143f23c237e523758173e65ff8fdd2966ed938b.zip
fixed severe exploitV0-3-2
Diffstat (limited to '')
-rw-r--r--g10/tdbio.c518
1 files changed, 518 insertions, 0 deletions
diff --git a/g10/tdbio.c b/g10/tdbio.c
new file mode 100644
index 000000000..d3b9a7221
--- /dev/null
+++ b/g10/tdbio.c
@@ -0,0 +1,518 @@
+/* tdbio.c
+ * Copyright (C) 1998 Free Software Foundation, Inc.
+ *
+ * This file is part of GNUPG.
+ *
+ * GNUPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GNUPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "errors.h"
+#include "iobuf.h"
+#include "memory.h"
+#include "util.h"
+#include "options.h"
+#include "main.h"
+#include "i18n.h"
+#include "trustdb.h"
+#include "tdbio.h"
+
+
+
+static char *db_name;
+static int db_fd = -1;
+
+
+
+static void create_db( const char *fname );
+static void open_db(void);
+
+/**************************************************
+ ************** read and write helpers ************
+ **************************************************/
+
+static void
+fwrite_8(FILE *fp, byte a)
+{
+ if( putc( a & 0xff, fp ) == EOF )
+ log_fatal("error writing byte to trustdb: %s\n", strerror(errno) );
+}
+
+
+static void
+fwrite_32( FILE*fp, ulong a)
+{
+ putc( (a>>24) & 0xff, fp );
+ putc( (a>>16) & 0xff, fp );
+ putc( (a>> 8) & 0xff, fp );
+ if( putc( a & 0xff, fp ) == EOF )
+ log_fatal("error writing ulong to trustdb: %s\n", strerror(errno) );
+}
+
+static void
+fwrite_zeros( FILE *fp, size_t n)
+{
+ while( n-- )
+ if( putc( 0, fp ) == EOF )
+ log_fatal("error writing zeros to trustdb: %s\n", strerror(errno) );
+}
+
+
+
+
+/**************************************************
+ ************** read and write stuff **************
+ **************************************************/
+
+int
+tdbio_set_dbname( const char *new_dbname, int create )
+{
+ char *fname;
+
+ fname = new_dbname? m_strdup( new_dbname )
+ : make_filename(opt.homedir, "trustdb.gpg", NULL );
+
+ if( access( fname, R_OK ) ) {
+ if( errno != ENOENT ) {
+ log_error_f( fname, _("can't access: %s\n"), strerror(errno) );
+ m_free(fname);
+ return G10ERR_TRUSTDB;
+ }
+ if( create ) {
+ char *p = strrchr( fname, '/' );
+ assert(p);
+ *p = 0;
+ if( access( fname, F_OK ) ) {
+ if( strlen(fname) >= 7
+ && !strcmp(fname+strlen(fname)-7, "/.gnupg" ) ) {
+ #if __MINGW32__
+ if( mkdir( fname ) )
+ #else
+ if( mkdir( fname, S_IRUSR|S_IWUSR|S_IXUSR ) )
+ #endif
+ log_fatal_f( fname, _("can't create directory: %s\n"),
+ strerror(errno) );
+ }
+ else
+ log_fatal_f(fname, _("directory does not exist!\n") );
+ }
+ *p = '/';
+ create_db( fname );
+ }
+ }
+ m_free(db_name);
+ db_name = fname;
+ return 0;
+}
+
+
+const char *
+tdbio_get_dbname()
+{
+ return db_name;
+}
+
+
+
+/****************
+ * Create a new trustdb
+ */
+static void
+create_db( const char *fname )
+{
+ FILE *fp;
+
+ fp =fopen( fname, "w" );
+ if( !fp )
+ log_fatal_f( fname, _("can't create %s: %s\n"), strerror(errno) );
+ fwrite_8( fp, 1 );
+ fwrite_8( fp, 'g' );
+ fwrite_8( fp, 'p' );
+ fwrite_8( fp, 'g' );
+ fwrite_8( fp, 1 ); /* version */
+ fwrite_zeros( fp, 3 ); /* reserved */
+ fwrite_32( fp, 0 ); /* not locked */
+ fwrite_32( fp, make_timestamp() ); /* created */
+ fwrite_32( fp, 0 ); /* not yet modified */
+ fwrite_32( fp, 0 ); /* not yet validated*/
+ fwrite_32( fp, 0 ); /* reserved */
+ fwrite_8( fp, 3 ); /* marginals needed */
+ fwrite_8( fp, 1 ); /* completes needed */
+ fwrite_8( fp, 4 ); /* max_cet_depth */
+ fwrite_zeros( fp, 9 ); /* filler */
+ fclose(fp);
+}
+
+
+
+static void
+open_db()
+{
+ TRUSTREC rec;
+ assert( db_fd == -1 );
+
+ db_fd = open( db_name, O_RDWR );
+ if( db_fd == -1 )
+ log_fatal_f( db_name, _("can't open: %s\n"), strerror(errno) );
+ if( tdbio_read_record( 0, &rec, RECTYPE_VER ) )
+ log_fatal_f( db_name, _("invalid trust-db\n") );
+ /* fixme: check ->locked and other stuff */
+}
+
+
+void
+tdbio_dump_record( ulong rnum, TRUSTREC *rec, FILE *fp )
+{
+ int i, any;
+
+ fprintf(fp, "rec %5lu, type=", rnum );
+
+ switch( rec->rectype ) {
+ case 0: fprintf(fp, "free\n");
+ break;
+ case RECTYPE_VER: fprintf(fp, "version\n");
+ break;
+ case RECTYPE_DIR:
+ fprintf(fp, "dir keyid=%08lX, key=%lu, ctl=%lu, sig=%lu",
+ (ulong)rec->r.dir.keyid[1],
+ rec->r.dir.keyrec, rec->r.dir.ctlrec, rec->r.dir.sigrec );
+ if( rec->r.dir.no_sigs == 1 )
+ fputs(", (none)", fp );
+ else if( rec->r.dir.no_sigs == 2 )
+ fputs(", (invalid)", fp );
+ else if( rec->r.dir.no_sigs == 3 )
+ fputs(", (revoked)", fp );
+ else if( rec->r.dir.no_sigs )
+ fputs(", (??)", fp );
+ putc('\n', fp);
+ break;
+ case RECTYPE_KEY: fprintf(fp,
+ "key %08lX, own=%lu, ownertrust=%02x, fl=%d\n",
+ (ulong)rec->r.key.keyid[1],
+ rec->r.key.owner, rec->r.key.ownertrust,
+ rec->r.key.fingerprint_len );
+ break;
+ case RECTYPE_UID:
+ if( !rec->r.uid.subtype )
+ fprintf(fp,
+ "uid %02x%02x, owner=%lu, chain=%lu, pref=%lu, otr=%02x\n",
+ rec->r.uid.namehash[18], rec->r.uid.namehash[19],
+ rec->r.uid.owner, rec->r.uid.chain, (ulong)rec->r.uid.prefrec,
+ rec->r.uid.ownertrust );
+ else
+ fprintf(fp,
+ "uid subtype%d, owner=%lu, chain=%lu\n",
+ rec->r.uid.subtype, rec->r.uid.owner, rec->r.uid.chain);
+ break;
+ case RECTYPE_CTL: fprintf(fp, "ctl\n");
+ break;
+ case RECTYPE_SIG:
+ fprintf(fp, "sigrec, owner=%lu, chain=%lu\n",
+ rec->r.sig.owner, rec->r.sig.chain );
+ for(i=any=0; i < SIGS_PER_RECORD; i++ ) {
+ if( rec->r.sig.sig[i].local_id ) {
+ if( !any ) {
+ putc('\t', fp);
+ any++;
+ }
+ fprintf(fp, " %lu:%02x", rec->r.sig.sig[i].local_id,
+ rec->r.sig.sig[i].flag );
+ }
+ }
+ if( any )
+ putc('\n', fp);
+ break;
+ default:
+ fprintf(fp, "%d (unknown)\n", rec->rectype );
+ break;
+ }
+}
+
+/****************
+ * read the record with number recnum
+ * returns: -1 on error, 0 on success
+ */
+int
+tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected )
+{
+ byte buf[TRUST_RECORD_LEN], *p;
+ int rc = 0;
+ int n, i;
+
+ if( db_fd == -1 )
+ open_db();
+ if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) {
+ log_error(_("trustdb: lseek failed: %s\n"), strerror(errno) );
+ return G10ERR_READ_FILE;
+ }
+ n = read( db_fd, buf, TRUST_RECORD_LEN);
+ if( !n ) {
+ return -1; /* eof */
+ }
+ else if( n != TRUST_RECORD_LEN ) {
+ log_error(_("trustdb: read failed (n=%d): %s\n"), n, strerror(errno) );
+ return G10ERR_READ_FILE;
+ }
+ p = buf;
+ rec->rectype = *p++;
+ if( expected && rec->rectype != expected ) {
+ log_error("%lu: read expected rec type %d, got %d\n",
+ recnum, expected, rec->rectype );
+ return G10ERR_TRUSTDB;
+ }
+ p++;
+ switch( rec->rectype ) {
+ case 0: /* unused record */
+ break;
+ case RECTYPE_VER: /* version record */
+ /* g10 was the original name */
+ if( memcmp(buf+1, "gpg", 3 ) && memcmp(buf+1, "g10", 3 ) ) {
+ log_error_f( db_name, _("not a trustdb file\n") );
+ rc = G10ERR_TRUSTDB;
+ }
+ p += 2; /* skip magic */
+ rec->r.ver.version = *p++;
+ rec->r.ver.locked = buftoulong(p); p += 4;
+ rec->r.ver.created = buftoulong(p); p += 4;
+ rec->r.ver.modified = buftoulong(p); p += 4;
+ rec->r.ver.validated= buftoulong(p); p += 4;
+ rec->r.ver.marginals_needed = *p++;
+ rec->r.ver.completes_needed = *p++;
+ rec->r.ver.max_cert_depth = *p++;
+ if( recnum ) {
+ log_error_f( db_name, "version record with recnum %lu\n",
+ (ulong)recnum );
+ rc = G10ERR_TRUSTDB;
+ }
+ if( rec->r.ver.version != 1 ) {
+ log_error_f( db_name, "invalid file version %d\n",
+ rec->r.ver.version );
+ rc = G10ERR_TRUSTDB;
+ }
+ break;
+ case RECTYPE_DIR: /*directory record */
+ rec->r.dir.local_id = buftoulong(p); p += 4;
+ rec->r.dir.keyid[0] = buftou32(p); p += 4;
+ rec->r.dir.keyid[1] = buftou32(p); p += 4;
+ rec->r.dir.keyrec = buftoulong(p); p += 4;
+ rec->r.dir.ctlrec = buftoulong(p); p += 4;
+ rec->r.dir.sigrec = buftoulong(p); p += 4;
+ rec->r.dir.no_sigs = *p++;
+ if( rec->r.dir.local_id != recnum ) {
+ log_error_f( db_name, "dir local_id != recnum (%lu,%lu)\n",
+ (ulong)rec->r.dir.local_id,
+ (ulong)recnum );
+ rc = G10ERR_TRUSTDB;
+ }
+ break;
+ case RECTYPE_KEY: /* public key record */
+ rec->r.key.owner = buftoulong(p); p += 4;
+ rec->r.dir.keyid[0] = buftou32(p); p += 4;
+ rec->r.dir.keyid[1] = buftou32(p); p += 4;
+ rec->r.key.pubkey_algo = *p++;
+ rec->r.key.fingerprint_len = *p++;
+ if( rec->r.key.fingerprint_len < 1 || rec->r.key.fingerprint_len > 20 )
+ rec->r.key.fingerprint_len = 20;
+ memcpy( rec->r.key.fingerprint, p, 20); p += 20;
+ rec->r.key.ownertrust = *p++;
+ break;
+ case RECTYPE_CTL: /* control record */
+ rec->r.ctl.owner = buftoulong(p); p += 4;
+ memcpy(rec->r.ctl.blockhash, p, 20); p += 20;
+ rec->r.ctl.trustlevel = *p++;
+ break;
+ case RECTYPE_SIG:
+ rec->r.sig.owner = buftoulong(p); p += 4;
+ rec->r.sig.chain = buftoulong(p); p += 4;
+ for(i=0; i < SIGS_PER_RECORD; i++ ) {
+ rec->r.sig.sig[i].local_id = buftoulong(p); p += 4;
+ rec->r.sig.sig[i].flag = *p++;
+ }
+ break;
+ default:
+ log_error_f( db_name, "invalid record type %d at recnum %lu\n",
+ rec->rectype, (ulong)recnum );
+ rc = G10ERR_TRUSTDB;
+ break;
+ }
+
+ return rc;
+}
+
+/****************
+ * Write the record at RECNUM
+ */
+int
+tdbio_write_record( ulong recnum, TRUSTREC *rec )
+{
+ byte buf[TRUST_RECORD_LEN], *p;
+ int rc = 0;
+ int i, n;
+
+ if( db_fd == -1 )
+ open_db();
+
+ memset(buf, 0, TRUST_RECORD_LEN);
+ p = buf;
+ *p++ = rec->rectype; p++;
+ switch( rec->rectype ) {
+ case 0: /* unused record */
+ break;
+ case 1: /* version record */
+ BUG();
+ break;
+
+ case RECTYPE_DIR: /*directory record */
+ ulongtobuf(p, rec->r.dir.local_id); p += 4;
+ u32tobuf(p, rec->r.key.keyid[0]); p += 4;
+ u32tobuf(p, rec->r.key.keyid[1]); p += 4;
+ ulongtobuf(p, rec->r.dir.keyrec); p += 4;
+ ulongtobuf(p, rec->r.dir.ctlrec); p += 4;
+ ulongtobuf(p, rec->r.dir.sigrec); p += 4;
+ *p++ = rec->r.dir.no_sigs;
+ assert( rec->r.dir.local_id == recnum );
+ break;
+
+ case RECTYPE_KEY:
+ ulongtobuf(p, rec->r.key.owner); p += 4;
+ u32tobuf(p, rec->r.key.keyid[0]); p += 4;
+ u32tobuf(p, rec->r.key.keyid[1]); p += 4;
+ *p++ = rec->r.key.pubkey_algo;
+ *p++ = rec->r.key.fingerprint_len;
+ memcpy( p, rec->r.key.fingerprint, 20); p += 20;
+ *p++ = rec->r.key.ownertrust;
+ break;
+
+ case RECTYPE_CTL: /* control record */
+ ulongtobuf(p, rec->r.ctl.owner); p += 4;
+ memcpy(p, rec->r.ctl.blockhash, 20); p += 20;
+ *p++ = rec->r.ctl.trustlevel;
+ break;
+
+ case RECTYPE_SIG:
+ ulongtobuf(p, rec->r.sig.owner); p += 4;
+ ulongtobuf(p, rec->r.sig.chain); p += 4;
+ for(i=0; i < SIGS_PER_RECORD; i++ ) {
+ ulongtobuf(p, rec->r.sig.sig[i].local_id); p += 4;
+ *p++ = rec->r.sig.sig[i].flag;
+ }
+ break;
+ default:
+ BUG();
+ }
+
+ if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) {
+ log_error(_("trustdb: lseek failed: %s\n"), strerror(errno) );
+ return G10ERR_WRITE_FILE;
+ }
+ n = write( db_fd, buf, TRUST_RECORD_LEN);
+ if( n != TRUST_RECORD_LEN ) {
+ log_error(_("trustdb: write failed (n=%d): %s\n"), n, strerror(errno) );
+ return G10ERR_WRITE_FILE;
+ }
+
+ return rc;
+}
+
+
+/****************
+ * create a new record and return its record number
+ */
+ulong
+tdbio_new_recnum()
+{
+ off_t offset;
+ ulong recnum;
+ TRUSTREC rec;
+ int rc;
+
+ /* fixme: look for unused records */
+ offset = lseek( db_fd, 0, SEEK_END );
+ if( offset == -1 )
+ log_fatal("trustdb: lseek to end failed: %s\n", strerror(errno) );
+ recnum = offset / TRUST_RECORD_LEN;
+ assert(recnum); /* this is will never be the first record */
+
+ /* we must write a record, so that the next call to this function
+ * returns another recnum */
+ memset( &rec, 0, sizeof rec );
+ rec.rectype = 0; /* free record */
+ rc = tdbio_write_record(recnum, &rec );
+ if( rc )
+ log_fatal_f(db_name,_("failed to append a record: %s\n"),
+ g10_errstr(rc));
+ return recnum ;
+}
+
+
+
+/****************
+ * Search the trustdb for a key which matches PK and return the dir record
+ * The local_id of PK is set to the correct value
+ *
+ * Note: To increase performance, we could use a index search here.
+ */
+int
+tdbio_search_record( PKT_public_key *pk, TRUSTREC *rec )
+{
+ ulong recnum;
+ u32 keyid[2];
+ byte *fingerprint;
+ size_t fingerlen;
+ int rc;
+
+ keyid_from_pk( pk, keyid );
+ fingerprint = fingerprint_from_pk( pk, &fingerlen );
+ assert( fingerlen == 20 || fingerlen == 16 );
+
+ for(recnum=1; !(rc=tdbio_read_record( recnum, rec, 0)); recnum++ ) {
+ if( rec->rectype != RECTYPE_DIR )
+ continue;
+ if( rec->r.dir.keyid[0] == keyid[0]
+ && rec->r.dir.keyid[1] == keyid[1]){
+ TRUSTREC keyrec;
+
+ if( tdbio_read_record( rec->r.dir.keyrec, &keyrec, RECTYPE_KEY ) ) {
+ log_error("%lu: ooops: invalid key record\n", recnum );
+ break;
+ }
+ if( keyrec.r.key.pubkey_algo == pk->pubkey_algo
+ && !memcmp(keyrec.r.key.fingerprint, fingerprint, fingerlen) ){
+ if( pk->local_id && pk->local_id != recnum )
+ log_error_f(db_name,
+ "found record, but local_id from memory does "
+ "not match recnum (%lu,%lu)\n",
+ (ulong)pk->local_id, (ulong)recnum );
+ pk->local_id = recnum;
+ return 0;
+ }
+ }
+ }
+ if( rc != -1 )
+ log_error_f( db_name, _("search_db failed: %s\n"), g10_errstr(rc) );
+ return rc;
+}
+
+