Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F27670640
tdbio.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
28 KB
Subscribers
None
tdbio.c
View Options
/* 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"
/* a type used to pass infomation to cmp_krec_fpr */
struct
cmp_krec_fpr_struct
{
int
pubkey_algo
;
const
char
*
fpr
;
int
fprlen
;
};
/* a type used to pass infomation to cmp_sdir */
struct
cmp_sdir_struct
{
int
pubkey_algo
;
u32
keyid
[
2
];
};
static
char
*
db_name
;
static
int
db_fd
=
-1
;
static
void
open_db
(
void
);
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
)
{
FILE
*
fp
;
TRUSTREC
rec
;
int
rc
;
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
=
'/'
;
fp
=
fopen
(
fname
,
"wb"
);
if
(
!
fp
)
log_fatal_f
(
fname
,
_
(
"can't create: %s
\n
"
),
strerror
(
errno
)
);
fclose
(
fp
);
m_free
(
db_name
);
db_name
=
fname
;
#ifdef __MINGW32__
db_fd
=
open
(
db_name
,
O_RDWR
|
O_BINARY
);
#else
db_fd
=
open
(
db_name
,
O_RDWR
);
#endif
if
(
db_fd
==
-1
)
log_fatal_f
(
db_name
,
_
(
"can't open: %s
\n
"
),
strerror
(
errno
)
);
memset
(
&
rec
,
0
,
sizeof
rec
);
rec
.
r
.
ver
.
version
=
2
;
rec
.
r
.
ver
.
created
=
make_timestamp
();
rec
.
rectype
=
RECTYPE_VER
;
rec
.
recnum
=
0
;
rc
=
tdbio_write_record
(
&
rec
);
if
(
rc
)
log_fatal_f
(
fname
,
_
(
"failed to create version record: %s"
),
g10_errstr
(
rc
));
/* and read again to check that we are okay */
if
(
tdbio_read_record
(
0
,
&
rec
,
RECTYPE_VER
)
)
log_fatal_f
(
db_name
,
"invalid trust-db created
\n
"
);
return
0
;
}
}
m_free
(
db_name
);
db_name
=
fname
;
return
0
;
}
const
char
*
tdbio_get_dbname
()
{
return
db_name
;
}
static
void
open_db
()
{
TRUSTREC
rec
;
assert
(
db_fd
==
-1
);
#ifdef __MINGW32__
db_fd
=
open
(
db_name
,
O_RDWR
|
O_BINARY
);
#else
db_fd
=
open
(
db_name
,
O_RDWR
);
#endif
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 */
}
/****************
* Make a hashtable: type 0 = key hash, 1 = sdir hash
*/
static
void
create_hashtable
(
TRUSTREC
*
vr
,
int
type
)
{
TRUSTREC
rec
;
off_t
offset
;
ulong
recnum
;
int
i
,
n
,
rc
;
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 */
if
(
!
type
)
vr
->
r
.
ver
.
keyhashtbl
=
recnum
;
else
vr
->
r
.
ver
.
sdirhashtbl
=
recnum
;
/* Now write the records */
n
=
(
256
+
ITEMS_PER_HTBL_RECORD
-1
)
/
ITEMS_PER_HTBL_RECORD
;
for
(
i
=
0
;
i
<
n
;
i
++
,
recnum
++
)
{
memset
(
&
rec
,
0
,
sizeof
rec
);
rec
.
rectype
=
RECTYPE_HTBL
;
/* free record */
rec
.
recnum
=
recnum
;
rc
=
tdbio_write_record
(
&
rec
);
if
(
rc
)
log_fatal_f
(
db_name
,
_
(
"failed to create hashtable: %s
\n
"
),
g10_errstr
(
rc
));
}
/* update the version record */
rc
=
tdbio_write_record
(
vr
);
if
(
rc
)
log_fatal_f
(
db_name
,
_
(
"error updating version record: %s
\n
"
),
g10_errstr
(
rc
));
}
/****************
* Return the record number of the keyhash tbl or create a new one.
*/
static
ulong
get_keyhashrec
()
{
static
ulong
keyhashtbl
;
/* record number of the key hashtable */
TRUSTREC
vr
;
int
rc
;
if
(
keyhashtbl
)
return
keyhashtbl
;
rc
=
tdbio_read_record
(
0
,
&
vr
,
RECTYPE_VER
);
if
(
rc
)
log_fatal_f
(
db_name
,
_
(
"error reading version record: %s
\n
"
),
g10_errstr
(
rc
)
);
if
(
!
vr
.
r
.
ver
.
keyhashtbl
)
create_hashtable
(
&
vr
,
0
);
return
vr
.
r
.
ver
.
keyhashtbl
;
}
/****************
* Return the record number of the shadow direcory hash table
* or create a new one.
*/
static
ulong
get_sdirhashrec
()
{
static
ulong
sdirhashtbl
;
/* record number of the hashtable */
TRUSTREC
vr
;
int
rc
;
if
(
sdirhashtbl
)
return
sdirhashtbl
;
rc
=
tdbio_read_record
(
0
,
&
vr
,
RECTYPE_VER
);
if
(
rc
)
log_fatal_f
(
db_name
,
_
(
"error reading version record: %s
\n
"
),
g10_errstr
(
rc
)
);
if
(
!
vr
.
r
.
ver
.
sdirhashtbl
)
create_hashtable
(
&
vr
,
1
);
return
vr
.
r
.
ver
.
sdirhashtbl
;
}
/****************
* Update a hashtable.
* table gives the start of the table, key and keylen is the key,
* newrecnum is the record number to insert.
*/
static
int
upd_hashtable
(
ulong
table
,
byte
*
key
,
int
keylen
,
ulong
newrecnum
)
{
TRUSTREC
lastrec
,
rec
;
ulong
hashrec
,
item
;
int
msb
;
int
level
=
0
;
int
rc
,
i
;
hashrec
=
table
;
next_level
:
msb
=
key
[
level
];
hashrec
+=
msb
/
ITEMS_PER_HTBL_RECORD
;
rc
=
tdbio_read_record
(
hashrec
,
&
rec
,
RECTYPE_HTBL
);
if
(
rc
)
{
log_error
(
db_name
,
"upd_hashtable: read failed: %s
\n
"
,
g10_errstr
(
rc
)
);
return
rc
;
}
item
=
rec
.
r
.
htbl
.
item
[
msb
%
ITEMS_PER_HTBL_RECORD
];
if
(
!
item
)
{
/* insert a new item into the hash table */
rec
.
r
.
htbl
.
item
[
msb
%
ITEMS_PER_HTBL_RECORD
]
=
newrecnum
;
rc
=
tdbio_write_record
(
&
rec
);
if
(
rc
)
{
log_error
(
db_name
,
"upd_hashtable: write htbl failed: %s
\n
"
,
g10_errstr
(
rc
)
);
return
rc
;
}
}
else
if
(
item
!=
newrecnum
)
{
/* must do an update */
lastrec
=
rec
;
rc
=
tdbio_read_record
(
item
,
&
rec
,
0
);
if
(
rc
)
{
log_error
(
db_name
,
"upd_hashtable: read item failed: %s
\n
"
,
g10_errstr
(
rc
)
);
return
rc
;
}
if
(
rec
.
rectype
==
RECTYPE_HTBL
)
{
hashrec
=
item
;
level
++
;
if
(
level
>=
keylen
)
{
log_error
(
db_name
,
"hashtable has invalid indirections.
\n
"
);
return
G10ERR_TRUSTDB
;
}
goto
next_level
;
}
else
if
(
rec
.
rectype
==
RECTYPE_HLST
)
{
/* extend list */
/* see whether the key is already in this list */
for
(;;)
{
for
(
i
=
0
;
i
<
ITEMS_PER_HLST_RECORD
;
i
++
)
{
if
(
rec
.
r
.
hlst
.
rnum
[
i
]
==
newrecnum
)
{
return
0
;
/* okay, already in the list */
}
}
if
(
rec
.
r
.
hlst
.
next
)
{
rc
=
tdbio_read_record
(
rec
.
r
.
hlst
.
next
,
&
rec
,
RECTYPE_HLST
);
if
(
rc
)
{
log_error
(
db_name
,
"scan keyhashtbl read hlst failed: %s
\n
"
,
g10_errstr
(
rc
)
);
return
rc
;
}
}
else
break
;
/* not there */
}
/* find the next free entry and put it in */
for
(;;)
{
for
(
i
=
0
;
i
<
ITEMS_PER_HLST_RECORD
;
i
++
)
{
if
(
!
rec
.
r
.
hlst
.
rnum
[
i
]
)
{
rec
.
r
.
hlst
.
rnum
[
i
]
=
newrecnum
;
rc
=
tdbio_write_record
(
&
rec
);
if
(
rc
)
log_error
(
db_name
,
"upd_hashtable: write hlst failed: %s
\n
"
,
g10_errstr
(
rc
)
);
return
rc
;
/* done */
}
}
if
(
rec
.
r
.
hlst
.
next
)
{
rc
=
tdbio_read_record
(
rec
.
r
.
hlst
.
next
,
&
rec
,
RECTYPE_HLST
);
if
(
rc
)
{
log_error
(
db_name
,
"upd_hashtable: read hlst failed: %s
\n
"
,
g10_errstr
(
rc
)
);
return
rc
;
}
}
else
{
/* add a new list record */
rec
.
r
.
hlst
.
next
=
item
=
tdbio_new_recnum
();
rc
=
tdbio_write_record
(
&
rec
);
if
(
rc
)
{
log_error
(
db_name
,
"upd_hashtable: write hlst failed: %s
\n
"
,
g10_errstr
(
rc
)
);
return
rc
;
}
memset
(
&
rec
,
0
,
sizeof
rec
);
rec
.
rectype
=
RECTYPE_HLST
;
rec
.
recnum
=
item
;
rec
.
r
.
hlst
.
rnum
[
0
]
=
newrecnum
;
rc
=
tdbio_write_record
(
&
rec
);
if
(
rc
)
log_error
(
db_name
,
"upd_hashtable: write ext hlst failed: %s
\n
"
,
g10_errstr
(
rc
)
);
return
rc
;
/* done */
}
}
/* end loop over hlst slots */
}
else
if
(
rec
.
rectype
==
RECTYPE_KEY
||
rec
.
rectype
==
RECTYPE_SDIR
)
{
/* insert a list record */
if
(
rec
.
recnum
==
newrecnum
)
{
return
0
;
}
item
=
rec
.
recnum
;
/* save number of key record */
memset
(
&
rec
,
0
,
sizeof
rec
);
rec
.
rectype
=
RECTYPE_HLST
;
rec
.
recnum
=
tdbio_new_recnum
();
rec
.
r
.
hlst
.
rnum
[
0
]
=
item
;
/* old keyrecord */
rec
.
r
.
hlst
.
rnum
[
1
]
=
newrecnum
;
/* and new one */
rc
=
tdbio_write_record
(
&
rec
);
if
(
rc
)
{
log_error
(
db_name
,
"upd_hashtable: write new hlst failed: %s
\n
"
,
g10_errstr
(
rc
)
);
return
rc
;
}
/* update the hashtable record */
lastrec
.
r
.
htbl
.
item
[
msb
%
ITEMS_PER_HTBL_RECORD
]
=
rec
.
recnum
;
rc
=
tdbio_write_record
(
&
lastrec
);
if
(
rc
)
log_error
(
db_name
,
"upd_hashtable: update htbl failed: %s
\n
"
,
g10_errstr
(
rc
)
);
return
rc
;
/* ready */
}
else
{
log_error
(
db_name
,
"hashtbl %lu points to an invalid record
\n
"
,
item
);
return
G10ERR_TRUSTDB
;
}
}
return
0
;
}
/****************
* Lookup a record via the hashtable tablewith key/keylen and return the
* result in rec. cmp() should return if the record is the desired one.
* Returns -1 if not found, 0 if found or another errocode
*/
static
int
lookup_hashtable
(
ulong
table
,
const
byte
*
key
,
size_t
keylen
,
int
(
*
cmpfnc
)(
void
*
,
const
TRUSTREC
*
),
void
*
cmpdata
,
TRUSTREC
*
rec
)
{
int
rc
;
ulong
hashrec
,
item
;
int
msb
;
int
level
=
0
;
hashrec
=
table
;
next_level
:
msb
=
key
[
level
];
hashrec
+=
msb
/
ITEMS_PER_HTBL_RECORD
;
rc
=
tdbio_read_record
(
hashrec
,
rec
,
RECTYPE_HTBL
);
if
(
rc
)
{
log_error
(
db_name
,
"lookup_hashtable failed: %s
\n
"
,
g10_errstr
(
rc
)
);
return
rc
;
}
item
=
rec
->
r
.
htbl
.
item
[
msb
%
ITEMS_PER_HTBL_RECORD
];
if
(
!
item
)
return
-1
;
/* not found */
rc
=
tdbio_read_record
(
item
,
rec
,
0
);
if
(
rc
)
{
log_error
(
db_name
,
"hashtable read failed: %s
\n
"
,
g10_errstr
(
rc
)
);
return
rc
;
}
if
(
rec
->
rectype
==
RECTYPE_HTBL
)
{
hashrec
=
item
;
level
++
;
if
(
level
>=
keylen
)
{
log_error
(
db_name
,
"hashtable has invalid indirections
\n
"
);
return
G10ERR_TRUSTDB
;
}
goto
next_level
;
}
else
if
(
rec
->
rectype
==
RECTYPE_HLST
)
{
for
(;;)
{
int
i
;
for
(
i
=
0
;
i
<
ITEMS_PER_HLST_RECORD
;
i
++
)
{
if
(
rec
->
r
.
hlst
.
rnum
[
i
]
)
{
TRUSTREC
tmp
;
rc
=
tdbio_read_record
(
rec
->
r
.
hlst
.
rnum
[
i
],
&
tmp
,
0
);
if
(
rc
)
{
log_error
(
"lookup_hashtable: read item failed: %s
\n
"
,
g10_errstr
(
rc
)
);
return
rc
;
}
if
(
(
*
cmpfnc
)(
cmpdata
,
&
tmp
)
)
{
*
rec
=
tmp
;
return
0
;
}
}
}
if
(
rec
->
r
.
hlst
.
next
)
{
rc
=
tdbio_read_record
(
rec
->
r
.
hlst
.
next
,
rec
,
RECTYPE_HLST
);
if
(
rc
)
{
log_error
(
"lookup_hashtable: read hlst failed: %s
\n
"
,
g10_errstr
(
rc
)
);
return
rc
;
}
}
else
return
-1
;
/* not found */
}
}
if
(
(
*
cmpfnc
)(
cmpdata
,
rec
)
)
return
0
;
/* really found */
return
-1
;
/* no: not found */
}
/****************
* Update the key hashtbl or create the table if it does not exist
*/
static
int
update_keyhashtbl
(
TRUSTREC
*
kr
)
{
return
upd_hashtable
(
get_keyhashrec
(),
kr
->
r
.
key
.
fingerprint
,
kr
->
r
.
key
.
fingerprint_len
,
kr
->
recnum
);
}
/****************
* Update the shadow dir hashtbl or create the table if it does not exist
*/
static
int
update_sdirhashtbl
(
TRUSTREC
*
sr
)
{
byte
key
[
8
];
u32tobuf
(
key
,
sr
->
r
.
sdir
.
keyid
[
0
]
);
u32tobuf
(
key
+
4
,
sr
->
r
.
sdir
.
keyid
[
1
]
);
return
upd_hashtable
(
get_sdirhashrec
(),
key
,
8
,
sr
->
recnum
);
}
void
tdbio_dump_record
(
TRUSTREC
*
rec
,
FILE
*
fp
)
{
int
i
;
ulong
rnum
=
rec
->
recnum
;
byte
*
p
;
fprintf
(
fp
,
"rec %5lu, "
,
rnum
);
switch
(
rec
->
rectype
)
{
case
0
:
fprintf
(
fp
,
"blank
\n
"
);
break
;
case
RECTYPE_VER
:
fprintf
(
fp
,
"version, kd=%lu, sd=%lu, free=%lu
\n
"
,
rec
->
r
.
ver
.
keyhashtbl
,
rec
->
r
.
ver
.
sdirhashtbl
,
rec
->
r
.
ver
.
firstfree
);
break
;
case
RECTYPE_FREE
:
fprintf
(
fp
,
"free, next=%lu
\n
"
,
rec
->
r
.
free
.
next
);
break
;
case
RECTYPE_DIR
:
fprintf
(
fp
,
"dir %lu, keys=%lu, uids=%lu, cach=%lu, ot=%02x"
,
rec
->
r
.
dir
.
lid
,
rec
->
r
.
dir
.
keylist
,
rec
->
r
.
dir
.
uidlist
,
rec
->
r
.
dir
.
cacherec
,
rec
->
r
.
dir
.
ownertrust
);
if
(
rec
->
r
.
dir
.
dirflags
&
DIRF_ERROR
)
fputs
(
", error"
,
fp
);
if
(
rec
->
r
.
dir
.
dirflags
&
DIRF_CHECKED
)
fputs
(
", checked"
,
fp
);
if
(
rec
->
r
.
dir
.
dirflags
&
DIRF_REVOKED
)
fputs
(
", revoked"
,
fp
);
if
(
rec
->
r
.
dir
.
dirflags
&
DIRF_MISKEY
)
fputs
(
", miskey"
,
fp
);
putc
(
'\n'
,
fp
);
break
;
case
RECTYPE_KEY
:
fprintf
(
fp
,
"key %lu, next=%lu, algo=%d, "
,
rec
->
r
.
key
.
lid
,
rec
->
r
.
key
.
next
,
rec
->
r
.
key
.
pubkey_algo
);
for
(
i
=
0
;
i
<
rec
->
r
.
key
.
fingerprint_len
;
i
++
)
fprintf
(
fp
,
"%02X"
,
rec
->
r
.
key
.
fingerprint
[
i
]
);
if
(
rec
->
r
.
key
.
keyflags
&
KEYF_REVOKED
)
fputs
(
", revoked"
,
fp
);
putc
(
'\n'
,
fp
);
break
;
case
RECTYPE_UID
:
fprintf
(
fp
,
"uid %lu, next=%lu, pref=%lu, sig=%lu, hash=%02X%02X"
,
rec
->
r
.
uid
.
lid
,
rec
->
r
.
uid
.
next
,
rec
->
r
.
uid
.
prefrec
,
rec
->
r
.
uid
.
siglist
,
rec
->
r
.
uid
.
namehash
[
18
],
rec
->
r
.
uid
.
namehash
[
19
]);
if
(
rec
->
r
.
uid
.
uidflags
&
UIDF_CHECKED
)
fputs
(
", checked"
,
fp
);
if
(
rec
->
r
.
uid
.
uidflags
&
UIDF_VALID
)
fputs
(
", valid"
,
fp
);
if
(
rec
->
r
.
uid
.
uidflags
&
UIDF_REVOKED
)
fputs
(
", revoked"
,
fp
);
putc
(
'\n'
,
fp
);
break
;
case
RECTYPE_PREF
:
fprintf
(
fp
,
"pref %lu, next=%lu,"
,
rec
->
r
.
pref
.
lid
,
rec
->
r
.
pref
.
next
);
for
(
i
=
0
,
p
=
rec
->
r
.
pref
.
data
;
i
<
ITEMS_PER_PREF_RECORD
;
i
+=
2
,
p
+=
2
)
{
if
(
*
p
)
fprintf
(
fp
,
" %c%d"
,
*
p
==
PREFTYPE_SYM
?
'S'
:
*
p
==
PREFTYPE_HASH
?
'H'
:
*
p
==
PREFTYPE_COMPR
?
'Z'
:
'?'
,
p
[
1
]);
}
putc
(
'\n'
,
fp
);
break
;
case
RECTYPE_SIG
:
fprintf
(
fp
,
"sig %lu, next=%lu,"
,
rec
->
r
.
sig
.
lid
,
rec
->
r
.
sig
.
next
);
for
(
i
=
0
;
i
<
SIGS_PER_RECORD
;
i
++
)
{
if
(
rec
->
r
.
sig
.
sig
[
i
].
lid
)
fprintf
(
fp
,
" %lu:%02x"
,
rec
->
r
.
sig
.
sig
[
i
].
lid
,
rec
->
r
.
sig
.
sig
[
i
].
flag
);
}
putc
(
'\n'
,
fp
);
break
;
case
RECTYPE_SDIR
:
fprintf
(
fp
,
"sdir %lu, keyid=%08lX%08lX, algo=%d, hint=%lu
\n
"
,
rec
->
r
.
sdir
.
lid
,
(
ulong
)
rec
->
r
.
sdir
.
keyid
[
0
],
(
ulong
)
rec
->
r
.
sdir
.
keyid
[
1
],
rec
->
r
.
sdir
.
pubkey_algo
,
(
ulong
)
rec
->
r
.
sdir
.
hintlist
);
break
;
case
RECTYPE_CACH
:
fprintf
(
fp
,
"cach
\n
"
);
break
;
case
RECTYPE_HTBL
:
fprintf
(
fp
,
"htbl,"
);
for
(
i
=
0
;
i
<
ITEMS_PER_HTBL_RECORD
;
i
++
)
fprintf
(
fp
,
" %lu"
,
rec
->
r
.
htbl
.
item
[
i
]
);
putc
(
'\n'
,
fp
);
break
;
case
RECTYPE_HLST
:
fprintf
(
fp
,
"hlst, next=%lu,"
,
rec
->
r
.
hlst
.
next
);
for
(
i
=
0
;
i
<
ITEMS_PER_HLST_RECORD
;
i
++
)
fprintf
(
fp
,
" %lu"
,
rec
->
r
.
hlst
.
rnum
[
i
]
);
putc
(
'\n'
,
fp
);
break
;
default
:
fprintf
(
fp
,
"unknown type %d
\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
;
}
rec
->
recnum
=
recnum
;
rec
->
dirty
=
0
;
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
++
;
/* skip reserved byte */
switch
(
rec
->
rectype
)
{
case
0
:
/* unused (free) record */
break
;
case
RECTYPE_VER
:
/* version record */
if
(
memcmp
(
buf
+
1
,
"gpg"
,
3
)
)
{
log_error_f
(
db_name
,
_
(
"not a trustdb file
\n
"
)
);
rc
=
G10ERR_TRUSTDB
;
}
p
+=
2
;
/* skip "pgp" */
rec
->
r
.
ver
.
version
=
*
p
++
;
p
+=
3
;
/* reserved bytes */
p
+=
4
;
/* lock flags */
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
.
keyhashtbl
=
buftoulong
(
p
);
p
+=
4
;
rec
->
r
.
ver
.
firstfree
=
buftoulong
(
p
);
p
+=
4
;
rec
->
r
.
ver
.
sdirhashtbl
=
buftoulong
(
p
);
p
+=
4
;
if
(
recnum
)
{
log_error_f
(
db_name
,
"version record with recnum %lu
\n
"
,
(
ulong
)
recnum
);
rc
=
G10ERR_TRUSTDB
;
}
else
if
(
rec
->
r
.
ver
.
version
!=
2
)
{
log_error_f
(
db_name
,
"invalid file version %d
\n
"
,
rec
->
r
.
ver
.
version
);
rc
=
G10ERR_TRUSTDB
;
}
break
;
case
RECTYPE_FREE
:
rec
->
r
.
free
.
next
=
buftoulong
(
p
);
p
+=
4
;
break
;
case
RECTYPE_DIR
:
/*directory record */
rec
->
r
.
dir
.
lid
=
buftoulong
(
p
);
p
+=
4
;
rec
->
r
.
dir
.
keylist
=
buftoulong
(
p
);
p
+=
4
;
rec
->
r
.
dir
.
uidlist
=
buftoulong
(
p
);
p
+=
4
;
rec
->
r
.
dir
.
cacherec
=
buftoulong
(
p
);
p
+=
4
;
rec
->
r
.
dir
.
ownertrust
=
*
p
++
;
rec
->
r
.
dir
.
dirflags
=
*
p
++
;
if
(
rec
->
r
.
dir
.
lid
!=
recnum
)
{
log_error_f
(
db_name
,
"dir LID != recnum (%lu,%lu)
\n
"
,
rec
->
r
.
dir
.
lid
,
(
ulong
)
recnum
);
rc
=
G10ERR_TRUSTDB
;
}
break
;
case
RECTYPE_KEY
:
/* public key record */
rec
->
r
.
key
.
lid
=
buftoulong
(
p
);
p
+=
4
;
rec
->
r
.
key
.
next
=
buftoulong
(
p
);
p
+=
4
;
p
+=
7
;
rec
->
r
.
key
.
keyflags
=
*
p
++
;
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
);
break
;
case
RECTYPE_UID
:
/* user id record */
rec
->
r
.
uid
.
lid
=
buftoulong
(
p
);
p
+=
4
;
rec
->
r
.
uid
.
next
=
buftoulong
(
p
);
p
+=
4
;
rec
->
r
.
uid
.
prefrec
=
buftoulong
(
p
);
p
+=
4
;
rec
->
r
.
uid
.
siglist
=
buftoulong
(
p
);
p
+=
4
;
rec
->
r
.
uid
.
uidflags
=
*
p
++
;
p
++
;
memcpy
(
rec
->
r
.
uid
.
namehash
,
p
,
20
);
break
;
case
RECTYPE_PREF
:
/* preference record */
rec
->
r
.
pref
.
lid
=
buftoulong
(
p
);
p
+=
4
;
rec
->
r
.
pref
.
next
=
buftoulong
(
p
);
p
+=
4
;
memcpy
(
rec
->
r
.
pref
.
data
,
p
,
30
);
break
;
case
RECTYPE_SIG
:
rec
->
r
.
sig
.
lid
=
buftoulong
(
p
);
p
+=
4
;
rec
->
r
.
sig
.
next
=
buftoulong
(
p
);
p
+=
4
;
for
(
i
=
0
;
i
<
SIGS_PER_RECORD
;
i
++
)
{
rec
->
r
.
sig
.
sig
[
i
].
lid
=
buftoulong
(
p
);
p
+=
4
;
rec
->
r
.
sig
.
sig
[
i
].
flag
=
*
p
++
;
}
break
;
case
RECTYPE_SDIR
:
/* shadow directory record */
rec
->
r
.
sdir
.
lid
=
buftoulong
(
p
);
p
+=
4
;
rec
->
r
.
sdir
.
keyid
[
0
]
=
buftou32
(
p
);
p
+=
4
;
rec
->
r
.
sdir
.
keyid
[
1
]
=
buftou32
(
p
);
p
+=
4
;
rec
->
r
.
sdir
.
pubkey_algo
=
*
p
++
;
p
+=
3
;
rec
->
r
.
sdir
.
hintlist
=
buftoulong
(
p
);
if
(
rec
->
r
.
sdir
.
lid
!=
recnum
)
{
log_error_f
(
db_name
,
"sdir LID != recnum (%lu,%lu)
\n
"
,
rec
->
r
.
sdir
.
lid
,
(
ulong
)
recnum
);
rc
=
G10ERR_TRUSTDB
;
}
break
;
case
RECTYPE_CACH
:
/* cache record (FIXME)*/
rec
->
r
.
cache
.
lid
=
buftoulong
(
p
);
p
+=
4
;
memcpy
(
rec
->
r
.
cache
.
blockhash
,
p
,
20
);
p
+=
20
;
rec
->
r
.
cache
.
trustlevel
=
*
p
++
;
break
;
case
RECTYPE_HTBL
:
for
(
i
=
0
;
i
<
ITEMS_PER_HTBL_RECORD
;
i
++
)
{
rec
->
r
.
htbl
.
item
[
i
]
=
buftoulong
(
p
);
p
+=
4
;
}
break
;
case
RECTYPE_HLST
:
rec
->
r
.
hlst
.
next
=
buftoulong
(
p
);
p
+=
4
;
for
(
i
=
0
;
i
<
ITEMS_PER_HLST_RECORD
;
i
++
)
{
rec
->
r
.
hlst
.
rnum
[
i
]
=
buftoulong
(
p
);
p
+=
4
;
}
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
* FIXME: create/update keyhash record.
*/
int
tdbio_write_record
(
TRUSTREC
*
rec
)
{
byte
buf
[
TRUST_RECORD_LEN
],
*
p
;
int
rc
=
0
;
int
i
,
n
;
ulong
recnum
=
rec
->
recnum
;
if
(
db_fd
==
-1
)
open_db
();
tdbio_dump_record
(
rec
,
stdout
);
memset
(
buf
,
0
,
TRUST_RECORD_LEN
);
p
=
buf
;
*
p
++
=
rec
->
rectype
;
p
++
;
switch
(
rec
->
rectype
)
{
case
0
:
/* unused record */
break
;
case
RECTYPE_VER
:
/* version record */
if
(
recnum
)
BUG
();
memcpy
(
p
-1
,
"gpg"
,
3
);
p
+=
2
;
*
p
++
=
rec
->
r
.
ver
.
version
;
p
+=
7
;
/* skip reserved bytes and lock flags */
ulongtobuf
(
p
,
rec
->
r
.
ver
.
created
);
p
+=
4
;
ulongtobuf
(
p
,
rec
->
r
.
ver
.
modified
);
p
+=
4
;
ulongtobuf
(
p
,
rec
->
r
.
ver
.
validated
);
p
+=
4
;
ulongtobuf
(
p
,
rec
->
r
.
ver
.
keyhashtbl
);
p
+=
4
;
ulongtobuf
(
p
,
rec
->
r
.
ver
.
firstfree
);
p
+=
4
;
ulongtobuf
(
p
,
rec
->
r
.
ver
.
sdirhashtbl
);
p
+=
4
;
break
;
case
RECTYPE_FREE
:
ulongtobuf
(
p
,
rec
->
r
.
free
.
next
);
p
+=
4
;
break
;
case
RECTYPE_DIR
:
/*directory record */
ulongtobuf
(
p
,
rec
->
r
.
dir
.
lid
);
p
+=
4
;
ulongtobuf
(
p
,
rec
->
r
.
dir
.
keylist
);
p
+=
4
;
ulongtobuf
(
p
,
rec
->
r
.
dir
.
uidlist
);
p
+=
4
;
ulongtobuf
(
p
,
rec
->
r
.
dir
.
cacherec
);
p
+=
4
;
*
p
++
=
rec
->
r
.
dir
.
ownertrust
;
*
p
++
=
rec
->
r
.
dir
.
dirflags
;
assert
(
rec
->
r
.
dir
.
lid
==
recnum
);
break
;
case
RECTYPE_KEY
:
ulongtobuf
(
p
,
rec
->
r
.
key
.
lid
);
p
+=
4
;
ulongtobuf
(
p
,
rec
->
r
.
key
.
next
);
p
+=
4
;
p
+=
7
;
*
p
++
=
rec
->
r
.
key
.
keyflags
;
*
p
++
=
rec
->
r
.
key
.
pubkey_algo
;
*
p
++
=
rec
->
r
.
key
.
fingerprint_len
;
memcpy
(
p
,
rec
->
r
.
key
.
fingerprint
,
20
);
p
+=
20
;
break
;
case
RECTYPE_UID
:
/* user id record */
ulongtobuf
(
p
,
rec
->
r
.
uid
.
lid
);
p
+=
4
;
ulongtobuf
(
p
,
rec
->
r
.
uid
.
next
);
p
+=
4
;
ulongtobuf
(
p
,
rec
->
r
.
uid
.
prefrec
);
p
+=
4
;
ulongtobuf
(
p
,
rec
->
r
.
uid
.
siglist
);
p
+=
4
;
*
p
++
=
rec
->
r
.
uid
.
uidflags
;
p
++
;
memcpy
(
p
,
rec
->
r
.
uid
.
namehash
,
20
);
p
+=
20
;
break
;
case
RECTYPE_PREF
:
ulongtobuf
(
p
,
rec
->
r
.
pref
.
lid
);
p
+=
4
;
ulongtobuf
(
p
,
rec
->
r
.
pref
.
next
);
p
+=
4
;
memcpy
(
p
,
rec
->
r
.
pref
.
data
,
30
);
break
;
case
RECTYPE_SIG
:
ulongtobuf
(
p
,
rec
->
r
.
sig
.
lid
);
p
+=
4
;
ulongtobuf
(
p
,
rec
->
r
.
sig
.
next
);
p
+=
4
;
for
(
i
=
0
;
i
<
SIGS_PER_RECORD
;
i
++
)
{
ulongtobuf
(
p
,
rec
->
r
.
sig
.
sig
[
i
].
lid
);
p
+=
4
;
*
p
++
=
rec
->
r
.
sig
.
sig
[
i
].
flag
;
}
break
;
case
RECTYPE_SDIR
:
ulongtobuf
(
p
,
rec
->
r
.
sdir
.
lid
);
p
+=
4
;
u32tobuf
(
p
,
rec
->
r
.
sdir
.
keyid
[
0
]
);
p
+=
4
;
u32tobuf
(
p
,
rec
->
r
.
sdir
.
keyid
[
1
]
);
p
+=
4
;
*
p
++
=
rec
->
r
.
sdir
.
pubkey_algo
;
p
+=
3
;
ulongtobuf
(
p
,
rec
->
r
.
sdir
.
hintlist
);
break
;
case
RECTYPE_CACH
:
/* FIXME*/
ulongtobuf
(
p
,
rec
->
r
.
cache
.
lid
);
p
+=
4
;
memcpy
(
p
,
rec
->
r
.
cache
.
blockhash
,
20
);
p
+=
20
;
*
p
++
=
rec
->
r
.
cache
.
trustlevel
;
break
;
case
RECTYPE_HTBL
:
for
(
i
=
0
;
i
<
ITEMS_PER_HTBL_RECORD
;
i
++
)
{
ulongtobuf
(
p
,
rec
->
r
.
htbl
.
item
[
i
]);
p
+=
4
;
}
break
;
case
RECTYPE_HLST
:
ulongtobuf
(
p
,
rec
->
r
.
hlst
.
next
);
p
+=
4
;
for
(
i
=
0
;
i
<
ITEMS_PER_HLST_RECORD
;
i
++
)
{
ulongtobuf
(
p
,
rec
->
r
.
hlst
.
rnum
[
i
]);
p
+=
4
;
}
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
;
}
else
if
(
rec
->
rectype
==
RECTYPE_KEY
)
rc
=
update_keyhashtbl
(
rec
);
else
if
(
rec
->
rectype
==
RECTYPE_SDIR
)
rc
=
update_sdirhashtbl
(
rec
);
return
rc
;
}
int
tdbio_delete_record
(
ulong
recnum
)
{
TRUSTREC
vr
,
rec
;
int
rc
;
rc
=
tdbio_read_record
(
0
,
&
vr
,
RECTYPE_VER
);
if
(
rc
)
log_fatal_f
(
db_name
,
_
(
"error reading version record: %s
\n
"
),
g10_errstr
(
rc
)
);
rec
.
recnum
=
recnum
;
rec
.
rectype
=
RECTYPE_FREE
;
rec
.
r
.
free
.
next
=
vr
.
r
.
ver
.
firstfree
;
vr
.
r
.
ver
.
firstfree
=
recnum
;
rc
=
tdbio_write_record
(
&
rec
);
if
(
!
rc
)
rc
=
tdbio_write_record
(
&
vr
);
return
rc
;
}
/****************
* create a new record and return its record number
*/
ulong
tdbio_new_recnum
()
{
off_t
offset
;
ulong
recnum
;
TRUSTREC
vr
,
rec
;
int
rc
;
/* look for unused records */
rc
=
tdbio_read_record
(
0
,
&
vr
,
RECTYPE_VER
);
if
(
rc
)
log_fatal_f
(
db_name
,
_
(
"error reading version record: %s
\n
"
),
g10_errstr
(
rc
)
);
if
(
vr
.
r
.
ver
.
firstfree
)
{
recnum
=
vr
.
r
.
ver
.
firstfree
;
rc
=
tdbio_read_record
(
recnum
,
&
rec
,
RECTYPE_FREE
);
if
(
rc
)
{
log_error_f
(
db_name
,
_
(
"error reading free record: %s
\n
"
),
g10_errstr
(
rc
)
);
return
rc
;
}
/* update dir record */
vr
.
r
.
ver
.
firstfree
=
rec
.
r
.
free
.
next
;
rc
=
tdbio_write_record
(
&
vr
);
if
(
rc
)
{
log_error_f
(
db_name
,
_
(
"error writing dir record: %s
\n
"
),
g10_errstr
(
rc
)
);
return
rc
;
}
/*zero out the new record */
memset
(
&
rec
,
0
,
sizeof
rec
);
rec
.
rectype
=
0
;
/* unused record */
rec
.
recnum
=
recnum
;
rc
=
tdbio_write_record
(
&
rec
);
if
(
rc
)
log_fatal_f
(
db_name
,
_
(
"failed to zero a record: %s
\n
"
),
g10_errstr
(
rc
));
}
else
{
/* not found, append a new record */
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
;
/* unused record */
rec
.
recnum
=
recnum
;
rc
=
tdbio_write_record
(
&
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
*/
int
tdbio_search_dir_bypk
(
PKT_public_key
*
pk
,
TRUSTREC
*
rec
)
{
byte
fingerprint
[
MAX_FINGERPRINT_LEN
];
size_t
fingerlen
;
u32
keyid
[
2
];
int
rc
;
keyid_from_pk
(
pk
,
keyid
);
fingerprint_from_pk
(
pk
,
fingerprint
,
&
fingerlen
);
rc
=
tdbio_search_dir_byfpr
(
fingerprint
,
fingerlen
,
pk
->
pubkey_algo
,
rec
);
if
(
!
rc
)
{
if
(
pk
->
local_id
&&
pk
->
local_id
!=
rec
->
recnum
)
log_error_f
(
db_name
,
"found record, but LID from memory does "
"not match recnum (%lu,%lu)
\n
"
,
pk
->
local_id
,
rec
->
recnum
);
pk
->
local_id
=
rec
->
recnum
;
}
return
rc
;
}
static
int
cmp_krec_fpr
(
void
*
dataptr
,
const
TRUSTREC
*
rec
)
{
const
struct
cmp_krec_fpr_struct
*
d
=
dataptr
;
return
rec
->
rectype
==
RECTYPE_KEY
&&
(
!
d
->
pubkey_algo
||
rec
->
r
.
key
.
pubkey_algo
==
d
->
pubkey_algo
)
&&
rec
->
r
.
key
.
fingerprint_len
==
d
->
fprlen
&&
!
memcmp
(
rec
->
r
.
key
.
fingerprint
,
d
->
fpr
,
d
->
fprlen
);
}
int
tdbio_search_dir_byfpr
(
const
byte
*
fingerprint
,
size_t
fingerlen
,
int
pubkey_algo
,
TRUSTREC
*
rec
)
{
struct
cmp_krec_fpr_struct
cmpdata
;
ulong
recnum
;
int
rc
;
assert
(
fingerlen
==
20
||
fingerlen
==
16
);
/* locate the key using the hash table */
cmpdata
.
pubkey_algo
=
pubkey_algo
;
cmpdata
.
fpr
=
fingerprint
;
cmpdata
.
fprlen
=
fingerlen
;
rc
=
lookup_hashtable
(
get_keyhashrec
(),
fingerprint
,
fingerlen
,
cmp_krec_fpr
,
&
cmpdata
,
rec
);
if
(
!
rc
)
{
recnum
=
rec
->
r
.
key
.
lid
;
/* Now read the dir record */
rc
=
tdbio_read_record
(
recnum
,
rec
,
RECTYPE_DIR
);
if
(
rc
)
log_error_f
(
db_name
,
"can't read dirrec %lu: %s
\n
"
,
recnum
,
g10_errstr
(
rc
)
);
}
return
rc
;
}
static
int
cmp_sdir
(
void
*
dataptr
,
const
TRUSTREC
*
rec
)
{
const
struct
cmp_sdir_struct
*
d
=
dataptr
;
return
rec
->
rectype
==
RECTYPE_SDIR
&&
(
!
d
->
pubkey_algo
||
rec
->
r
.
sdir
.
pubkey_algo
==
d
->
pubkey_algo
)
&&
rec
->
r
.
sdir
.
keyid
[
0
]
==
d
->
keyid
[
0
]
&&
rec
->
r
.
sdir
.
keyid
[
1
]
==
d
->
keyid
[
1
];
}
int
tdbio_search_sdir
(
u32
*
keyid
,
int
pubkey_algo
,
TRUSTREC
*
rec
)
{
struct
cmp_sdir_struct
cmpdata
;
int
rc
;
byte
key
[
8
];
/* locate the shadow dir record using the hash table */
u32tobuf
(
key
,
keyid
[
0
]
);
u32tobuf
(
key
+
4
,
keyid
[
1
]
);
cmpdata
.
pubkey_algo
=
pubkey_algo
;
cmpdata
.
keyid
[
0
]
=
keyid
[
0
];
cmpdata
.
keyid
[
1
]
=
keyid
[
1
];
rc
=
lookup_hashtable
(
get_sdirhashrec
(),
key
,
8
,
cmp_sdir
,
&
cmpdata
,
rec
);
return
rc
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Mon, Sep 15, 10:27 PM (1 d, 2 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
79/22/9c6605c96f5b5b1935d3f1af352a
Attached To
rG GnuPG
Event Timeline
Log In to Comment