Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34113523
keyserver.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
47 KB
Subscribers
None
keyserver.c
View Options
/* keyserver.c - generic keyserver code
* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
* 2009, 2011 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 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 <http://www.gnu.org/licenses/>.
*/
#include
<config.h>
#include
<ctype.h>
#include
<stdio.h>
#include
<string.h>
#include
<stdlib.h>
#include
<assert.h>
#include
<errno.h>
#include
"gpg.h"
#include
"iobuf.h"
#include
"filter.h"
#include
"keydb.h"
#include
"status.h"
#include
"exec.h"
#include
"main.h"
#include
"i18n.h"
#include
"ttyio.h"
#include
"options.h"
#include
"packet.h"
#include
"trustdb.h"
#include
"keyserver-internal.h"
#include
"util.h"
#include
"dns-cert.h"
#include
"pka.h"
#ifdef USE_DNS_SRV
#include
"srv.h"
#endif
#include
"membuf.h"
#include
"call-dirmngr.h"
#ifdef HAVE_W32_SYSTEM
/* It seems Vista doesn't grok X_OK and so fails access() tests.
Previous versions interpreted X_OK as F_OK anyway, so we'll just
use F_OK directly. */
#undef X_OK
#define X_OK F_OK
#endif
/* HAVE_W32_SYSTEM */
struct
keyrec
{
KEYDB_SEARCH_DESC
desc
;
u32
createtime
,
expiretime
;
int
size
,
flags
;
byte
type
;
IOBUF
uidbuf
;
unsigned
int
lines
;
};
/* Parameters for the search line handler. */
struct
search_line_handler_parm_s
{
ctrl_t
ctrl
;
/* The session control structure. */
char
*
searchstr_disp
;
/* Native encoded search string or NULL. */
KEYDB_SEARCH_DESC
*
desc
;
/* Array with search descriptions. */
int
count
;
/* Number of keys we are currently prepared to
handle. This is the size of the DESC array. If
it is too small, it will grow safely. */
int
validcount
;
/* Enable the "Key x-y of z" messages. */
int
nkeys
;
/* Number of processed records. */
int
any_lines
;
/* At least one line has been processed. */
unsigned
int
numlines
;
/* Counter for displayed lines. */
int
eof_seen
;
/* EOF encountered. */
int
not_found
;
/* Set if no keys have been found. */
};
enum
ks_action
{
KS_UNKNOWN
=
0
,
KS_GET
,
KS_GETNAME
,
KS_SEND
,
KS_SEARCH
};
static
struct
parse_options
keyserver_opts
[]
=
{
/* some of these options are not real - just for the help
message */
{
"max-cert-size"
,
0
,
NULL
,
NULL
},
{
"include-revoked"
,
0
,
NULL
,
N_
(
"include revoked keys in search results"
)},
{
"include-subkeys"
,
0
,
NULL
,
N_
(
"include subkeys when searching by key ID"
)},
{
"use-temp-files"
,
0
,
NULL
,
N_
(
"use temporary files to pass data to keyserver helpers"
)},
{
"keep-temp-files"
,
KEYSERVER_KEEP_TEMP_FILES
,
NULL
,
N_
(
"do not delete temporary files after using them"
)},
{
"refresh-add-fake-v3-keyids"
,
KEYSERVER_ADD_FAKE_V3
,
NULL
,
NULL
},
{
"auto-key-retrieve"
,
KEYSERVER_AUTO_KEY_RETRIEVE
,
NULL
,
N_
(
"automatically retrieve keys when verifying signatures"
)},
{
"honor-keyserver-url"
,
KEYSERVER_HONOR_KEYSERVER_URL
,
NULL
,
N_
(
"honor the preferred keyserver URL set on the key"
)},
{
"honor-pka-record"
,
KEYSERVER_HONOR_PKA_RECORD
,
NULL
,
N_
(
"honor the PKA record set on a key when retrieving keys"
)},
{
NULL
,
0
,
NULL
,
NULL
}
};
static
gpg_error_t
keyserver_get
(
ctrl_t
ctrl
,
KEYDB_SEARCH_DESC
*
desc
,
int
ndesc
,
struct
keyserver_spec
*
keyserver
);
static
gpg_error_t
keyserver_put
(
ctrl_t
ctrl
,
strlist_t
keyspecs
,
struct
keyserver_spec
*
keyserver
);
/* Reasonable guess */
#define DEFAULT_MAX_CERT_SIZE 16384
static
size_t
max_cert_size
=
DEFAULT_MAX_CERT_SIZE
;
static
void
add_canonical_option
(
char
*
option
,
strlist_t
*
list
)
{
char
*
arg
=
argsplit
(
option
);
if
(
arg
)
{
char
*
joined
;
joined
=
xmalloc
(
strlen
(
option
)
+
1
+
strlen
(
arg
)
+
1
);
/* Make a canonical name=value form with no spaces */
strcpy
(
joined
,
option
);
strcat
(
joined
,
"="
);
strcat
(
joined
,
arg
);
append_to_strlist
(
list
,
joined
);
xfree
(
joined
);
}
else
append_to_strlist
(
list
,
option
);
}
int
parse_keyserver_options
(
char
*
options
)
{
int
ret
=
1
;
char
*
tok
;
char
*
max_cert
=
NULL
;
keyserver_opts
[
0
].
value
=&
max_cert
;
while
((
tok
=
optsep
(
&
options
)))
{
if
(
tok
[
0
]
==
'\0'
)
continue
;
/* For backwards compatibility. 1.2.x used honor-http-proxy and
there are a good number of documents published that recommend
it. */
if
(
ascii_strcasecmp
(
tok
,
"honor-http-proxy"
)
==
0
)
tok
=
"http-proxy"
;
else
if
(
ascii_strcasecmp
(
tok
,
"no-honor-http-proxy"
)
==
0
)
tok
=
"no-http-proxy"
;
/* We accept quite a few possible options here - some options to
handle specially, the keyserver_options list, and import and
export options that pertain to keyserver operations. Note
that you must use strncasecmp here as there might be an
=argument attached which will foil the use of strcasecmp. */
#ifdef EXEC_TEMPFILE_ONLY
if
(
ascii_strncasecmp
(
tok
,
"use-temp-files"
,
14
)
==
0
||
ascii_strncasecmp
(
tok
,
"no-use-temp-files"
,
17
)
==
0
)
log_info
(
_
(
"WARNING: keyserver option `%s' is not used"
" on this platform
\n
"
),
tok
);
#else
if
(
ascii_strncasecmp
(
tok
,
"use-temp-files"
,
14
)
==
0
)
opt
.
keyserver_options
.
options
|=
KEYSERVER_USE_TEMP_FILES
;
else
if
(
ascii_strncasecmp
(
tok
,
"no-use-temp-files"
,
17
)
==
0
)
opt
.
keyserver_options
.
options
&=~
KEYSERVER_USE_TEMP_FILES
;
#endif
else
if
(
!
parse_options
(
tok
,
&
opt
.
keyserver_options
.
options
,
keyserver_opts
,
0
)
&&
!
parse_import_options
(
tok
,
&
opt
.
keyserver_options
.
import_options
,
0
)
&&
!
parse_export_options
(
tok
,
&
opt
.
keyserver_options
.
export_options
,
0
))
{
/* All of the standard options have failed, so the option is
destined for a keyserver plugin. */
add_canonical_option
(
tok
,
&
opt
.
keyserver_options
.
other
);
}
}
if
(
max_cert
)
{
max_cert_size
=
strtoul
(
max_cert
,(
char
**
)
NULL
,
10
);
if
(
max_cert_size
==
0
)
max_cert_size
=
DEFAULT_MAX_CERT_SIZE
;
}
return
ret
;
}
void
free_keyserver_spec
(
struct
keyserver_spec
*
keyserver
)
{
xfree
(
keyserver
->
uri
);
xfree
(
keyserver
->
scheme
);
xfree
(
keyserver
->
auth
);
xfree
(
keyserver
->
host
);
xfree
(
keyserver
->
port
);
xfree
(
keyserver
->
path
);
xfree
(
keyserver
->
opaque
);
free_strlist
(
keyserver
->
options
);
xfree
(
keyserver
);
}
/* Return 0 for match */
static
int
cmp_keyserver_spec
(
struct
keyserver_spec
*
one
,
struct
keyserver_spec
*
two
)
{
if
(
ascii_strcasecmp
(
one
->
scheme
,
two
->
scheme
)
==
0
)
{
if
(
one
->
host
&&
two
->
host
&&
ascii_strcasecmp
(
one
->
host
,
two
->
host
)
==
0
)
{
if
((
one
->
port
&&
two
->
port
&&
ascii_strcasecmp
(
one
->
port
,
two
->
port
)
==
0
)
||
(
!
one
->
port
&&
!
two
->
port
))
return
0
;
}
else
if
(
one
->
opaque
&&
two
->
opaque
&&
ascii_strcasecmp
(
one
->
opaque
,
two
->
opaque
)
==
0
)
return
0
;
}
return
1
;
}
/* Try and match one of our keyservers. If we can, return that. If
we can't, return our input. */
struct
keyserver_spec
*
keyserver_match
(
struct
keyserver_spec
*
spec
)
{
struct
keyserver_spec
*
ks
;
for
(
ks
=
opt
.
keyserver
;
ks
;
ks
=
ks
->
next
)
if
(
cmp_keyserver_spec
(
spec
,
ks
)
==
0
)
return
ks
;
return
spec
;
}
/* TODO: once we cut over to an all-curl world, we don't need this
parser any longer so it can be removed, or at least moved to
keyserver/ksutil.c for limited use in gpgkeys_ldap or the like. */
keyserver_spec_t
parse_keyserver_uri
(
const
char
*
string
,
int
require_scheme
,
const
char
*
configname
,
unsigned
int
configlineno
)
{
int
assume_hkp
=
0
;
struct
keyserver_spec
*
keyserver
;
const
char
*
idx
;
int
count
;
char
*
uri
,
*
options
;
assert
(
string
!=
NULL
);
keyserver
=
xmalloc_clear
(
sizeof
(
struct
keyserver_spec
));
uri
=
xstrdup
(
string
);
options
=
strchr
(
uri
,
' '
);
if
(
options
)
{
char
*
tok
;
*
options
=
'\0'
;
options
++
;
while
((
tok
=
optsep
(
&
options
)))
add_canonical_option
(
tok
,
&
keyserver
->
options
);
}
/* Get the scheme */
for
(
idx
=
uri
,
count
=
0
;
*
idx
&&
*
idx
!=
':'
;
idx
++
)
{
count
++
;
/* Do we see the start of an RFC-2732 ipv6 address here? If so,
there clearly isn't a scheme so get out early. */
if
(
*
idx
==
'['
)
{
/* Was the '[' the first thing in the string? If not, we
have a mangled scheme with a [ in it so fail. */
if
(
count
==
1
)
break
;
else
goto
fail
;
}
}
if
(
count
==
0
)
goto
fail
;
if
(
*
idx
==
'\0'
||
*
idx
==
'['
)
{
if
(
require_scheme
)
return
NULL
;
/* Assume HKP if there is no scheme */
assume_hkp
=
1
;
keyserver
->
scheme
=
xstrdup
(
"hkp"
);
keyserver
->
uri
=
xmalloc
(
strlen
(
keyserver
->
scheme
)
+
3
+
strlen
(
uri
)
+
1
);
strcpy
(
keyserver
->
uri
,
keyserver
->
scheme
);
strcat
(
keyserver
->
uri
,
"://"
);
strcat
(
keyserver
->
uri
,
uri
);
}
else
{
int
i
;
keyserver
->
uri
=
xstrdup
(
uri
);
keyserver
->
scheme
=
xmalloc
(
count
+
1
);
/* Force to lowercase */
for
(
i
=
0
;
i
<
count
;
i
++
)
keyserver
->
scheme
[
i
]
=
ascii_tolower
(
uri
[
i
]);
keyserver
->
scheme
[
i
]
=
'\0'
;
/* Skip past the scheme and colon */
uri
+=
count
+
1
;
}
if
(
ascii_strcasecmp
(
keyserver
->
scheme
,
"x-broken-hkp"
)
==
0
)
{
deprecated_warning
(
configname
,
configlineno
,
"x-broken-hkp"
,
"--keyserver-options "
,
"broken-http-proxy"
);
xfree
(
keyserver
->
scheme
);
keyserver
->
scheme
=
xstrdup
(
"hkp"
);
append_to_strlist
(
&
opt
.
keyserver_options
.
other
,
"broken-http-proxy"
);
}
else
if
(
ascii_strcasecmp
(
keyserver
->
scheme
,
"x-hkp"
)
==
0
)
{
/* Canonicalize this to "hkp" so it works with both the internal
and external keyserver interface. */
xfree
(
keyserver
->
scheme
);
keyserver
->
scheme
=
xstrdup
(
"hkp"
);
}
if
(
uri
[
0
]
==
'/'
&&
uri
[
1
]
==
'/'
&&
uri
[
2
]
==
'/'
)
{
/* Three slashes means network path with a default host name.
This is a hack because it does not crok all possible
combiantions. We should better repalce all code bythe parser
from http.c. */
keyserver
->
path
=
xstrdup
(
uri
+
2
);
}
else
if
(
assume_hkp
||
(
uri
[
0
]
==
'/'
&&
uri
[
1
]
==
'/'
))
{
/* Two slashes means network path. */
/* Skip over the "//", if any */
if
(
!
assume_hkp
)
uri
+=
2
;
/* Do we have userinfo auth data present? */
for
(
idx
=
uri
,
count
=
0
;
*
idx
&&
*
idx
!=
'@'
&&
*
idx
!=
'/'
;
idx
++
)
count
++
;
/* We found a @ before the slash, so that means everything
before the @ is auth data. */
if
(
*
idx
==
'@'
)
{
if
(
count
==
0
)
goto
fail
;
keyserver
->
auth
=
xmalloc
(
count
+
1
);
strncpy
(
keyserver
->
auth
,
uri
,
count
);
keyserver
->
auth
[
count
]
=
'\0'
;
uri
+=
count
+
1
;
}
/* Is it an RFC-2732 ipv6 [literal address] ? */
if
(
*
uri
==
'['
)
{
for
(
idx
=
uri
+
1
,
count
=
1
;
*
idx
&&
((
isascii
(
*
idx
)
&&
isxdigit
(
*
idx
))
||
*
idx
==
':'
||
*
idx
==
'.'
);
idx
++
)
count
++
;
/* Is the ipv6 literal address terminated? */
if
(
*
idx
==
']'
)
count
++
;
else
goto
fail
;
}
else
for
(
idx
=
uri
,
count
=
0
;
*
idx
&&
*
idx
!=
':'
&&
*
idx
!=
'/'
;
idx
++
)
count
++
;
if
(
count
==
0
)
goto
fail
;
keyserver
->
host
=
xmalloc
(
count
+
1
);
strncpy
(
keyserver
->
host
,
uri
,
count
);
keyserver
->
host
[
count
]
=
'\0'
;
/* Skip past the host */
uri
+=
count
;
if
(
*
uri
==
':'
)
{
/* It would seem to be reasonable to limit the range of the
ports to values between 1-65535, but RFC 1738 and 1808
imply there is no limit. Of course, the real world has
limits. */
for
(
idx
=
uri
+
1
,
count
=
0
;
*
idx
&&
*
idx
!=
'/'
;
idx
++
)
{
count
++
;
/* Ports are digits only */
if
(
!
digitp
(
idx
))
goto
fail
;
}
keyserver
->
port
=
xmalloc
(
count
+
1
);
strncpy
(
keyserver
->
port
,
uri
+
1
,
count
);
keyserver
->
port
[
count
]
=
'\0'
;
/* Skip past the colon and port number */
uri
+=
1
+
count
;
}
/* Everything else is the path */
if
(
*
uri
)
keyserver
->
path
=
xstrdup
(
uri
);
else
keyserver
->
path
=
xstrdup
(
"/"
);
if
(
keyserver
->
path
[
1
])
keyserver
->
flags
.
direct_uri
=
1
;
}
else
if
(
uri
[
0
]
!=
'/'
)
{
/* No slash means opaque. Just record the opaque blob and get
out. */
keyserver
->
opaque
=
xstrdup
(
uri
);
}
else
{
/* One slash means absolute path. We don't need to support that
yet. */
goto
fail
;
}
return
keyserver
;
fail
:
free_keyserver_spec
(
keyserver
);
return
NULL
;
}
struct
keyserver_spec
*
parse_preferred_keyserver
(
PKT_signature
*
sig
)
{
struct
keyserver_spec
*
spec
=
NULL
;
const
byte
*
p
;
size_t
plen
;
p
=
parse_sig_subpkt
(
sig
->
hashed
,
SIGSUBPKT_PREF_KS
,
&
plen
);
if
(
p
&&
plen
)
{
byte
*
dupe
=
xmalloc
(
plen
+
1
);
memcpy
(
dupe
,
p
,
plen
);
dupe
[
plen
]
=
'\0'
;
spec
=
parse_keyserver_uri
(
dupe
,
1
,
NULL
,
0
);
xfree
(
dupe
);
}
return
spec
;
}
static
void
print_keyrec
(
int
number
,
struct
keyrec
*
keyrec
)
{
int
i
;
iobuf_writebyte
(
keyrec
->
uidbuf
,
0
);
iobuf_flush_temp
(
keyrec
->
uidbuf
);
es_printf
(
"(%d)
\t
%s "
,
number
,
iobuf_get_temp_buffer
(
keyrec
->
uidbuf
));
if
(
keyrec
->
size
>
0
)
es_printf
(
"%d bit "
,
keyrec
->
size
);
if
(
keyrec
->
type
)
{
const
char
*
str
=
gcry_pk_algo_name
(
keyrec
->
type
);
if
(
str
)
es_printf
(
"%s "
,
str
);
else
es_printf
(
"unknown "
);
}
switch
(
keyrec
->
desc
.
mode
)
{
/* If the keyserver helper gave us a short keyid, we have no
choice but to use it. Do check --keyid-format to add a 0x if
needed. */
case
KEYDB_SEARCH_MODE_SHORT_KID
:
es_printf
(
"key %s%08lX"
,
(
opt
.
keyid_format
==
KF_0xSHORT
||
opt
.
keyid_format
==
KF_0xLONG
)
?
"0x"
:
""
,
(
ulong
)
keyrec
->
desc
.
u
.
kid
[
1
]);
break
;
/* However, if it gave us a long keyid, we can honor
--keyid-format */
case
KEYDB_SEARCH_MODE_LONG_KID
:
es_printf
(
"key %s"
,
keystr
(
keyrec
->
desc
.
u
.
kid
));
break
;
case
KEYDB_SEARCH_MODE_FPR16
:
es_printf
(
"key "
);
for
(
i
=
0
;
i
<
16
;
i
++
)
es_printf
(
"%02X"
,
keyrec
->
desc
.
u
.
fpr
[
i
]);
break
;
case
KEYDB_SEARCH_MODE_FPR20
:
es_printf
(
"key "
);
for
(
i
=
0
;
i
<
20
;
i
++
)
es_printf
(
"%02X"
,
keyrec
->
desc
.
u
.
fpr
[
i
]);
break
;
default
:
BUG
();
break
;
}
if
(
keyrec
->
createtime
>
0
)
{
es_printf
(
", "
);
es_printf
(
_
(
"created: %s"
),
strtimestamp
(
keyrec
->
createtime
));
}
if
(
keyrec
->
expiretime
>
0
)
{
es_printf
(
", "
);
es_printf
(
_
(
"expires: %s"
),
strtimestamp
(
keyrec
->
expiretime
));
}
if
(
keyrec
->
flags
&
1
)
es_printf
(
" (%s)"
,
_
(
"revoked"
));
if
(
keyrec
->
flags
&
2
)
es_printf
(
" (%s)"
,
_
(
"disabled"
));
if
(
keyrec
->
flags
&
4
)
es_printf
(
" (%s)"
,
_
(
"expired"
));
es_printf
(
"
\n
"
);
}
/* Returns a keyrec (which must be freed) once a key is complete, and
NULL otherwise. Call with a NULL keystring once key parsing is
complete to return any unfinished keys. */
static
struct
keyrec
*
parse_keyrec
(
char
*
keystring
)
{
/* FIXME: Remove the static and put the data into the parms we use
for the caller anyway. */
static
struct
keyrec
*
work
=
NULL
;
struct
keyrec
*
ret
=
NULL
;
char
*
record
;
int
i
;
if
(
keystring
==
NULL
)
{
if
(
work
==
NULL
)
return
NULL
;
else
if
(
work
->
desc
.
mode
==
KEYDB_SEARCH_MODE_NONE
)
{
xfree
(
work
);
return
NULL
;
}
else
{
ret
=
work
;
work
=
NULL
;
return
ret
;
}
}
if
(
work
==
NULL
)
{
work
=
xmalloc_clear
(
sizeof
(
struct
keyrec
));
work
->
uidbuf
=
iobuf_temp
();
}
trim_trailing_ws
(
keystring
,
strlen
(
keystring
));
if
((
record
=
strsep
(
&
keystring
,
":"
))
==
NULL
)
return
ret
;
if
(
ascii_strcasecmp
(
"pub"
,
record
)
==
0
)
{
char
*
tok
;
gpg_error_t
err
;
if
(
work
->
desc
.
mode
)
{
ret
=
work
;
work
=
xmalloc_clear
(
sizeof
(
struct
keyrec
));
work
->
uidbuf
=
iobuf_temp
();
}
if
((
tok
=
strsep
(
&
keystring
,
":"
))
==
NULL
)
return
ret
;
err
=
classify_user_id
(
tok
,
&
work
->
desc
,
1
);
if
(
err
||
(
work
->
desc
.
mode
!=
KEYDB_SEARCH_MODE_SHORT_KID
&&
work
->
desc
.
mode
!=
KEYDB_SEARCH_MODE_LONG_KID
&&
work
->
desc
.
mode
!=
KEYDB_SEARCH_MODE_FPR16
&&
work
->
desc
.
mode
!=
KEYDB_SEARCH_MODE_FPR20
))
{
work
->
desc
.
mode
=
KEYDB_SEARCH_MODE_NONE
;
return
ret
;
}
/* Note all items after this are optional. This allows us to
have a pub line as simple as pub:keyid and nothing else. */
work
->
lines
++
;
if
((
tok
=
strsep
(
&
keystring
,
":"
))
==
NULL
)
return
ret
;
work
->
type
=
atoi
(
tok
);
if
((
tok
=
strsep
(
&
keystring
,
":"
))
==
NULL
)
return
ret
;
work
->
size
=
atoi
(
tok
);
if
((
tok
=
strsep
(
&
keystring
,
":"
))
==
NULL
)
return
ret
;
if
(
atoi
(
tok
)
<=
0
)
work
->
createtime
=
0
;
else
work
->
createtime
=
atoi
(
tok
);
if
((
tok
=
strsep
(
&
keystring
,
":"
))
==
NULL
)
return
ret
;
if
(
atoi
(
tok
)
<=
0
)
work
->
expiretime
=
0
;
else
{
work
->
expiretime
=
atoi
(
tok
);
/* Force the 'e' flag on if this key is expired. */
if
(
work
->
expiretime
<=
make_timestamp
())
work
->
flags
|=
4
;
}
if
((
tok
=
strsep
(
&
keystring
,
":"
))
==
NULL
)
return
ret
;
while
(
*
tok
)
switch
(
*
tok
++
)
{
case
'r'
:
case
'R'
:
work
->
flags
|=
1
;
break
;
case
'd'
:
case
'D'
:
work
->
flags
|=
2
;
break
;
case
'e'
:
case
'E'
:
work
->
flags
|=
4
;
break
;
}
}
else
if
(
ascii_strcasecmp
(
"uid"
,
record
)
==
0
&&
work
->
desc
.
mode
)
{
char
*
userid
,
*
tok
,
*
decoded
;
if
((
tok
=
strsep
(
&
keystring
,
":"
))
==
NULL
)
return
ret
;
if
(
strlen
(
tok
)
==
0
)
return
ret
;
userid
=
tok
;
/* By definition, de-%-encoding is always smaller than the
original string so we can decode in place. */
i
=
0
;
while
(
*
tok
)
if
(
tok
[
0
]
==
'%'
&&
tok
[
1
]
&&
tok
[
2
])
{
int
c
;
userid
[
i
]
=
(
c
=
hextobyte
(
&
tok
[
1
]))
==
-1
?
'?'
:
c
;
i
++
;
tok
+=
3
;
}
else
userid
[
i
++
]
=*
tok
++
;
/* We don't care about the other info provided in the uid: line
since no keyserver supports marking userids with timestamps
or revoked/expired/disabled yet. */
/* No need to check for control characters, as utf8_to_native
does this for us. */
decoded
=
utf8_to_native
(
userid
,
i
,
0
);
if
(
strlen
(
decoded
)
>
opt
.
screen_columns
-10
)
decoded
[
opt
.
screen_columns
-10
]
=
'\0'
;
iobuf_writestr
(
work
->
uidbuf
,
decoded
);
xfree
(
decoded
);
iobuf_writestr
(
work
->
uidbuf
,
"
\n\t
"
);
work
->
lines
++
;
}
/* Ignore any records other than "pri" and "uid" for easy future
growth. */
return
ret
;
}
/* Show a prompt and allow the user to select keys for retrieval. */
static
gpg_error_t
show_prompt
(
ctrl_t
ctrl
,
KEYDB_SEARCH_DESC
*
desc
,
int
numdesc
,
int
count
,
const
char
*
search
)
{
gpg_error_t
err
;
char
*
answer
=
NULL
;
es_fflush
(
es_stdout
);
if
(
count
&&
opt
.
command_fd
==
-1
)
{
static
int
from
=
1
;
tty_printf
(
"Keys %d-%d of %d for
\"
%s
\"
. "
,
from
,
numdesc
,
count
,
search
);
from
=
numdesc
+
1
;
}
again
:
err
=
0
;
xfree
(
answer
);
answer
=
cpr_get_no_help
(
"keysearch.prompt"
,
_
(
"Enter number(s), N)ext, or Q)uit > "
));
/* control-d */
if
(
answer
[
0
]
==
'\x04'
)
{
tty_printf
(
"Q
\n
"
);
answer
[
0
]
=
'q'
;
}
if
(
answer
[
0
]
==
'q'
||
answer
[
0
]
==
'Q'
)
err
=
gpg_error
(
GPG_ERR_CANCELED
);
else
if
(
atoi
(
answer
)
>=
1
&&
atoi
(
answer
)
<=
numdesc
)
{
char
*
split
=
answer
;
char
*
num
;
int
numarray
[
50
];
int
numidx
=
0
;
int
idx
;
while
((
num
=
strsep
(
&
split
,
" ,"
)))
if
(
atoi
(
num
)
>=
1
&&
atoi
(
num
)
<=
numdesc
)
{
if
(
numidx
>=
DIM
(
numarray
))
{
tty_printf
(
"Too many keys selected
\n
"
);
goto
again
;
}
numarray
[
numidx
++
]
=
atoi
(
num
);
}
if
(
!
numidx
)
goto
again
;
{
KEYDB_SEARCH_DESC
*
selarray
;
selarray
=
xtrymalloc
(
numidx
*
sizeof
*
selarray
);
if
(
!
selarray
)
{
err
=
gpg_error_from_syserror
();
goto
leave
;
}
for
(
idx
=
0
;
idx
<
numidx
;
idx
++
)
selarray
[
idx
]
=
desc
[
numarray
[
idx
]
-1
];
err
=
keyserver_get
(
ctrl
,
selarray
,
numidx
,
NULL
);
xfree
(
selarray
);
}
}
leave
:
xfree
(
answer
);
return
err
;
}
/* This is a callback used by call-dirmngr.c to process the result of
KS_SEARCH command. LINE is the actual data line received with all
escaping removed and guaranteed to be exactly one line with
stripped LF; an EOF is indicated by LINE passed as NULL. LINE may
be modified after return. */
static
gpg_error_t
search_line_handler
(
void
*
opaque
,
char
*
line
)
{
struct
search_line_handler_parm_s
*
parm
=
opaque
;
gpg_error_t
err
=
0
;
struct
keyrec
*
keyrec
;
if
(
parm
->
eof_seen
&&
line
)
{
log_debug
(
"ooops: unexpected data after EOF
\n
"
);
line
=
NULL
;
}
/* Print the received line. */
if
(
opt
.
with_colons
&&
line
)
{
log_debug
(
"%s
\n
"
,
line
);
}
/* Look for an info: line. The only current info: values defined
are the version and key count. */
if
(
line
&&
!
parm
->
any_lines
&&
!
ascii_strncasecmp
(
"info:"
,
line
,
5
))
{
char
*
str
=
line
+
5
;
char
*
tok
;
if
((
tok
=
strsep
(
&
str
,
":"
)))
{
int
version
;
if
(
sscanf
(
tok
,
"%d"
,
&
version
)
!=
1
)
version
=
1
;
if
(
version
!=
1
)
{
log_error
(
_
(
"invalid keyserver protocol "
"(us %d!=handler %d)
\n
"
),
1
,
version
);
return
gpg_error
(
GPG_ERR_UNSUPPORTED_PROTOCOL
);
}
}
if
((
tok
=
strsep
(
&
str
,
":"
))
&&
sscanf
(
tok
,
"%d"
,
&
parm
->
count
)
==
1
)
{
if
(
!
parm
->
count
)
parm
->
not_found
=
1
;
/* Server indicated that no items follow. */
else
if
(
parm
->
count
<
0
)
parm
->
count
=
10
;
/* Bad value - assume something reasonable. */
else
parm
->
validcount
=
1
;
/* COUNT seems to be okay. */
}
parm
->
any_lines
=
1
;
return
0
;
/* Line processing finished. */
}
again
:
if
(
line
)
keyrec
=
parse_keyrec
(
line
);
else
{
/* Received EOF - flush data */
parm
->
eof_seen
=
1
;
keyrec
=
parse_keyrec
(
NULL
);
if
(
!
keyrec
)
{
if
(
!
parm
->
nkeys
)
parm
->
not_found
=
1
;
/* No keys at all. */
else
{
if
(
parm
->
nkeys
!=
parm
->
count
)
parm
->
validcount
=
0
;
if
(
!
(
opt
.
with_colons
&&
opt
.
batch
))
{
err
=
show_prompt
(
parm
->
ctrl
,
parm
->
desc
,
parm
->
nkeys
,
parm
->
validcount
?
parm
->
count
:
0
,
parm
->
searchstr_disp
);
return
err
;
}
}
}
}
/* Save the key in the key array. */
if
(
keyrec
)
{
/* Allocate or enlarge the key array if needed. */
if
(
!
parm
->
desc
)
{
if
(
parm
->
count
<
1
)
{
parm
->
count
=
10
;
parm
->
validcount
=
0
;
}
parm
->
desc
=
xtrymalloc
(
parm
->
count
*
sizeof
*
parm
->
desc
);
if
(
!
parm
->
desc
)
{
err
=
gpg_error_from_syserror
();
iobuf_close
(
keyrec
->
uidbuf
);
xfree
(
keyrec
);
return
err
;
}
}
else
if
(
parm
->
nkeys
==
parm
->
count
)
{
/* Keyserver sent more keys than claimed in the info: line. */
KEYDB_SEARCH_DESC
*
tmp
;
int
newcount
=
parm
->
count
+
10
;
tmp
=
xtryrealloc
(
parm
->
desc
,
newcount
*
sizeof
*
parm
->
desc
);
if
(
!
tmp
)
{
err
=
gpg_error_from_syserror
();
iobuf_close
(
keyrec
->
uidbuf
);
xfree
(
keyrec
);
return
err
;
}
parm
->
count
=
newcount
;
parm
->
desc
=
tmp
;
parm
->
validcount
=
0
;
}
parm
->
desc
[
parm
->
nkeys
]
=
keyrec
->
desc
;
if
(
!
opt
.
with_colons
)
{
/* SCREEN_LINES - 1 for the prompt. */
if
(
parm
->
numlines
+
keyrec
->
lines
>
opt
.
screen_lines
-
1
)
{
err
=
show_prompt
(
parm
->
ctrl
,
parm
->
desc
,
parm
->
nkeys
,
parm
->
validcount
?
parm
->
count
:
0
,
parm
->
searchstr_disp
);
if
(
err
)
return
err
;
parm
->
numlines
=
0
;
}
print_keyrec
(
parm
->
nkeys
+
1
,
keyrec
);
}
parm
->
numlines
+=
keyrec
->
lines
;
iobuf_close
(
keyrec
->
uidbuf
);
xfree
(
keyrec
);
parm
->
any_lines
=
1
;
parm
->
nkeys
++
;
/* If we are here due to a flush after the EOF, run again for
the last prompt. Fixme: Make this code better readable. */
if
(
parm
->
eof_seen
)
goto
again
;
}
return
0
;
}
int
keyserver_export
(
ctrl_t
ctrl
,
strlist_t
users
)
{
gpg_error_t
err
;
strlist_t
sl
=
NULL
;
KEYDB_SEARCH_DESC
desc
;
int
rc
=
0
;
/* Weed out descriptors that we don't support sending */
for
(;
users
;
users
=
users
->
next
)
{
err
=
classify_user_id
(
users
->
d
,
&
desc
,
1
);
if
(
err
||
(
desc
.
mode
!=
KEYDB_SEARCH_MODE_SHORT_KID
&&
desc
.
mode
!=
KEYDB_SEARCH_MODE_LONG_KID
&&
desc
.
mode
!=
KEYDB_SEARCH_MODE_FPR16
&&
desc
.
mode
!=
KEYDB_SEARCH_MODE_FPR20
))
{
log_error
(
_
(
"
\"
%s
\"
not a key ID: skipping
\n
"
),
users
->
d
);
continue
;
}
else
append_to_strlist
(
&
sl
,
users
->
d
);
}
if
(
sl
)
{
rc
=
keyserver_put
(
ctrl
,
sl
,
opt
.
keyserver
);
free_strlist
(
sl
);
}
return
rc
;
}
int
keyserver_import
(
ctrl_t
ctrl
,
strlist_t
users
)
{
gpg_error_t
err
;
KEYDB_SEARCH_DESC
*
desc
;
int
num
=
100
,
count
=
0
;
int
rc
=
0
;
/* Build a list of key ids */
desc
=
xmalloc
(
sizeof
(
KEYDB_SEARCH_DESC
)
*
num
);
for
(;
users
;
users
=
users
->
next
)
{
err
=
classify_user_id
(
users
->
d
,
&
desc
[
count
],
1
);
if
(
err
||
(
desc
[
count
].
mode
!=
KEYDB_SEARCH_MODE_SHORT_KID
&&
desc
[
count
].
mode
!=
KEYDB_SEARCH_MODE_LONG_KID
&&
desc
[
count
].
mode
!=
KEYDB_SEARCH_MODE_FPR16
&&
desc
[
count
].
mode
!=
KEYDB_SEARCH_MODE_FPR20
))
{
log_error
(
_
(
"
\"
%s
\"
not a key ID: skipping
\n
"
),
users
->
d
);
continue
;
}
count
++
;
if
(
count
==
num
)
{
num
+=
100
;
desc
=
xrealloc
(
desc
,
sizeof
(
KEYDB_SEARCH_DESC
)
*
num
);
}
}
if
(
count
>
0
)
rc
=
keyserver_get
(
ctrl
,
desc
,
count
,
NULL
);
xfree
(
desc
);
return
rc
;
}
int
keyserver_import_fprint
(
ctrl_t
ctrl
,
const
byte
*
fprint
,
size_t
fprint_len
,
struct
keyserver_spec
*
keyserver
)
{
KEYDB_SEARCH_DESC
desc
;
memset
(
&
desc
,
0
,
sizeof
(
desc
));
if
(
fprint_len
==
16
)
desc
.
mode
=
KEYDB_SEARCH_MODE_FPR16
;
else
if
(
fprint_len
==
20
)
desc
.
mode
=
KEYDB_SEARCH_MODE_FPR20
;
else
return
-1
;
memcpy
(
desc
.
u
.
fpr
,
fprint
,
fprint_len
);
/* TODO: Warn here if the fingerprint we got doesn't match the one
we asked for? */
return
keyserver_get
(
ctrl
,
&
desc
,
1
,
keyserver
);
}
int
keyserver_import_keyid
(
ctrl_t
ctrl
,
u32
*
keyid
,
struct
keyserver_spec
*
keyserver
)
{
KEYDB_SEARCH_DESC
desc
;
memset
(
&
desc
,
0
,
sizeof
(
desc
));
desc
.
mode
=
KEYDB_SEARCH_MODE_LONG_KID
;
desc
.
u
.
kid
[
0
]
=
keyid
[
0
];
desc
.
u
.
kid
[
1
]
=
keyid
[
1
];
return
keyserver_get
(
ctrl
,
&
desc
,
1
,
keyserver
);
}
/* code mostly stolen from do_export_stream */
static
int
keyidlist
(
strlist_t
users
,
KEYDB_SEARCH_DESC
**
klist
,
int
*
count
,
int
fakev3
)
{
int
rc
=
0
,
ndesc
,
num
=
100
;
KBNODE
keyblock
=
NULL
,
node
;
KEYDB_HANDLE
kdbhd
;
KEYDB_SEARCH_DESC
*
desc
;
strlist_t
sl
;
*
count
=
0
;
*
klist
=
xmalloc
(
sizeof
(
KEYDB_SEARCH_DESC
)
*
num
);
kdbhd
=
keydb_new
();
if
(
!
users
)
{
ndesc
=
1
;
desc
=
xmalloc_clear
(
ndesc
*
sizeof
*
desc
);
desc
[
0
].
mode
=
KEYDB_SEARCH_MODE_FIRST
;
}
else
{
for
(
ndesc
=
0
,
sl
=
users
;
sl
;
sl
=
sl
->
next
,
ndesc
++
)
;
desc
=
xmalloc
(
ndesc
*
sizeof
*
desc
);
for
(
ndesc
=
0
,
sl
=
users
;
sl
;
sl
=
sl
->
next
)
{
gpg_error_t
err
;
if
(
!
(
err
=
classify_user_id
(
sl
->
d
,
desc
+
ndesc
,
1
)))
ndesc
++
;
else
log_error
(
_
(
"key
\"
%s
\"
not found: %s
\n
"
),
sl
->
d
,
gpg_strerror
(
err
));
}
}
while
(
!
(
rc
=
keydb_search
(
kdbhd
,
desc
,
ndesc
)))
{
if
(
!
users
)
desc
[
0
].
mode
=
KEYDB_SEARCH_MODE_NEXT
;
/* read the keyblock */
rc
=
keydb_get_keyblock
(
kdbhd
,
&
keyblock
);
if
(
rc
)
{
log_error
(
_
(
"error reading keyblock: %s
\n
"
),
g10_errstr
(
rc
)
);
goto
leave
;
}
if
((
node
=
find_kbnode
(
keyblock
,
PKT_PUBLIC_KEY
)))
{
/* This is to work around a bug in some keyservers (pksd and
OKS) that calculate v4 RSA keyids as if they were v3 RSA.
The answer is to refresh both the correct v4 keyid
(e.g. 99242560) and the fake v3 keyid (e.g. 68FDDBC7).
This only happens for key refresh using the HKP scheme
and if the refresh-add-fake-v3-keyids keyserver option is
set. */
if
(
fakev3
&&
is_RSA
(
node
->
pkt
->
pkt
.
public_key
->
pubkey_algo
)
&&
node
->
pkt
->
pkt
.
public_key
->
version
>=
4
)
{
(
*
klist
)[
*
count
].
mode
=
KEYDB_SEARCH_MODE_LONG_KID
;
v3_keyid
(
node
->
pkt
->
pkt
.
public_key
->
pkey
[
0
],
(
*
klist
)[
*
count
].
u
.
kid
);
(
*
count
)
++
;
if
(
*
count
==
num
)
{
num
+=
100
;
*
klist
=
xrealloc
(
*
klist
,
sizeof
(
KEYDB_SEARCH_DESC
)
*
num
);
}
}
/* v4 keys get full fingerprints. v3 keys get long keyids.
This is because it's easy to calculate any sort of keyid
from a v4 fingerprint, but not a v3 fingerprint. */
if
(
node
->
pkt
->
pkt
.
public_key
->
version
<
4
)
{
(
*
klist
)[
*
count
].
mode
=
KEYDB_SEARCH_MODE_LONG_KID
;
keyid_from_pk
(
node
->
pkt
->
pkt
.
public_key
,
(
*
klist
)[
*
count
].
u
.
kid
);
}
else
{
size_t
dummy
;
(
*
klist
)[
*
count
].
mode
=
KEYDB_SEARCH_MODE_FPR20
;
fingerprint_from_pk
(
node
->
pkt
->
pkt
.
public_key
,
(
*
klist
)[
*
count
].
u
.
fpr
,
&
dummy
);
}
/* This is a little hackish, using the skipfncvalue as a
void* pointer to the keyserver spec, but we don't need
the skipfnc here, and it saves having an additional field
for this (which would be wasted space most of the
time). */
(
*
klist
)[
*
count
].
skipfncvalue
=
NULL
;
/* Are we honoring preferred keyservers? */
if
(
opt
.
keyserver_options
.
options
&
KEYSERVER_HONOR_KEYSERVER_URL
)
{
PKT_user_id
*
uid
=
NULL
;
PKT_signature
*
sig
=
NULL
;
merge_keys_and_selfsig
(
keyblock
);
for
(
node
=
node
->
next
;
node
;
node
=
node
->
next
)
{
if
(
node
->
pkt
->
pkttype
==
PKT_USER_ID
&&
node
->
pkt
->
pkt
.
user_id
->
is_primary
)
uid
=
node
->
pkt
->
pkt
.
user_id
;
else
if
(
node
->
pkt
->
pkttype
==
PKT_SIGNATURE
&&
node
->
pkt
->
pkt
.
signature
->
flags
.
chosen_selfsig
&&
uid
)
{
sig
=
node
->
pkt
->
pkt
.
signature
;
break
;
}
}
/* Try and parse the keyserver URL. If it doesn't work,
then we end up writing NULL which indicates we are
the same as any other key. */
if
(
sig
)
(
*
klist
)[
*
count
].
skipfncvalue
=
parse_preferred_keyserver
(
sig
);
}
(
*
count
)
++
;
if
(
*
count
==
num
)
{
num
+=
100
;
*
klist
=
xrealloc
(
*
klist
,
sizeof
(
KEYDB_SEARCH_DESC
)
*
num
);
}
}
}
if
(
rc
==
-1
)
rc
=
0
;
leave
:
if
(
rc
)
xfree
(
*
klist
);
xfree
(
desc
);
keydb_release
(
kdbhd
);
release_kbnode
(
keyblock
);
return
rc
;
}
/* Note this is different than the original HKP refresh. It allows
usernames to refresh only part of the keyring. */
int
keyserver_refresh
(
ctrl_t
ctrl
,
strlist_t
users
)
{
int
rc
,
count
,
numdesc
,
fakev3
=
0
;
KEYDB_SEARCH_DESC
*
desc
;
unsigned
int
options
=
opt
.
keyserver_options
.
import_options
;
/* We switch merge-only on during a refresh, as 'refresh' should
never import new keys, even if their keyids match. */
opt
.
keyserver_options
.
import_options
|=
IMPORT_MERGE_ONLY
;
/* Similarly, we switch on fast-import, since refresh may make
multiple import sets (due to preferred keyserver URLs). We don't
want each set to rebuild the trustdb. Instead we do it once at
the end here. */
opt
.
keyserver_options
.
import_options
|=
IMPORT_FAST
;
/* If refresh_add_fake_v3_keyids is on and it's a HKP or MAILTO
scheme, then enable fake v3 keyid generation. */
if
((
opt
.
keyserver_options
.
options
&
KEYSERVER_ADD_FAKE_V3
)
&&
opt
.
keyserver
&&
(
ascii_strcasecmp
(
opt
.
keyserver
->
scheme
,
"hkp"
)
==
0
||
ascii_strcasecmp
(
opt
.
keyserver
->
scheme
,
"mailto"
)
==
0
))
fakev3
=
1
;
rc
=
keyidlist
(
users
,
&
desc
,
&
numdesc
,
fakev3
);
if
(
rc
)
return
rc
;
count
=
numdesc
;
if
(
count
>
0
)
{
int
i
;
/* Try to handle preferred keyserver keys first */
for
(
i
=
0
;
i
<
numdesc
;
i
++
)
{
if
(
desc
[
i
].
skipfncvalue
)
{
struct
keyserver_spec
*
keyserver
=
desc
[
i
].
skipfncvalue
;
/* We use the keyserver structure we parsed out before.
Note that a preferred keyserver without a scheme://
will be interpreted as hkp:// */
rc
=
keyserver_get
(
ctrl
,
&
desc
[
i
],
1
,
keyserver
);
if
(
rc
)
log_info
(
_
(
"WARNING: unable to refresh key %s"
" via %s: %s
\n
"
),
keystr_from_desc
(
&
desc
[
i
]),
keyserver
->
uri
,
g10_errstr
(
rc
));
else
{
/* We got it, so mark it as NONE so we don't try and
get it again from the regular keyserver. */
desc
[
i
].
mode
=
KEYDB_SEARCH_MODE_NONE
;
count
--
;
}
free_keyserver_spec
(
keyserver
);
}
}
}
if
(
count
>
0
)
{
if
(
opt
.
keyserver
)
{
if
(
count
==
1
)
log_info
(
_
(
"refreshing 1 key from %s
\n
"
),
opt
.
keyserver
->
uri
);
else
log_info
(
_
(
"refreshing %d keys from %s
\n
"
),
count
,
opt
.
keyserver
->
uri
);
}
rc
=
keyserver_get
(
ctrl
,
desc
,
numdesc
,
NULL
);
}
xfree
(
desc
);
opt
.
keyserver_options
.
import_options
=
options
;
/* If the original options didn't have fast import, and the trustdb
is dirty, rebuild. */
if
(
!
(
opt
.
keyserver_options
.
import_options
&
IMPORT_FAST
))
trustdb_check_or_update
();
return
rc
;
}
/* Search for keys on the keyservers. The patterns are given in the
string list TOKENS. */
gpg_error_t
keyserver_search
(
ctrl_t
ctrl
,
strlist_t
tokens
)
{
gpg_error_t
err
;
char
*
searchstr
;
struct
search_line_handler_parm_s
parm
;
memset
(
&
parm
,
0
,
sizeof
parm
);
if
(
!
tokens
)
return
0
;
/* Return success if no patterns are given. */
if
(
!
opt
.
keyserver
)
{
log_error
(
_
(
"no keyserver known (use option --keyserver)
\n
"
));
return
gpg_error
(
GPG_ERR_NO_KEYSERVER
);
}
/* Write global options */
/* for(temp=opt.keyserver_options.other;temp;temp=temp->next) */
/* fprintf(spawn->tochild,"OPTION %s\n",temp->d); */
/* Write per-keyserver options */
/* for(temp=keyserver->options;temp;temp=temp->next) */
/* fprintf(spawn->tochild,"OPTION %s\n",temp->d); */
{
membuf_t
mb
;
strlist_t
item
;
init_membuf
(
&
mb
,
1024
);
for
(
item
=
tokens
;
item
;
item
=
item
->
next
)
{
if
(
item
!=
tokens
)
put_membuf
(
&
mb
,
" "
,
1
);
put_membuf_str
(
&
mb
,
item
->
d
);
}
put_membuf
(
&
mb
,
""
,
1
);
/* Append Nul. */
searchstr
=
get_membuf
(
&
mb
,
NULL
);
if
(
!
searchstr
)
{
err
=
gpg_error_from_syserror
();
goto
leave
;
}
}
/* FIXME: Enable the next line */
/* log_info (_("searching for \"%s\" from %s\n"), searchstr, keyserver->uri); */
parm
.
ctrl
=
ctrl
;
if
(
searchstr
)
parm
.
searchstr_disp
=
utf8_to_native
(
searchstr
,
strlen
(
searchstr
),
0
);
err
=
gpg_dirmngr_ks_search
(
ctrl
,
searchstr
,
search_line_handler
,
&
parm
);
if
(
parm
.
not_found
)
{
if
(
parm
.
searchstr_disp
)
log_info
(
_
(
"key
\"
%s
\"
not found on keyserver
\n
"
),
parm
.
searchstr_disp
);
else
log_info
(
_
(
"key not found on keyserver
\n
"
));
}
if
(
gpg_err_code
(
err
)
==
GPG_ERR_NO_KEYSERVER
)
log_error
(
_
(
"no keyserver known (use option --keyserver)
\n
"
));
else
if
(
err
)
log_error
(
"error searching keyserver: %s
\n
"
,
gpg_strerror
(
err
));
/* switch(ret) */
/* { */
/* case KEYSERVER_SCHEME_NOT_FOUND: */
/* log_error(_("no handler for keyserver scheme `%s'\n"), */
/* opt.keyserver->scheme); */
/* break; */
/* case KEYSERVER_NOT_SUPPORTED: */
/* log_error(_("action `%s' not supported with keyserver " */
/* "scheme `%s'\n"), "search", opt.keyserver->scheme); */
/* break; */
/* case KEYSERVER_TIMEOUT: */
/* log_error(_("keyserver timed out\n")); */
/* break; */
/* case KEYSERVER_INTERNAL_ERROR: */
/* default: */
/* log_error(_("keyserver internal error\n")); */
/* break; */
/* } */
/* return gpg_error (GPG_ERR_KEYSERVER); */
leave
:
xfree
(
parm
.
desc
);
xfree
(
parm
.
searchstr_disp
);
xfree
(
searchstr
);
return
err
;
}
/* Called using:
import_name:
rc = keyserver_work (ctrl, KS_GETNAME, list, NULL,
0, fpr, fpr_len, keyserver);
import_ldap:
rc = keyserver_work (ctrl, KS_GETNAME, list, NULL,
0, fpr, fpr_len, keyserver);
*/
static
gpg_error_t
keyserver_get
(
ctrl_t
ctrl
,
KEYDB_SEARCH_DESC
*
desc
,
int
ndesc
,
struct
keyserver_spec
*
keyserver
)
{
gpg_error_t
err
=
0
;
char
**
pattern
;
int
idx
,
npat
;
estream_t
datastream
;
/* Create an array filled with a search pattern for each key. The
array is delimited by a NULL entry. */
pattern
=
xtrycalloc
(
ndesc
+
1
,
sizeof
*
pattern
);
if
(
!
pattern
)
return
gpg_error_from_syserror
();
for
(
npat
=
idx
=
0
;
idx
<
ndesc
;
idx
++
)
{
int
quiet
=
0
;
if
(
desc
[
idx
].
mode
==
KEYDB_SEARCH_MODE_FPR20
||
desc
[
idx
].
mode
==
KEYDB_SEARCH_MODE_FPR16
)
{
pattern
[
npat
]
=
xtrymalloc
(
2
+
2
*
20
+
1
);
if
(
!
pattern
[
npat
])
err
=
gpg_error_from_syserror
();
else
{
strcpy
(
pattern
[
npat
],
"0x"
);
bin2hex
(
desc
[
idx
].
u
.
fpr
,
desc
[
idx
].
mode
==
KEYDB_SEARCH_MODE_FPR20
?
20
:
16
,
pattern
[
npat
]
+
2
);
npat
++
;
}
}
else
if
(
desc
[
idx
].
mode
==
KEYDB_SEARCH_MODE_LONG_KID
)
{
pattern
[
npat
]
=
xtryasprintf
(
"0x%08lX%08lX"
,
(
ulong
)
desc
[
idx
].
u
.
kid
[
0
],
(
ulong
)
desc
[
idx
].
u
.
kid
[
1
]);
if
(
!
pattern
[
npat
])
err
=
gpg_error_from_syserror
();
else
npat
++
;
}
else
if
(
desc
[
idx
].
mode
==
KEYDB_SEARCH_MODE_SHORT_KID
)
{
pattern
[
npat
]
=
xtryasprintf
(
"0x%08lX"
,
(
ulong
)
desc
[
idx
].
u
.
kid
[
1
]);
if
(
!
pattern
[
npat
])
err
=
gpg_error_from_syserror
();
else
npat
++
;
}
else
if
(
desc
[
idx
].
mode
==
KEYDB_SEARCH_MODE_EXACT
)
{
/* FIXME: We don't need this. It is used as a dummy by
keyserver_fetch which passes an entire URL. Better use a
separate function here. */
pattern
[
npat
]
=
xtrystrdup
(
"0x0000000000000000"
);
if
(
!
pattern
[
npat
])
err
=
gpg_error_from_syserror
();
else
{
npat
++
;
quiet
=
1
;
}
}
else
if
(
desc
[
idx
].
mode
==
KEYDB_SEARCH_MODE_NONE
)
continue
;
else
BUG
();
if
(
err
)
{
for
(
idx
=
0
;
idx
<
npat
;
idx
++
)
xfree
(
pattern
[
idx
]);
xfree
(
pattern
);
return
err
;
}
if
(
!
quiet
&&
keyserver
)
{
if
(
keyserver
->
host
)
log_info
(
_
(
"requesting key %s from %s server %s
\n
"
),
keystr_from_desc
(
&
desc
[
idx
]),
keyserver
->
scheme
,
keyserver
->
host
);
else
log_info
(
_
(
"requesting key %s from %s
\n
"
),
keystr_from_desc
(
&
desc
[
idx
]),
keyserver
->
uri
);
}
}
err
=
gpg_dirmngr_ks_get
(
ctrl
,
pattern
,
&
datastream
);
for
(
idx
=
0
;
idx
<
npat
;
idx
++
)
xfree
(
pattern
[
idx
]);
xfree
(
pattern
);
if
(
!
err
)
{
void
*
stats_handle
;
stats_handle
=
import_new_stats_handle
();
/* FIXME: Check whether this comment should be moved to dirmngr.
Slurp up all the key data. In the future, it might be nice
to look for KEY foo OUTOFBAND and FAILED indicators. It's
harmless to ignore them, but ignoring them does make gpg
complain about "no valid OpenPGP data found". One way to do
this could be to continue parsing this line-by-line and make
a temp iobuf for each key. */
import_keys_es_stream
(
ctrl
,
datastream
,
stats_handle
,
NULL
,
NULL
,
opt
.
keyserver_options
.
import_options
);
import_print_stats
(
stats_handle
);
import_release_stats_handle
(
stats_handle
);
}
es_fclose
(
datastream
);
return
err
;
}
/* Send all keys specified by KEYSPECS to the KEYSERVERS. */
static
gpg_error_t
keyserver_put
(
ctrl_t
ctrl
,
strlist_t
keyspecs
,
struct
keyserver_spec
*
keyserver
)
{
gpg_error_t
err
;
strlist_t
kspec
;
if
(
!
keyspecs
)
return
0
;
/* Return success if the list is empty. */
if
(
!
opt
.
keyserver
)
{
log_error
(
_
(
"no keyserver known (use option --keyserver)
\n
"
));
return
gpg_error
(
GPG_ERR_NO_KEYSERVER
);
}
for
(
kspec
=
keyspecs
;
kspec
;
kspec
=
kspec
->
next
)
{
void
*
data
;
size_t
datalen
;
kbnode_t
keyblock
;
err
=
export_pubkey_buffer
(
ctrl
,
kspec
->
d
,
opt
.
keyserver_options
.
export_options
,
&
keyblock
,
&
data
,
&
datalen
);
if
(
err
)
log_error
(
_
(
"skipped
\"
%s
\"
: %s
\n
"
),
kspec
->
d
,
gpg_strerror
(
err
));
else
{
if
(
keyserver
->
host
)
log_info
(
_
(
"sending key %s to %s server %s
\n
"
),
keystr
(
keyblock
->
pkt
->
pkt
.
public_key
->
keyid
),
keyserver
->
scheme
,
keyserver
->
host
);
else
log_info
(
_
(
"sending key %s to %s
\n
"
),
keystr
(
keyblock
->
pkt
->
pkt
.
public_key
->
keyid
),
keyserver
->
uri
);
err
=
gpg_dirmngr_ks_put
(
ctrl
,
data
,
datalen
,
keyblock
);
release_kbnode
(
keyblock
);
xfree
(
data
);
if
(
err
)
log_error
(
_
(
"keyserver send failed: %s
\n
"
),
gpg_strerror
(
err
));
}
}
return
err
;
}
int
keyserver_fetch
(
ctrl_t
ctrl
,
strlist_t
urilist
)
{
gpg_error_t
err
;
strlist_t
sl
;
estream_t
datastream
;
unsigned
int
options
=
opt
.
keyserver_options
.
import_options
;
/* Switch on fast-import, since fetch can handle more than one
import and we don't want each set to rebuild the trustdb.
Instead we do it once at the end. */
opt
.
keyserver_options
.
import_options
|=
IMPORT_FAST
;
for
(
sl
=
urilist
;
sl
;
sl
=
sl
->
next
)
{
if
(
!
opt
.
quiet
)
log_info
(
_
(
"requesting key from `%s'
\n
"
),
sl
->
d
);
err
=
gpg_dirmngr_ks_fetch
(
ctrl
,
sl
->
d
,
&
datastream
);
if
(
!
err
)
{
void
*
stats_handle
;
stats_handle
=
import_new_stats_handle
();
import_keys_es_stream
(
ctrl
,
datastream
,
stats_handle
,
NULL
,
NULL
,
opt
.
keyserver_options
.
import_options
);
import_print_stats
(
stats_handle
);
import_release_stats_handle
(
stats_handle
);
}
else
log_info
(
_
(
"WARNING: unable to fetch URI %s: %s
\n
"
),
sl
->
d
,
gpg_strerror
(
err
));
es_fclose
(
datastream
);
}
opt
.
keyserver_options
.
import_options
=
options
;
/* If the original options didn't have fast import, and the trustdb
is dirty, rebuild. */
if
(
!
(
opt
.
keyserver_options
.
import_options
&
IMPORT_FAST
))
trustdb_check_or_update
();
return
0
;
}
/* Import key in a CERT or pointed to by a CERT */
int
keyserver_import_cert
(
ctrl_t
ctrl
,
const
char
*
name
,
unsigned
char
**
fpr
,
size_t
*
fpr_len
)
{
char
*
domain
,
*
look
,
*
url
;
IOBUF
key
;
int
type
,
rc
=
G10ERR_GENERAL
;
look
=
xstrdup
(
name
);
domain
=
strrchr
(
look
,
'@'
);
if
(
domain
)
*
domain
=
'.'
;
type
=
get_dns_cert
(
look
,
max_cert_size
,
&
key
,
fpr
,
fpr_len
,
&
url
);
if
(
!
type
||
type
==
-1
)
{
/* There might be an error in res_query which leads to an error
return (-1) in the case that nothing was found. Thus we take
all errors as key not found. */
rc
=
G10ERR_NO_PUBKEY
;
}
else
if
(
type
==
1
)
{
int
armor_status
=
opt
.
no_armor
;
/* CERTs are always in binary format */
opt
.
no_armor
=
1
;
/* FIXME: Pass CTRL. */
rc
=
import_keys_stream
(
NULL
,
key
,
NULL
,
fpr
,
fpr_len
,
opt
.
keyserver_options
.
import_options
);
opt
.
no_armor
=
armor_status
;
iobuf_close
(
key
);
}
else
if
(
type
==
2
&&
*
fpr
)
{
/* We only consider the IPGP type if a fingerprint was provided.
This lets us select the right key regardless of what a URL
points to, or get the key from a keyserver. */
if
(
url
)
{
struct
keyserver_spec
*
spec
;
spec
=
parse_keyserver_uri
(
url
,
1
,
NULL
,
0
);
if
(
spec
)
{
rc
=
keyserver_import_fprint
(
ctrl
,
*
fpr
,
*
fpr_len
,
spec
);
free_keyserver_spec
(
spec
);
}
}
else
if
(
opt
.
keyserver
)
{
/* If only a fingerprint is provided, try and fetch it from
our --keyserver */
rc
=
keyserver_import_fprint
(
ctrl
,
*
fpr
,
*
fpr_len
,
opt
.
keyserver
);
}
else
log_info
(
_
(
"no keyserver known (use option --keyserver)
\n
"
));
/* Give a better string here? "CERT fingerprint for \"%s\"
found, but no keyserver" " known (use option
--keyserver)\n" ? */
xfree
(
url
);
}
xfree
(
look
);
return
rc
;
}
/* Import key pointed to by a PKA record. Return the requested
fingerprint in fpr. */
int
keyserver_import_pka
(
ctrl_t
ctrl
,
const
char
*
name
,
unsigned
char
**
fpr
,
size_t
*
fpr_len
)
{
char
*
uri
;
int
rc
=
G10ERR_NO_PUBKEY
;
*
fpr
=
xmalloc
(
20
);
*
fpr_len
=
20
;
uri
=
get_pka_info
(
name
,
*
fpr
);
if
(
uri
&&
*
uri
)
{
/* An URI is available. Lookup the key. */
struct
keyserver_spec
*
spec
;
spec
=
parse_keyserver_uri
(
uri
,
1
,
NULL
,
0
);
if
(
spec
)
{
rc
=
keyserver_import_fprint
(
ctrl
,
*
fpr
,
20
,
spec
);
free_keyserver_spec
(
spec
);
}
xfree
(
uri
);
}
if
(
rc
)
{
xfree
(
*
fpr
);
*
fpr
=
NULL
;
}
return
rc
;
}
/* Import all keys that match name */
int
keyserver_import_name
(
ctrl_t
ctrl
,
const
char
*
name
,
unsigned
char
**
fpr
,
size_t
*
fpr_len
,
struct
keyserver_spec
*
keyserver
)
{
strlist_t
list
=
NULL
;
int
rc
;
append_to_strlist
(
&
list
,
name
);
rc
=
gpg_error
(
GPG_ERR_NOT_IMPLEMENTED
);
/* FIXME */
/* keyserver_work (ctrl, KS_GETNAME, list, NULL, */
/* 0, fpr, fpr_len, keyserver); */
free_strlist
(
list
);
return
rc
;
}
/* Import a key by name using LDAP */
int
keyserver_import_ldap
(
ctrl_t
ctrl
,
const
char
*
name
,
unsigned
char
**
fpr
,
size_t
*
fpr_len
)
{
char
*
domain
;
struct
keyserver_spec
*
keyserver
;
strlist_t
list
=
NULL
;
int
rc
,
hostlen
=
1
;
#ifdef USE_DNS_SRV
struct
srventry
*
srvlist
=
NULL
;
int
srvcount
,
i
;
char
srvname
[
MAXDNAME
];
#endif
/* Parse out the domain */
domain
=
strrchr
(
name
,
'@'
);
if
(
!
domain
)
return
G10ERR_GENERAL
;
domain
++
;
keyserver
=
xmalloc_clear
(
sizeof
(
struct
keyserver_spec
));
keyserver
->
scheme
=
xstrdup
(
"ldap"
);
keyserver
->
host
=
xmalloc
(
1
);
keyserver
->
host
[
0
]
=
'\0'
;
#ifdef USE_DNS_SRV
snprintf
(
srvname
,
MAXDNAME
,
"_pgpkey-ldap._tcp.%s"
,
domain
);
srvcount
=
getsrv
(
srvname
,
&
srvlist
);
for
(
i
=
0
;
i
<
srvcount
;
i
++
)
{
hostlen
+=
strlen
(
srvlist
[
i
].
target
)
+
1
;
keyserver
->
host
=
xrealloc
(
keyserver
->
host
,
hostlen
);
strcat
(
keyserver
->
host
,
srvlist
[
i
].
target
);
if
(
srvlist
[
i
].
port
!=
389
)
{
char
port
[
7
];
hostlen
+=
6
;
/* a colon, plus 5 digits (unsigned 16-bit value) */
keyserver
->
host
=
xrealloc
(
keyserver
->
host
,
hostlen
);
snprintf
(
port
,
7
,
":%u"
,
srvlist
[
i
].
port
);
strcat
(
keyserver
->
host
,
port
);
}
strcat
(
keyserver
->
host
,
" "
);
}
free
(
srvlist
);
#endif
/* If all else fails, do the PGP Universal trick of
ldap://keys.(domain) */
hostlen
+=
5
+
strlen
(
domain
);
keyserver
->
host
=
xrealloc
(
keyserver
->
host
,
hostlen
);
strcat
(
keyserver
->
host
,
"keys."
);
strcat
(
keyserver
->
host
,
domain
);
append_to_strlist
(
&
list
,
name
);
rc
=
gpg_error
(
GPG_ERR_NOT_IMPLEMENTED
);
/*FIXME*/
/* keyserver_work (ctrl, KS_GETNAME, list, NULL, */
/* 0, fpr, fpr_len, keyserver); */
free_strlist
(
list
);
free_keyserver_spec
(
keyserver
);
return
rc
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Fri, Dec 5, 6:01 PM (1 d, 1 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
ab/11/d287765d77e6b7ab1c253e872134
Attached To
rG GnuPG
Event Timeline
Log In to Comment