Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F22067787
keyring.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
49 KB
Subscribers
None
keyring.c
View Options
/* keyring.c - keyring file handling
* Copyright (C) 1998-2010 Free Software Foundation, Inc.
* Copyright (C) 1997-2015 Werner Koch
*
* 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 3 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, see <https://www.gnu.org/licenses/>.
*/
#include
<config.h>
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#include
<errno.h>
#include
<unistd.h>
#include
<sys/types.h>
#include
<sys/stat.h>
#include
"gpg.h"
#include
"util.h"
#include
"keyring.h"
#include
"packet.h"
#include
"keydb.h"
#include
"options.h"
#include
"main.h"
/*for check_key_signature()*/
#include
"i18n.h"
#include
"../kbx/keybox.h"
typedef
struct
keyring_resource
*
KR_RESOURCE
;
struct
keyring_resource
{
struct
keyring_resource
*
next
;
int
read_only
;
dotlock_t
lockhd
;
int
is_locked
;
int
did_full_scan
;
char
fname
[
1
];
};
typedef
struct
keyring_resource
const
*
CONST_KR_RESOURCE
;
static
KR_RESOURCE
kr_resources
;
struct
keyring_handle
{
CONST_KR_RESOURCE
resource
;
struct
{
CONST_KR_RESOURCE
kr
;
IOBUF
iobuf
;
int
eof
;
int
error
;
}
current
;
struct
{
CONST_KR_RESOURCE
kr
;
off_t
offset
;
size_t
pk_no
;
size_t
uid_no
;
unsigned
int
n_packets
;
/*used for delete and update*/
}
found
,
saved_found
;
struct
{
char
*
name
;
char
*
pattern
;
}
word_match
;
};
/* The number of extant handles. */
static
int
active_handles
;
static
int
do_copy
(
int
mode
,
const
char
*
fname
,
KBNODE
root
,
off_t
start_offset
,
unsigned
int
n_packets
);
/* We keep a cache of entries that we have entered in the DB. This
includes not only public keys, but also subkeys.
Note: we'd like to keep the offset of the items that are present,
however, this doesn't work, because another concurrent GnuPG
process could modify the keyring. */
struct
key_present
{
struct
key_present
*
next
;
u32
kid
[
2
];
};
/* For the hash table, we use separate chaining with linked lists.
This means that we have an array of N linked lists (buckets), which
is indexed by KEYID[1] mod N. Elements present in the keyring will
be on the list; elements not present in the keyring will not be on
the list.
Note: since the hash table stores both present and not present
information, it cannot be used until we complete a full scan of the
keyring. This is indicated by key_present_hash_ready. */
typedef
struct
key_present
**
key_present_hash_t
;
static
key_present_hash_t
key_present_hash
;
static
int
key_present_hash_ready
;
#define KEY_PRESENT_HASH_BUCKETS 2048
/* Allocate a new value for a key present hash table. */
static
struct
key_present
*
key_present_value_new
(
void
)
{
struct
key_present
*
k
;
k
=
xmalloc_clear
(
sizeof
*
k
);
return
k
;
}
/* Allocate a new key present hash table. */
static
key_present_hash_t
key_present_hash_new
(
void
)
{
struct
key_present
**
tbl
;
tbl
=
xmalloc_clear
(
KEY_PRESENT_HASH_BUCKETS
*
sizeof
*
tbl
);
return
tbl
;
}
/* Return whether the value described by KID if it is in the hash
table. Otherwise, return NULL. */
static
struct
key_present
*
key_present_hash_lookup
(
key_present_hash_t
tbl
,
u32
*
kid
)
{
struct
key_present
*
k
;
for
(
k
=
tbl
[(
kid
[
1
]
%
(
KEY_PRESENT_HASH_BUCKETS
-
1
))];
k
;
k
=
k
->
next
)
if
(
k
->
kid
[
0
]
==
kid
[
0
]
&&
k
->
kid
[
1
]
==
kid
[
1
])
return
k
;
return
NULL
;
}
/* Add the key to the hash table TBL if it is not already present. */
static
void
key_present_hash_update
(
key_present_hash_t
tbl
,
u32
*
kid
)
{
struct
key_present
*
k
;
for
(
k
=
tbl
[(
kid
[
1
]
%
(
KEY_PRESENT_HASH_BUCKETS
-
1
))];
k
;
k
=
k
->
next
)
{
if
(
k
->
kid
[
0
]
==
kid
[
0
]
&&
k
->
kid
[
1
]
==
kid
[
1
])
return
;
}
k
=
key_present_value_new
();
k
->
kid
[
0
]
=
kid
[
0
];
k
->
kid
[
1
]
=
kid
[
1
];
k
->
next
=
tbl
[(
kid
[
1
]
%
(
KEY_PRESENT_HASH_BUCKETS
-
1
))];
tbl
[(
kid
[
1
]
%
(
KEY_PRESENT_HASH_BUCKETS
-
1
))]
=
k
;
}
/* Add all the keys (public and subkeys) present in the keyblock to
the hash TBL. */
static
void
key_present_hash_update_from_kb
(
key_present_hash_t
tbl
,
KBNODE
node
)
{
for
(;
node
;
node
=
node
->
next
)
{
if
(
node
->
pkt
->
pkttype
==
PKT_PUBLIC_KEY
||
node
->
pkt
->
pkttype
==
PKT_PUBLIC_SUBKEY
)
{
u32
aki
[
2
];
keyid_from_pk
(
node
->
pkt
->
pkt
.
public_key
,
aki
);
key_present_hash_update
(
tbl
,
aki
);
}
}
}
/*
* Register a filename for plain keyring files. ptr is set to a
* pointer to be used to create a handles etc, or the already-issued
* pointer if it has already been registered. The function returns 1
* if a new keyring was registered.
*/
int
keyring_register_filename
(
const
char
*
fname
,
int
read_only
,
void
**
ptr
)
{
KR_RESOURCE
kr
;
if
(
active_handles
)
/* There are open handles. */
BUG
();
for
(
kr
=
kr_resources
;
kr
;
kr
=
kr
->
next
)
{
if
(
same_file_p
(
kr
->
fname
,
fname
))
{
/* Already registered. */
if
(
read_only
)
kr
->
read_only
=
1
;
*
ptr
=
kr
;
return
0
;
}
}
kr
=
xmalloc
(
sizeof
*
kr
+
strlen
(
fname
));
strcpy
(
kr
->
fname
,
fname
);
kr
->
read_only
=
read_only
;
kr
->
lockhd
=
NULL
;
kr
->
is_locked
=
0
;
kr
->
did_full_scan
=
0
;
/* keep a list of all issued pointers */
kr
->
next
=
kr_resources
;
kr_resources
=
kr
;
/* create the offset table the first time a function here is used */
if
(
!
key_present_hash
)
key_present_hash
=
key_present_hash_new
();
*
ptr
=
kr
;
return
1
;
}
int
keyring_is_writable
(
void
*
token
)
{
KR_RESOURCE
r
=
token
;
return
r
?
(
r
->
read_only
||
!
access
(
r
->
fname
,
W_OK
))
:
0
;
}
/* Create a new handle for the resource associated with TOKEN.
On error NULL is returned and ERRNO is set.
The returned handle must be released using keyring_release (). */
KEYRING_HANDLE
keyring_new
(
void
*
token
)
{
KEYRING_HANDLE
hd
;
KR_RESOURCE
resource
=
token
;
log_assert
(
resource
);
hd
=
xtrycalloc
(
1
,
sizeof
*
hd
);
if
(
!
hd
)
return
hd
;
hd
->
resource
=
resource
;
active_handles
++
;
return
hd
;
}
void
keyring_release
(
KEYRING_HANDLE
hd
)
{
if
(
!
hd
)
return
;
log_assert
(
active_handles
>
0
);
active_handles
--
;
xfree
(
hd
->
word_match
.
name
);
xfree
(
hd
->
word_match
.
pattern
);
iobuf_close
(
hd
->
current
.
iobuf
);
xfree
(
hd
);
}
/* Save the current found state in HD for later retrieval by
keybox_pop_found_state. Only one state may be saved. */
void
keyring_push_found_state
(
KEYRING_HANDLE
hd
)
{
hd
->
saved_found
=
hd
->
found
;
hd
->
found
.
kr
=
NULL
;
}
/* Restore the saved found state in HD. */
void
keyring_pop_found_state
(
KEYRING_HANDLE
hd
)
{
hd
->
found
=
hd
->
saved_found
;
hd
->
saved_found
.
kr
=
NULL
;
}
const
char
*
keyring_get_resource_name
(
KEYRING_HANDLE
hd
)
{
if
(
!
hd
||
!
hd
->
resource
)
return
NULL
;
return
hd
->
resource
->
fname
;
}
/*
* Lock the keyring with the given handle, or unlock if YES is false.
* We ignore the handle and lock all registered files.
*/
int
keyring_lock
(
KEYRING_HANDLE
hd
,
int
yes
)
{
KR_RESOURCE
kr
;
int
rc
=
0
;
(
void
)
hd
;
if
(
yes
)
{
/* first make sure the lock handles are created */
for
(
kr
=
kr_resources
;
kr
;
kr
=
kr
->
next
)
{
if
(
!
keyring_is_writable
(
kr
))
continue
;
if
(
!
kr
->
lockhd
)
{
kr
->
lockhd
=
dotlock_create
(
kr
->
fname
,
0
);
if
(
!
kr
->
lockhd
)
{
log_info
(
"can't allocate lock for '%s'
\n
"
,
kr
->
fname
);
rc
=
GPG_ERR_GENERAL
;
}
}
}
if
(
rc
)
return
rc
;
/* and now set the locks */
for
(
kr
=
kr_resources
;
kr
;
kr
=
kr
->
next
)
{
if
(
!
keyring_is_writable
(
kr
))
continue
;
if
(
kr
->
is_locked
)
continue
;
#ifdef HAVE_W32_SYSTEM
/* Under Windows we need to CloseHandle the file before we
* try to lock it. This is because another process might
* have taken the lock and is using keybox_file_rename to
* rename the base file. How if our dotlock_take below is
* waiting for the lock but we have the base file still
* open, keybox_file_rename will never succeed as we are
* in a deadlock. */
iobuf_ioctl
(
NULL
,
IOBUF_IOCTL_INVALIDATE_CACHE
,
0
,
(
char
*
)
kr
->
fname
);
#endif
/*HAVE_W32_SYSTEM*/
if
(
dotlock_take
(
kr
->
lockhd
,
-1
)
)
{
log_info
(
"can't lock '%s'
\n
"
,
kr
->
fname
);
rc
=
GPG_ERR_GENERAL
;
}
else
kr
->
is_locked
=
1
;
}
}
if
(
rc
||
!
yes
)
{
for
(
kr
=
kr_resources
;
kr
;
kr
=
kr
->
next
)
{
if
(
!
keyring_is_writable
(
kr
))
continue
;
if
(
!
kr
->
is_locked
)
continue
;
if
(
dotlock_release
(
kr
->
lockhd
))
log_info
(
"can't unlock '%s'
\n
"
,
kr
->
fname
);
else
kr
->
is_locked
=
0
;
}
}
return
rc
;
}
/*
* Return the last found keyblock. Caller must free it.
* The returned keyblock has the kbode flag bit 0 set for the node with
* the public key used to locate the keyblock or flag bit 1 set for
* the user ID node.
*/
int
keyring_get_keyblock
(
KEYRING_HANDLE
hd
,
KBNODE
*
ret_kb
)
{
PACKET
*
pkt
;
int
rc
;
KBNODE
keyblock
=
NULL
,
node
,
lastnode
;
IOBUF
a
;
int
in_cert
=
0
;
int
pk_no
=
0
;
int
uid_no
=
0
;
int
save_mode
;
if
(
ret_kb
)
*
ret_kb
=
NULL
;
if
(
!
hd
->
found
.
kr
)
return
-1
;
/* no successful search */
a
=
iobuf_open
(
hd
->
found
.
kr
->
fname
);
if
(
!
a
)
{
log_error
(
_
(
"can't open '%s'
\n
"
),
hd
->
found
.
kr
->
fname
);
return
GPG_ERR_KEYRING_OPEN
;
}
if
(
iobuf_seek
(
a
,
hd
->
found
.
offset
)
)
{
log_error
(
"can't seek '%s'
\n
"
,
hd
->
found
.
kr
->
fname
);
iobuf_close
(
a
);
return
GPG_ERR_KEYRING_OPEN
;
}
pkt
=
xmalloc
(
sizeof
*
pkt
);
init_packet
(
pkt
);
hd
->
found
.
n_packets
=
0
;;
lastnode
=
NULL
;
save_mode
=
set_packet_list_mode
(
0
);
while
((
rc
=
parse_packet
(
a
,
pkt
))
!=
-1
)
{
hd
->
found
.
n_packets
++
;
if
(
gpg_err_code
(
rc
)
==
GPG_ERR_UNKNOWN_PACKET
)
{
free_packet
(
pkt
);
init_packet
(
pkt
);
continue
;
}
if
(
gpg_err_code
(
rc
)
==
GPG_ERR_LEGACY_KEY
)
{
if
(
in_cert
)
/* It is not this key that is problematic, but the
following key. */
{
rc
=
0
;
hd
->
found
.
n_packets
--
;
}
else
/* Upper layer needs to handle this. */
{
}
break
;
}
if
(
rc
)
{
log_error
(
"keyring_get_keyblock: read error: %s
\n
"
,
gpg_strerror
(
rc
)
);
rc
=
GPG_ERR_INV_KEYRING
;
break
;
}
/* Filter allowed packets. */
switch
(
pkt
->
pkttype
)
{
case
PKT_PUBLIC_KEY
:
case
PKT_PUBLIC_SUBKEY
:
case
PKT_SECRET_KEY
:
case
PKT_SECRET_SUBKEY
:
case
PKT_USER_ID
:
case
PKT_ATTRIBUTE
:
case
PKT_SIGNATURE
:
break
;
/* Allowed per RFC. */
case
PKT_RING_TRUST
:
case
PKT_OLD_COMMENT
:
case
PKT_COMMENT
:
case
PKT_GPG_CONTROL
:
break
;
/* Allowed by us. */
default
:
log_error
(
"skipped packet of type %d in keyring
\n
"
,
(
int
)
pkt
->
pkttype
);
free_packet
(
pkt
);
init_packet
(
pkt
);
continue
;
}
if
(
in_cert
&&
(
pkt
->
pkttype
==
PKT_PUBLIC_KEY
||
pkt
->
pkttype
==
PKT_SECRET_KEY
))
{
hd
->
found
.
n_packets
--
;
/* fix counter */
break
;
/* ready */
}
in_cert
=
1
;
if
(
pkt
->
pkttype
==
PKT_RING_TRUST
)
{
/*(this code is duplicated after the loop)*/
if
(
lastnode
&&
lastnode
->
pkt
->
pkttype
==
PKT_SIGNATURE
&&
(
pkt
->
pkt
.
ring_trust
->
sigcache
&
1
)
)
{
/* This is a ring trust packet with a checked signature
* status cache following directly a signature paket.
* Set the cache status into that signature packet. */
PKT_signature
*
sig
=
lastnode
->
pkt
->
pkt
.
signature
;
sig
->
flags
.
checked
=
1
;
sig
->
flags
.
valid
=
!!
(
pkt
->
pkt
.
ring_trust
->
sigcache
&
2
);
}
/* Reset LASTNODE, so that we set the cache status only from
* the ring trust packet immediately following a signature. */
lastnode
=
NULL
;
free_packet
(
pkt
);
init_packet
(
pkt
);
continue
;
}
node
=
lastnode
=
new_kbnode
(
pkt
);
if
(
!
keyblock
)
keyblock
=
node
;
else
add_kbnode
(
keyblock
,
node
);
switch
(
pkt
->
pkttype
)
{
case
PKT_PUBLIC_KEY
:
case
PKT_PUBLIC_SUBKEY
:
case
PKT_SECRET_KEY
:
case
PKT_SECRET_SUBKEY
:
if
(
++
pk_no
==
hd
->
found
.
pk_no
)
node
->
flag
|=
1
;
break
;
case
PKT_USER_ID
:
if
(
++
uid_no
==
hd
->
found
.
uid_no
)
node
->
flag
|=
2
;
break
;
default
:
break
;
}
pkt
=
xmalloc
(
sizeof
*
pkt
);
init_packet
(
pkt
);
}
set_packet_list_mode
(
save_mode
);
if
(
rc
==
-1
&&
keyblock
)
rc
=
0
;
/* got the entire keyblock */
if
(
rc
||
!
ret_kb
)
release_kbnode
(
keyblock
);
else
{
/*(duplicated from the loop body)*/
if
(
pkt
&&
pkt
->
pkttype
==
PKT_RING_TRUST
&&
lastnode
&&
lastnode
->
pkt
->
pkttype
==
PKT_SIGNATURE
&&
(
pkt
->
pkt
.
ring_trust
->
sigcache
&
1
)
)
{
PKT_signature
*
sig
=
lastnode
->
pkt
->
pkt
.
signature
;
sig
->
flags
.
checked
=
1
;
sig
->
flags
.
valid
=
!!
(
pkt
->
pkt
.
ring_trust
->
sigcache
&
2
);
}
*
ret_kb
=
keyblock
;
}
free_packet
(
pkt
);
xfree
(
pkt
);
iobuf_close
(
a
);
/* Make sure that future search operations fail immediately when
* we know that we are working on a invalid keyring
*/
if
(
gpg_err_code
(
rc
)
==
GPG_ERR_INV_KEYRING
)
hd
->
current
.
error
=
rc
;
return
rc
;
}
int
keyring_update_keyblock
(
KEYRING_HANDLE
hd
,
KBNODE
kb
)
{
int
rc
;
if
(
!
hd
->
found
.
kr
)
return
-1
;
/* no successful prior search */
if
(
hd
->
found
.
kr
->
read_only
)
return
gpg_error
(
GPG_ERR_EACCES
);
if
(
!
hd
->
found
.
n_packets
)
{
/* need to know the number of packets - do a dummy get_keyblock*/
rc
=
keyring_get_keyblock
(
hd
,
NULL
);
if
(
rc
)
{
log_error
(
"re-reading keyblock failed: %s
\n
"
,
gpg_strerror
(
rc
));
return
rc
;
}
if
(
!
hd
->
found
.
n_packets
)
BUG
();
}
/* The open iobuf isn't needed anymore and in fact is a problem when
it comes to renaming the keyring files on some operating systems,
so close it here */
iobuf_close
(
hd
->
current
.
iobuf
);
hd
->
current
.
iobuf
=
NULL
;
/* do the update */
rc
=
do_copy
(
3
,
hd
->
found
.
kr
->
fname
,
kb
,
hd
->
found
.
offset
,
hd
->
found
.
n_packets
);
if
(
!
rc
)
{
if
(
key_present_hash
)
{
key_present_hash_update_from_kb
(
key_present_hash
,
kb
);
}
/* better reset the found info */
hd
->
found
.
kr
=
NULL
;
hd
->
found
.
offset
=
0
;
}
return
rc
;
}
int
keyring_insert_keyblock
(
KEYRING_HANDLE
hd
,
KBNODE
kb
)
{
int
rc
;
const
char
*
fname
;
if
(
!
hd
)
fname
=
NULL
;
else
if
(
hd
->
found
.
kr
)
{
fname
=
hd
->
found
.
kr
->
fname
;
if
(
hd
->
found
.
kr
->
read_only
)
return
gpg_error
(
GPG_ERR_EACCES
);
}
else
if
(
hd
->
current
.
kr
)
{
fname
=
hd
->
current
.
kr
->
fname
;
if
(
hd
->
current
.
kr
->
read_only
)
return
gpg_error
(
GPG_ERR_EACCES
);
}
else
fname
=
hd
->
resource
?
hd
->
resource
->
fname
:
NULL
;
if
(
!
fname
)
return
GPG_ERR_GENERAL
;
/* Close this one otherwise we will lose the position for
* a next search. Fixme: it would be better to adjust the position
* after the write opertions.
*/
iobuf_close
(
hd
->
current
.
iobuf
);
hd
->
current
.
iobuf
=
NULL
;
/* do the insert */
rc
=
do_copy
(
1
,
fname
,
kb
,
0
,
0
);
if
(
!
rc
&&
key_present_hash
)
{
key_present_hash_update_from_kb
(
key_present_hash
,
kb
);
}
return
rc
;
}
int
keyring_delete_keyblock
(
KEYRING_HANDLE
hd
)
{
int
rc
;
if
(
!
hd
->
found
.
kr
)
return
-1
;
/* no successful prior search */
if
(
hd
->
found
.
kr
->
read_only
)
return
gpg_error
(
GPG_ERR_EACCES
);
if
(
!
hd
->
found
.
n_packets
)
{
/* need to know the number of packets - do a dummy get_keyblock*/
rc
=
keyring_get_keyblock
(
hd
,
NULL
);
if
(
rc
)
{
log_error
(
"re-reading keyblock failed: %s
\n
"
,
gpg_strerror
(
rc
));
return
rc
;
}
if
(
!
hd
->
found
.
n_packets
)
BUG
();
}
/* close this one otherwise we will lose the position for
* a next search. Fixme: it would be better to adjust the position
* after the write opertions.
*/
iobuf_close
(
hd
->
current
.
iobuf
);
hd
->
current
.
iobuf
=
NULL
;
/* do the delete */
rc
=
do_copy
(
2
,
hd
->
found
.
kr
->
fname
,
NULL
,
hd
->
found
.
offset
,
hd
->
found
.
n_packets
);
if
(
!
rc
)
{
/* better reset the found info */
hd
->
found
.
kr
=
NULL
;
hd
->
found
.
offset
=
0
;
/* Delete is a rare operations, so we don't remove the keys
* from the offset table */
}
return
rc
;
}
/*
* Start the next search on this handle right at the beginning
*/
int
keyring_search_reset
(
KEYRING_HANDLE
hd
)
{
log_assert
(
hd
);
hd
->
current
.
kr
=
NULL
;
iobuf_close
(
hd
->
current
.
iobuf
);
hd
->
current
.
iobuf
=
NULL
;
hd
->
current
.
eof
=
0
;
hd
->
current
.
error
=
0
;
hd
->
found
.
kr
=
NULL
;
hd
->
found
.
offset
=
0
;
return
0
;
}
static
int
prepare_search
(
KEYRING_HANDLE
hd
)
{
if
(
hd
->
current
.
error
)
{
/* If the last key was a legacy key, we simply ignore the error so that
we can easily use search_next. */
if
(
gpg_err_code
(
hd
->
current
.
error
)
==
GPG_ERR_LEGACY_KEY
)
{
if
(
DBG_LOOKUP
)
log_debug
(
"%s: last error was GPG_ERR_LEGACY_KEY, clearing
\n
"
,
__func__
);
hd
->
current
.
error
=
0
;
}
else
{
if
(
DBG_LOOKUP
)
log_debug
(
"%s: returning last error: %s
\n
"
,
__func__
,
gpg_strerror
(
hd
->
current
.
error
));
return
hd
->
current
.
error
;
/* still in error state */
}
}
if
(
hd
->
current
.
kr
&&
!
hd
->
current
.
eof
)
{
if
(
!
hd
->
current
.
iobuf
)
{
if
(
DBG_LOOKUP
)
log_debug
(
"%s: missing iobuf!
\n
"
,
__func__
);
return
GPG_ERR_GENERAL
;
/* Position invalid after a modify. */
}
return
0
;
/* okay */
}
if
(
!
hd
->
current
.
kr
&&
hd
->
current
.
eof
)
{
if
(
DBG_LOOKUP
)
log_debug
(
"%s: EOF!
\n
"
,
__func__
);
return
-1
;
/* still EOF */
}
if
(
!
hd
->
current
.
kr
)
{
/* start search with first keyring */
hd
->
current
.
kr
=
hd
->
resource
;
if
(
!
hd
->
current
.
kr
)
{
if
(
DBG_LOOKUP
)
log_debug
(
"%s: keyring not available!
\n
"
,
__func__
);
hd
->
current
.
eof
=
1
;
return
-1
;
/* keyring not available */
}
log_assert
(
!
hd
->
current
.
iobuf
);
}
else
{
/* EOF */
if
(
DBG_LOOKUP
)
log_debug
(
"%s: EOF
\n
"
,
__func__
);
iobuf_close
(
hd
->
current
.
iobuf
);
hd
->
current
.
iobuf
=
NULL
;
hd
->
current
.
kr
=
NULL
;
hd
->
current
.
eof
=
1
;
return
-1
;
}
hd
->
current
.
eof
=
0
;
hd
->
current
.
iobuf
=
iobuf_open
(
hd
->
current
.
kr
->
fname
);
if
(
!
hd
->
current
.
iobuf
)
{
hd
->
current
.
error
=
gpg_error_from_syserror
();
log_error
(
_
(
"can't open '%s'
\n
"
),
hd
->
current
.
kr
->
fname
);
return
hd
->
current
.
error
;
}
return
0
;
}
/* A map of the all characters valid used for word_match()
* Valid characters are in in this table converted to uppercase.
* because the upper 128 bytes have special meaning, we assume
* that they are all valid.
* Note: We must use numerical values here in case that this program
* will be converted to those little blue HAL9000s with their strange
* EBCDIC character set (user ids are UTF-8).
* wk 2000-04-13: Hmmm, does this really make sense, given the fact that
* we can run gpg now on a S/390 running GNU/Linux, where the code
* translation is done by the device drivers?
*/
static
const
byte
word_match_chars
[
256
]
=
{
/* 00 */
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
/* 08 */
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
/* 10 */
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
/* 18 */
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
/* 20 */
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
/* 28 */
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
/* 30 */
0x30
,
0x31
,
0x32
,
0x33
,
0x34
,
0x35
,
0x36
,
0x37
,
/* 38 */
0x38
,
0x39
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
/* 40 */
0x00
,
0x41
,
0x42
,
0x43
,
0x44
,
0x45
,
0x46
,
0x47
,
/* 48 */
0x48
,
0x49
,
0x4a
,
0x4b
,
0x4c
,
0x4d
,
0x4e
,
0x4f
,
/* 50 */
0x50
,
0x51
,
0x52
,
0x53
,
0x54
,
0x55
,
0x56
,
0x57
,
/* 58 */
0x58
,
0x59
,
0x5a
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
/* 60 */
0x00
,
0x41
,
0x42
,
0x43
,
0x44
,
0x45
,
0x46
,
0x47
,
/* 68 */
0x48
,
0x49
,
0x4a
,
0x4b
,
0x4c
,
0x4d
,
0x4e
,
0x4f
,
/* 70 */
0x50
,
0x51
,
0x52
,
0x53
,
0x54
,
0x55
,
0x56
,
0x57
,
/* 78 */
0x58
,
0x59
,
0x5a
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
/* 80 */
0x80
,
0x81
,
0x82
,
0x83
,
0x84
,
0x85
,
0x86
,
0x87
,
/* 88 */
0x88
,
0x89
,
0x8a
,
0x8b
,
0x8c
,
0x8d
,
0x8e
,
0x8f
,
/* 90 */
0x90
,
0x91
,
0x92
,
0x93
,
0x94
,
0x95
,
0x96
,
0x97
,
/* 98 */
0x98
,
0x99
,
0x9a
,
0x9b
,
0x9c
,
0x9d
,
0x9e
,
0x9f
,
/* a0 */
0xa0
,
0xa1
,
0xa2
,
0xa3
,
0xa4
,
0xa5
,
0xa6
,
0xa7
,
/* a8 */
0xa8
,
0xa9
,
0xaa
,
0xab
,
0xac
,
0xad
,
0xae
,
0xaf
,
/* b0 */
0xb0
,
0xb1
,
0xb2
,
0xb3
,
0xb4
,
0xb5
,
0xb6
,
0xb7
,
/* b8 */
0xb8
,
0xb9
,
0xba
,
0xbb
,
0xbc
,
0xbd
,
0xbe
,
0xbf
,
/* c0 */
0xc0
,
0xc1
,
0xc2
,
0xc3
,
0xc4
,
0xc5
,
0xc6
,
0xc7
,
/* c8 */
0xc8
,
0xc9
,
0xca
,
0xcb
,
0xcc
,
0xcd
,
0xce
,
0xcf
,
/* d0 */
0xd0
,
0xd1
,
0xd2
,
0xd3
,
0xd4
,
0xd5
,
0xd6
,
0xd7
,
/* d8 */
0xd8
,
0xd9
,
0xda
,
0xdb
,
0xdc
,
0xdd
,
0xde
,
0xdf
,
/* e0 */
0xe0
,
0xe1
,
0xe2
,
0xe3
,
0xe4
,
0xe5
,
0xe6
,
0xe7
,
/* e8 */
0xe8
,
0xe9
,
0xea
,
0xeb
,
0xec
,
0xed
,
0xee
,
0xef
,
/* f0 */
0xf0
,
0xf1
,
0xf2
,
0xf3
,
0xf4
,
0xf5
,
0xf6
,
0xf7
,
/* f8 */
0xf8
,
0xf9
,
0xfa
,
0xfb
,
0xfc
,
0xfd
,
0xfe
,
0xff
};
/****************
* Do a word match (original user id starts with a '+').
* The pattern is already tokenized to a more suitable format:
* There are only the real words in it delimited by one space
* and all converted to uppercase.
*
* Returns: 0 if all words match.
*
* Note: This algorithm is a straightforward one and not very
* fast. It works for UTF-8 strings. The uidlen should
* be removed but due to the fact that old versions of
* pgp don't use UTF-8 we still use the length; this should
* be fixed in parse-packet (and replace \0 by some special
* UTF-8 encoding)
*/
static
int
word_match
(
const
byte
*
uid
,
size_t
uidlen
,
const
byte
*
pattern
)
{
size_t
wlen
,
n
;
const
byte
*
p
;
const
byte
*
s
;
for
(
s
=
pattern
;
*
s
;
)
{
do
{
/* skip leading delimiters */
while
(
uidlen
&&
!
word_match_chars
[
*
uid
]
)
uid
++
,
uidlen
--
;
/* get length of the word */
n
=
uidlen
;
p
=
uid
;
while
(
n
&&
word_match_chars
[
*
p
]
)
p
++
,
n
--
;
wlen
=
p
-
uid
;
/* and compare against the current word from pattern */
for
(
n
=
0
,
p
=
uid
;
n
<
wlen
&&
s
[
n
]
!=
' '
&&
s
[
n
]
;
n
++
,
p
++
)
{
if
(
word_match_chars
[
*
p
]
!=
s
[
n
]
)
break
;
}
if
(
n
==
wlen
&&
(
s
[
n
]
==
' '
||
!
s
[
n
])
)
break
;
/* found */
uid
+=
wlen
;
uidlen
-=
wlen
;
}
while
(
uidlen
);
if
(
!
uidlen
)
return
-1
;
/* not found */
/* advance to next word in pattern */
for
(;
*
s
!=
' '
&&
*
s
;
s
++
)
;
if
(
*
s
)
s
++
;
}
return
0
;
/* found */
}
/****************
* prepare word word_match; that is parse the name and
* build the pattern.
* caller has to free the returned pattern
*/
static
char
*
prepare_word_match
(
const
byte
*
name
)
{
byte
*
pattern
,
*
p
;
int
c
;
/* the original length is always enough for the pattern */
p
=
pattern
=
xmalloc
(
strlen
(
name
)
+
1
);
do
{
/* skip leading delimiters */
while
(
*
name
&&
!
word_match_chars
[
*
name
]
)
name
++
;
/* copy as long as we don't have a delimiter and convert
* to uppercase.
* fixme: how can we handle utf8 uppercasing */
for
(
;
*
name
&&
(
c
=
word_match_chars
[
*
name
]);
name
++
)
*
p
++
=
c
;
*
p
++
=
' '
;
/* append pattern delimiter */
}
while
(
*
name
);
p
[
-1
]
=
0
;
/* replace last pattern delimiter by EOS */
return
pattern
;
}
static
int
compare_name
(
int
mode
,
const
char
*
name
,
const
char
*
uid
,
size_t
uidlen
)
{
int
i
;
const
char
*
s
,
*
se
;
if
(
mode
==
KEYDB_SEARCH_MODE_EXACT
)
{
for
(
i
=
0
;
name
[
i
]
&&
uidlen
;
i
++
,
uidlen
--
)
if
(
uid
[
i
]
!=
name
[
i
])
break
;
if
(
!
uidlen
&&
!
name
[
i
])
return
0
;
/* found */
}
else
if
(
mode
==
KEYDB_SEARCH_MODE_SUBSTR
)
{
if
(
ascii_memistr
(
uid
,
uidlen
,
name
))
return
0
;
}
else
if
(
mode
==
KEYDB_SEARCH_MODE_MAIL
||
mode
==
KEYDB_SEARCH_MODE_MAILSUB
||
mode
==
KEYDB_SEARCH_MODE_MAILEND
)
{
for
(
i
=
0
,
s
=
uid
;
i
<
uidlen
&&
*
s
!=
'<'
;
s
++
,
i
++
)
;
if
(
i
<
uidlen
)
{
/* skip opening delim and one char and look for the closing one*/
s
++
;
i
++
;
for
(
se
=
s
+
1
,
i
++
;
i
<
uidlen
&&
*
se
!=
'>'
;
se
++
,
i
++
)
;
if
(
i
<
uidlen
)
{
i
=
se
-
s
;
if
(
mode
==
KEYDB_SEARCH_MODE_MAIL
)
{
if
(
strlen
(
name
)
-2
==
i
&&
!
ascii_memcasecmp
(
s
,
name
+
1
,
i
)
)
return
0
;
}
else
if
(
mode
==
KEYDB_SEARCH_MODE_MAILSUB
)
{
if
(
ascii_memistr
(
s
,
i
,
name
)
)
return
0
;
}
else
{
/* email from end */
/* nyi */
}
}
}
}
else
if
(
mode
==
KEYDB_SEARCH_MODE_WORDS
)
return
word_match
(
uid
,
uidlen
,
name
);
else
BUG
();
return
-1
;
/* not found */
}
/*
* Search through the keyring(s), starting at the current position,
* for a keyblock which contains one of the keys described in the DESC array.
*/
int
keyring_search
(
KEYRING_HANDLE
hd
,
KEYDB_SEARCH_DESC
*
desc
,
size_t
ndesc
,
size_t
*
descindex
,
int
ignore_legacy
)
{
int
rc
;
PACKET
pkt
;
int
save_mode
;
off_t
offset
,
main_offset
;
size_t
n
;
int
need_uid
,
need_words
,
need_keyid
,
need_fpr
,
any_skip
;
int
pk_no
,
uid_no
;
int
initial_skip
;
int
scanned_from_start
;
int
use_key_present_hash
;
PKT_user_id
*
uid
=
NULL
;
PKT_public_key
*
pk
=
NULL
;
u32
aki
[
2
];
/* figure out what information we need */
need_uid
=
need_words
=
need_keyid
=
need_fpr
=
any_skip
=
0
;
for
(
n
=
0
;
n
<
ndesc
;
n
++
)
{
switch
(
desc
[
n
].
mode
)
{
case
KEYDB_SEARCH_MODE_EXACT
:
case
KEYDB_SEARCH_MODE_SUBSTR
:
case
KEYDB_SEARCH_MODE_MAIL
:
case
KEYDB_SEARCH_MODE_MAILSUB
:
case
KEYDB_SEARCH_MODE_MAILEND
:
need_uid
=
1
;
break
;
case
KEYDB_SEARCH_MODE_WORDS
:
need_uid
=
1
;
need_words
=
1
;
break
;
case
KEYDB_SEARCH_MODE_SHORT_KID
:
case
KEYDB_SEARCH_MODE_LONG_KID
:
need_keyid
=
1
;
break
;
case
KEYDB_SEARCH_MODE_FPR16
:
case
KEYDB_SEARCH_MODE_FPR20
:
case
KEYDB_SEARCH_MODE_FPR
:
need_fpr
=
1
;
break
;
case
KEYDB_SEARCH_MODE_FIRST
:
/* always restart the search in this mode */
keyring_search_reset
(
hd
);
break
;
default
:
break
;
}
if
(
desc
[
n
].
skipfnc
)
{
any_skip
=
1
;
need_keyid
=
1
;
}
}
if
(
DBG_LOOKUP
)
log_debug
(
"%s: need_uid = %d; need_words = %d; need_keyid = %d; need_fpr = %d; any_skip = %d
\n
"
,
__func__
,
need_uid
,
need_words
,
need_keyid
,
need_fpr
,
any_skip
);
rc
=
prepare_search
(
hd
);
if
(
rc
)
{
if
(
DBG_LOOKUP
)
log_debug
(
"%s: prepare_search failed: %s (%d)
\n
"
,
__func__
,
gpg_strerror
(
rc
),
gpg_err_code
(
rc
));
return
rc
;
}
use_key_present_hash
=
!!
key_present_hash
;
if
(
!
use_key_present_hash
)
{
if
(
DBG_LOOKUP
)
log_debug
(
"%s: no offset table.
\n
"
,
__func__
);
}
else
if
(
!
key_present_hash_ready
)
{
if
(
DBG_LOOKUP
)
log_debug
(
"%s: initializing offset table. (need_keyid: %d => 1)
\n
"
,
__func__
,
need_keyid
);
need_keyid
=
1
;
}
else
if
(
ndesc
==
1
&&
desc
[
0
].
mode
==
KEYDB_SEARCH_MODE_LONG_KID
)
{
struct
key_present
*
oi
;
if
(
DBG_LOOKUP
)
log_debug
(
"%s: look up by long key id, checking cache
\n
"
,
__func__
);
oi
=
key_present_hash_lookup
(
key_present_hash
,
desc
[
0
].
u
.
kid
);
if
(
!
oi
)
{
/* We know that we don't have this key */
if
(
DBG_LOOKUP
)
log_debug
(
"%s: cache says not present
\n
"
,
__func__
);
hd
->
found
.
kr
=
NULL
;
hd
->
current
.
eof
=
1
;
return
-1
;
}
/* We could now create a positive search status and return.
* However the problem is that another instance of gpg may
* have changed the keyring so that the offsets are not valid
* anymore - therefore we don't do it
*/
}
if
(
need_words
)
{
const
char
*
name
=
NULL
;
log_debug
(
"word search mode does not yet work
\n
"
);
/* FIXME: here is a long standing bug in our function and in addition we
just use the first search description */
for
(
n
=
0
;
n
<
ndesc
&&
!
name
;
n
++
)
{
if
(
desc
[
n
].
mode
==
KEYDB_SEARCH_MODE_WORDS
)
name
=
desc
[
n
].
u
.
name
;
}
log_assert
(
name
);
if
(
!
hd
->
word_match
.
name
||
strcmp
(
hd
->
word_match
.
name
,
name
)
)
{
/* name changed */
xfree
(
hd
->
word_match
.
name
);
xfree
(
hd
->
word_match
.
pattern
);
hd
->
word_match
.
name
=
xstrdup
(
name
);
hd
->
word_match
.
pattern
=
prepare_word_match
(
name
);
}
/* name = hd->word_match.pattern; */
}
init_packet
(
&
pkt
);
save_mode
=
set_packet_list_mode
(
0
);
hd
->
found
.
kr
=
NULL
;
main_offset
=
0
;
pk_no
=
uid_no
=
0
;
initial_skip
=
1
;
/* skip until we see the start of a keyblock */
scanned_from_start
=
iobuf_tell
(
hd
->
current
.
iobuf
)
==
0
;
if
(
DBG_LOOKUP
)
log_debug
(
"%s: %ssearching from start of resource.
\n
"
,
__func__
,
scanned_from_start
?
""
:
"not "
);
while
(
1
)
{
byte
afp
[
MAX_FINGERPRINT_LEN
];
size_t
an
;
rc
=
search_packet
(
hd
->
current
.
iobuf
,
&
pkt
,
&
offset
,
need_uid
);
if
(
ignore_legacy
&&
gpg_err_code
(
rc
)
==
GPG_ERR_LEGACY_KEY
)
{
free_packet
(
&
pkt
);
continue
;
}
if
(
rc
)
break
;
if
(
pkt
.
pkttype
==
PKT_PUBLIC_KEY
||
pkt
.
pkttype
==
PKT_SECRET_KEY
)
{
main_offset
=
offset
;
pk_no
=
uid_no
=
0
;
initial_skip
=
0
;
}
if
(
initial_skip
)
{
free_packet
(
&
pkt
);
continue
;
}
pk
=
NULL
;
uid
=
NULL
;
if
(
pkt
.
pkttype
==
PKT_PUBLIC_KEY
||
pkt
.
pkttype
==
PKT_PUBLIC_SUBKEY
||
pkt
.
pkttype
==
PKT_SECRET_KEY
||
pkt
.
pkttype
==
PKT_SECRET_SUBKEY
)
{
pk
=
pkt
.
pkt
.
public_key
;
++
pk_no
;
if
(
need_fpr
)
{
fingerprint_from_pk
(
pk
,
afp
,
&
an
);
while
(
an
<
20
)
/* fill up to 20 bytes */
afp
[
an
++
]
=
0
;
}
if
(
need_keyid
)
keyid_from_pk
(
pk
,
aki
);
if
(
use_key_present_hash
&&
!
key_present_hash_ready
&&
scanned_from_start
)
key_present_hash_update
(
key_present_hash
,
aki
);
}
else
if
(
pkt
.
pkttype
==
PKT_USER_ID
)
{
uid
=
pkt
.
pkt
.
user_id
;
++
uid_no
;
}
for
(
n
=
0
;
n
<
ndesc
;
n
++
)
{
switch
(
desc
[
n
].
mode
)
{
case
KEYDB_SEARCH_MODE_NONE
:
BUG
();
break
;
case
KEYDB_SEARCH_MODE_EXACT
:
case
KEYDB_SEARCH_MODE_SUBSTR
:
case
KEYDB_SEARCH_MODE_MAIL
:
case
KEYDB_SEARCH_MODE_MAILSUB
:
case
KEYDB_SEARCH_MODE_MAILEND
:
case
KEYDB_SEARCH_MODE_WORDS
:
if
(
uid
&&
!
compare_name
(
desc
[
n
].
mode
,
desc
[
n
].
u
.
name
,
uid
->
name
,
uid
->
len
))
goto
found
;
break
;
case
KEYDB_SEARCH_MODE_SHORT_KID
:
if
(
pk
&&
desc
[
n
].
u
.
kid
[
1
]
==
aki
[
1
])
goto
found
;
break
;
case
KEYDB_SEARCH_MODE_LONG_KID
:
if
(
pk
&&
desc
[
n
].
u
.
kid
[
0
]
==
aki
[
0
]
&&
desc
[
n
].
u
.
kid
[
1
]
==
aki
[
1
])
goto
found
;
break
;
case
KEYDB_SEARCH_MODE_FPR16
:
if
(
pk
&&
!
memcmp
(
desc
[
n
].
u
.
fpr
,
afp
,
16
))
goto
found
;
break
;
case
KEYDB_SEARCH_MODE_FPR20
:
case
KEYDB_SEARCH_MODE_FPR
:
if
(
pk
&&
!
memcmp
(
desc
[
n
].
u
.
fpr
,
afp
,
20
))
goto
found
;
break
;
case
KEYDB_SEARCH_MODE_FIRST
:
if
(
pk
)
goto
found
;
break
;
case
KEYDB_SEARCH_MODE_NEXT
:
if
(
pk
)
goto
found
;
break
;
default
:
rc
=
GPG_ERR_INV_ARG
;
goto
found
;
}
}
free_packet
(
&
pkt
);
continue
;
found
:
if
(
rc
)
goto
real_found
;
if
(
DBG_LOOKUP
)
log_debug
(
"%s: packet starting at offset %lld matched descriptor %zu
\n
"
,
__func__
,
(
long
long
)
offset
,
n
);
/* Record which desc we matched on. Note this value is only
meaningful if this function returns with no errors. */
if
(
descindex
)
*
descindex
=
n
;
for
(
n
=
any_skip
?
0
:
ndesc
;
n
<
ndesc
;
n
++
)
{
if
(
desc
[
n
].
skipfnc
&&
desc
[
n
].
skipfnc
(
desc
[
n
].
skipfncvalue
,
aki
,
uid_no
))
{
if
(
DBG_LOOKUP
)
log_debug
(
"%s: skipping match: desc %zd's skip function returned TRUE
\n
"
,
__func__
,
n
);
break
;
}
}
if
(
n
==
ndesc
)
goto
real_found
;
free_packet
(
&
pkt
);
}
real_found
:
if
(
!
rc
)
{
if
(
DBG_LOOKUP
)
log_debug
(
"%s: returning success
\n
"
,
__func__
);
hd
->
found
.
offset
=
main_offset
;
hd
->
found
.
kr
=
hd
->
current
.
kr
;
hd
->
found
.
pk_no
=
pk
?
pk_no
:
0
;
hd
->
found
.
uid_no
=
uid
?
uid_no
:
0
;
}
else
if
(
rc
==
-1
)
{
if
(
DBG_LOOKUP
)
log_debug
(
"%s: no matches (EOF)
\n
"
,
__func__
);
hd
->
current
.
eof
=
1
;
/* if we scanned all keyrings, we are sure that
* all known key IDs are in our offtbl, mark that. */
if
(
use_key_present_hash
&&
!
key_present_hash_ready
&&
scanned_from_start
)
{
KR_RESOURCE
kr
;
/* First set the did_full_scan flag for this keyring. */
for
(
kr
=
kr_resources
;
kr
;
kr
=
kr
->
next
)
{
if
(
hd
->
resource
==
kr
)
{
kr
->
did_full_scan
=
1
;
break
;
}
}
/* Then check whether all flags are set and if so, mark the
offtbl ready */
for
(
kr
=
kr_resources
;
kr
;
kr
=
kr
->
next
)
{
if
(
!
kr
->
did_full_scan
)
break
;
}
if
(
!
kr
)
key_present_hash_ready
=
1
;
}
}
else
{
if
(
DBG_LOOKUP
)
log_debug
(
"%s: error encountered during search: %s (%d)
\n
"
,
__func__
,
gpg_strerror
(
rc
),
rc
);
hd
->
current
.
error
=
rc
;
}
free_packet
(
&
pkt
);
set_packet_list_mode
(
save_mode
);
return
rc
;
}
static
int
create_tmp_file
(
const
char
*
template
,
char
**
r_bakfname
,
char
**
r_tmpfname
,
IOBUF
*
r_fp
)
{
gpg_error_t
err
;
mode_t
oldmask
;
err
=
keybox_tmp_names
(
template
,
1
,
r_bakfname
,
r_tmpfname
);
if
(
err
)
return
err
;
/* Create the temp file with limited access. Note that the umask
call is not anymore needed because iobuf_create now takes care of
it. However, it does not harm and thus we keep it. */
oldmask
=
umask
(
077
);
if
(
is_secured_filename
(
*
r_tmpfname
))
{
*
r_fp
=
NULL
;
gpg_err_set_errno
(
EPERM
);
}
else
*
r_fp
=
iobuf_create
(
*
r_tmpfname
,
1
);
umask
(
oldmask
);
if
(
!*
r_fp
)
{
err
=
gpg_error_from_syserror
();
log_error
(
_
(
"can't create '%s': %s
\n
"
),
*
r_tmpfname
,
gpg_strerror
(
err
));
xfree
(
*
r_tmpfname
);
*
r_tmpfname
=
NULL
;
xfree
(
*
r_bakfname
);
*
r_bakfname
=
NULL
;
}
return
err
;
}
static
int
rename_tmp_file
(
const
char
*
bakfname
,
const
char
*
tmpfname
,
const
char
*
fname
)
{
int
rc
=
0
;
int
block
=
0
;
/* Invalidate close caches. */
if
(
iobuf_ioctl
(
NULL
,
IOBUF_IOCTL_INVALIDATE_CACHE
,
0
,
(
char
*
)
tmpfname
))
{
rc
=
gpg_error_from_syserror
();
goto
fail
;
}
iobuf_ioctl
(
NULL
,
IOBUF_IOCTL_INVALIDATE_CACHE
,
0
,
(
char
*
)
bakfname
);
iobuf_ioctl
(
NULL
,
IOBUF_IOCTL_INVALIDATE_CACHE
,
0
,
(
char
*
)
fname
);
/* First make a backup file. */
block
=
1
;
rc
=
gnupg_rename_file
(
fname
,
bakfname
,
&
block
);
if
(
rc
)
goto
fail
;
/* then rename the file */
rc
=
gnupg_rename_file
(
tmpfname
,
fname
,
NULL
);
if
(
block
)
{
gnupg_unblock_all_signals
();
block
=
0
;
}
if
(
rc
)
{
register_secured_file
(
fname
);
goto
fail
;
}
/* Now make sure the file has the same permissions as the original */
#ifndef HAVE_DOSISH_SYSTEM
{
struct
stat
statbuf
;
statbuf
.
st_mode
=
S_IRUSR
|
S_IWUSR
;
if
(
!
stat
(
bakfname
,
&
statbuf
)
&&
!
chmod
(
fname
,
statbuf
.
st_mode
))
;
else
log_error
(
"WARNING: unable to restore permissions to '%s': %s"
,
fname
,
strerror
(
errno
));
}
#endif
return
0
;
fail
:
if
(
block
)
gnupg_unblock_all_signals
();
return
rc
;
}
static
int
write_keyblock
(
IOBUF
fp
,
KBNODE
keyblock
)
{
KBNODE
kbctx
=
NULL
,
node
;
int
rc
;
while
(
(
node
=
walk_kbnode
(
keyblock
,
&
kbctx
,
0
))
)
{
if
(
node
->
pkt
->
pkttype
==
PKT_RING_TRUST
)
continue
;
/* we write it later on our own */
if
(
(
rc
=
build_packet
(
fp
,
node
->
pkt
)
))
{
log_error
(
"build_packet(%d) failed: %s
\n
"
,
node
->
pkt
->
pkttype
,
gpg_strerror
(
rc
)
);
return
rc
;
}
if
(
node
->
pkt
->
pkttype
==
PKT_SIGNATURE
)
{
/* always write a signature cache packet */
PKT_signature
*
sig
=
node
->
pkt
->
pkt
.
signature
;
unsigned
int
cacheval
=
0
;
if
(
sig
->
flags
.
checked
)
{
cacheval
|=
1
;
if
(
sig
->
flags
.
valid
)
cacheval
|=
2
;
}
iobuf_put
(
fp
,
0xb0
);
/* old style packet 12, 1 byte len*/
iobuf_put
(
fp
,
2
);
/* 2 bytes */
iobuf_put
(
fp
,
0
);
/* unused */
if
(
iobuf_put
(
fp
,
cacheval
))
{
rc
=
gpg_error_from_syserror
();
log_error
(
"writing sigcache packet failed
\n
"
);
return
rc
;
}
}
}
return
0
;
}
/*
* Walk over all public keyrings, check the signatures and replace the
* keyring with a new one where the signature cache is then updated.
* This is only done for the public keyrings.
*/
int
keyring_rebuild_cache
(
void
*
token
,
int
noisy
)
{
KEYRING_HANDLE
hd
;
KEYDB_SEARCH_DESC
desc
;
KBNODE
keyblock
=
NULL
,
node
;
const
char
*
lastresname
=
NULL
,
*
resname
;
IOBUF
tmpfp
=
NULL
;
char
*
tmpfilename
=
NULL
;
char
*
bakfilename
=
NULL
;
int
rc
;
ulong
count
=
0
,
sigcount
=
0
;
hd
=
keyring_new
(
token
);
if
(
!
hd
)
return
gpg_error_from_syserror
();
memset
(
&
desc
,
0
,
sizeof
desc
);
desc
.
mode
=
KEYDB_SEARCH_MODE_FIRST
;
rc
=
keyring_lock
(
hd
,
1
);
if
(
rc
)
goto
leave
;
for
(;;)
{
rc
=
keyring_search
(
hd
,
&
desc
,
1
,
NULL
,
1
/* ignore_legacy */
);
if
(
rc
)
break
;
/* ready. */
desc
.
mode
=
KEYDB_SEARCH_MODE_NEXT
;
resname
=
keyring_get_resource_name
(
hd
);
if
(
lastresname
!=
resname
)
{
/* we have switched to a new keyring - commit changes */
if
(
tmpfp
)
{
if
(
iobuf_close
(
tmpfp
))
{
rc
=
gpg_error_from_syserror
();
log_error
(
"error closing '%s': %s
\n
"
,
tmpfilename
,
strerror
(
errno
));
goto
leave
;
}
/* because we have switched resources, we can be sure that
* the original file is closed */
tmpfp
=
NULL
;
}
/* Static analyzer note: BAKFILENAME is never NULL here
because it is controlled by LASTRESNAME. */
rc
=
lastresname
?
rename_tmp_file
(
bakfilename
,
tmpfilename
,
lastresname
)
:
0
;
xfree
(
tmpfilename
);
tmpfilename
=
NULL
;
xfree
(
bakfilename
);
bakfilename
=
NULL
;
if
(
rc
)
goto
leave
;
lastresname
=
resname
;
if
(
noisy
&&
!
opt
.
quiet
)
log_info
(
_
(
"caching keyring '%s'
\n
"
),
resname
);
rc
=
create_tmp_file
(
resname
,
&
bakfilename
,
&
tmpfilename
,
&
tmpfp
);
if
(
rc
)
goto
leave
;
}
release_kbnode
(
keyblock
);
rc
=
keyring_get_keyblock
(
hd
,
&
keyblock
);
if
(
rc
)
{
if
(
gpg_err_code
(
rc
)
==
GPG_ERR_LEGACY_KEY
)
continue
;
/* Skip legacy keys. */
log_error
(
"keyring_get_keyblock failed: %s
\n
"
,
gpg_strerror
(
rc
));
goto
leave
;
}
if
(
keyblock
->
pkt
->
pkttype
!=
PKT_PUBLIC_KEY
)
{
/* We had a few reports about corrupted keyrings; if we have
been called directly from the command line we delete such
a keyblock instead of bailing out. */
log_error
(
"unexpected keyblock found (pkttype=%d)%s
\n
"
,
keyblock
->
pkt
->
pkttype
,
noisy
?
" - deleted"
:
""
);
if
(
noisy
)
continue
;
log_info
(
"Hint: backup your keys and try running '%s'
\n
"
,
"gpg --rebuild-keydb-caches"
);
rc
=
gpg_error
(
GPG_ERR_INV_KEYRING
);
goto
leave
;
}
if
(
keyblock
->
pkt
->
pkt
.
public_key
->
version
<
4
)
{
/* We do not copy/cache v3 keys or any other unknown
packets. It is better to remove them from the keyring.
The code required to keep them in the keyring would be
too complicated. Given that we do not touch the old
secring.gpg a suitable backup for decryption of v3 stuff
using an older gpg version will always be available.
Note: This test is actually superfluous because we
already acted upon GPG_ERR_LEGACY_KEY. */
}
else
{
/* Check all signature to set the signature's cache flags. */
for
(
node
=
keyblock
;
node
;
node
=
node
->
next
)
{
/* Note that this doesn't cache the result of a
revocation issued by a designated revoker. This is
because the pk in question does not carry the revkeys
as we haven't merged the key and selfsigs. It is
questionable whether this matters very much since
there are very very few designated revoker revocation
packets out there. */
if
(
node
->
pkt
->
pkttype
==
PKT_SIGNATURE
)
{
PKT_signature
*
sig
=
node
->
pkt
->
pkt
.
signature
;
if
(
!
opt
.
no_sig_cache
&&
sig
->
flags
.
checked
&&
sig
->
flags
.
valid
&&
(
openpgp_md_test_algo
(
sig
->
digest_algo
)
||
openpgp_pk_test_algo
(
sig
->
pubkey_algo
)))
sig
->
flags
.
checked
=
sig
->
flags
.
valid
=
0
;
else
check_key_signature
(
keyblock
,
node
,
NULL
);
sigcount
++
;
}
}
/* Write the keyblock to the temporary file. */
rc
=
write_keyblock
(
tmpfp
,
keyblock
);
if
(
rc
)
goto
leave
;
if
(
!
(
++
count
%
50
)
&&
noisy
&&
!
opt
.
quiet
)
log_info
(
ngettext
(
"%lu keys cached so far (%lu signature)
\n
"
,
"%lu keys cached so far (%lu signatures)
\n
"
,
sigcount
),
count
,
sigcount
);
}
}
/* end main loop */
if
(
rc
==
-1
)
rc
=
0
;
if
(
rc
)
{
log_error
(
"keyring_search failed: %s
\n
"
,
gpg_strerror
(
rc
));
goto
leave
;
}
if
(
noisy
||
opt
.
verbose
)
{
log_info
(
ngettext
(
"%lu key cached"
,
"%lu keys cached"
,
count
),
count
);
log_printf
(
ngettext
(
" (%lu signature)
\n
"
,
" (%lu signatures)
\n
"
,
sigcount
),
sigcount
);
}
if
(
tmpfp
)
{
if
(
iobuf_close
(
tmpfp
))
{
rc
=
gpg_error_from_syserror
();
log_error
(
"error closing '%s': %s
\n
"
,
tmpfilename
,
strerror
(
errno
));
goto
leave
;
}
/* because we have switched resources, we can be sure that
* the original file is closed */
tmpfp
=
NULL
;
}
rc
=
lastresname
?
rename_tmp_file
(
bakfilename
,
tmpfilename
,
lastresname
)
:
0
;
xfree
(
tmpfilename
);
tmpfilename
=
NULL
;
xfree
(
bakfilename
);
bakfilename
=
NULL
;
leave
:
if
(
tmpfp
)
iobuf_cancel
(
tmpfp
);
xfree
(
tmpfilename
);
xfree
(
bakfilename
);
release_kbnode
(
keyblock
);
keyring_lock
(
hd
,
0
);
keyring_release
(
hd
);
return
rc
;
}
/****************
* Perform insert/delete/update operation.
* mode 1 = insert
* 2 = delete
* 3 = update
*/
static
int
do_copy
(
int
mode
,
const
char
*
fname
,
KBNODE
root
,
off_t
start_offset
,
unsigned
int
n_packets
)
{
IOBUF
fp
,
newfp
;
int
rc
=
0
;
char
*
bakfname
=
NULL
;
char
*
tmpfname
=
NULL
;
/* Open the source file. Because we do a rename, we have to check the
permissions of the file */
if
(
access
(
fname
,
W_OK
))
return
gpg_error_from_syserror
();
fp
=
iobuf_open
(
fname
);
if
(
mode
==
1
&&
!
fp
&&
errno
==
ENOENT
)
{
/* insert mode but file does not exist: create a new file */
KBNODE
kbctx
,
node
;
mode_t
oldmask
;
oldmask
=
umask
(
077
);
if
(
is_secured_filename
(
fname
))
{
newfp
=
NULL
;
gpg_err_set_errno
(
EPERM
);
}
else
newfp
=
iobuf_create
(
fname
,
1
);
umask
(
oldmask
);
if
(
!
newfp
)
{
rc
=
gpg_error_from_syserror
();
log_error
(
_
(
"can't create '%s': %s
\n
"
),
fname
,
strerror
(
errno
));
return
rc
;
}
if
(
!
opt
.
quiet
)
log_info
(
_
(
"%s: keyring created
\n
"
),
fname
);
kbctx
=
NULL
;
while
(
(
node
=
walk_kbnode
(
root
,
&
kbctx
,
0
))
)
{
if
(
(
rc
=
build_packet
(
newfp
,
node
->
pkt
))
)
{
log_error
(
"build_packet(%d) failed: %s
\n
"
,
node
->
pkt
->
pkttype
,
gpg_strerror
(
rc
)
);
iobuf_cancel
(
newfp
);
return
rc
;
}
}
if
(
iobuf_close
(
newfp
)
)
{
rc
=
gpg_error_from_syserror
();
log_error
(
"%s: close failed: %s
\n
"
,
fname
,
strerror
(
errno
));
return
rc
;
}
return
0
;
/* ready */
}
if
(
!
fp
)
{
rc
=
gpg_error_from_syserror
();
log_error
(
_
(
"can't open '%s': %s
\n
"
),
fname
,
strerror
(
errno
)
);
goto
leave
;
}
/* Create the new file. */
rc
=
create_tmp_file
(
fname
,
&
bakfname
,
&
tmpfname
,
&
newfp
);
if
(
rc
)
{
iobuf_close
(
fp
);
goto
leave
;
}
if
(
mode
==
1
)
{
/* insert */
/* copy everything to the new file */
rc
=
copy_all_packets
(
fp
,
newfp
);
if
(
rc
!=
-1
)
{
log_error
(
"%s: copy to '%s' failed: %s
\n
"
,
fname
,
tmpfname
,
gpg_strerror
(
rc
)
);
iobuf_close
(
fp
);
iobuf_cancel
(
newfp
);
goto
leave
;
}
}
if
(
mode
==
2
||
mode
==
3
)
{
/* delete or update */
/* copy first part to the new file */
rc
=
copy_some_packets
(
fp
,
newfp
,
start_offset
);
if
(
rc
)
{
/* should never get EOF here */
log_error
(
"%s: copy to '%s' failed: %s
\n
"
,
fname
,
tmpfname
,
gpg_strerror
(
rc
)
);
iobuf_close
(
fp
);
iobuf_cancel
(
newfp
);
goto
leave
;
}
/* skip this keyblock */
log_assert
(
n_packets
);
rc
=
skip_some_packets
(
fp
,
n_packets
);
if
(
rc
)
{
log_error
(
"%s: skipping %u packets failed: %s
\n
"
,
fname
,
n_packets
,
gpg_strerror
(
rc
));
iobuf_close
(
fp
);
iobuf_cancel
(
newfp
);
goto
leave
;
}
}
if
(
mode
==
1
||
mode
==
3
)
{
/* insert or update */
rc
=
write_keyblock
(
newfp
,
root
);
if
(
rc
)
{
iobuf_close
(
fp
);
iobuf_cancel
(
newfp
);
goto
leave
;
}
}
if
(
mode
==
2
||
mode
==
3
)
{
/* delete or update */
/* copy the rest */
rc
=
copy_all_packets
(
fp
,
newfp
);
if
(
rc
!=
-1
)
{
log_error
(
"%s: copy to '%s' failed: %s
\n
"
,
fname
,
tmpfname
,
gpg_strerror
(
rc
)
);
iobuf_close
(
fp
);
iobuf_cancel
(
newfp
);
goto
leave
;
}
}
/* close both files */
if
(
iobuf_close
(
fp
)
)
{
rc
=
gpg_error_from_syserror
();
log_error
(
"%s: close failed: %s
\n
"
,
fname
,
strerror
(
errno
)
);
goto
leave
;
}
if
(
iobuf_close
(
newfp
)
)
{
rc
=
gpg_error_from_syserror
();
log_error
(
"%s: close failed: %s
\n
"
,
tmpfname
,
strerror
(
errno
)
);
goto
leave
;
}
rc
=
rename_tmp_file
(
bakfname
,
tmpfname
,
fname
);
leave
:
xfree
(
bakfname
);
xfree
(
tmpfname
);
return
rc
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Tue, Apr 22, 3:57 AM (4 h, 12 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
73/06/aec9d61dd0ae925f646cd6b1d841
Attached To
rG GnuPG
Event Timeline
Log In to Comment