1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-12-22 10:19:57 +01:00

dirmngr: Return modifyTimestamp and add server option --newer.

* dirmngr/server.c (cmd_ks_get): Add option --newer.
(cmd_ad_query): Ditto.
* dirmngr/ldap-misc.c (isotime2rfc4517): New.
(rfc4517toisotime): New.
* dirmngr/ks-action.c (ks_action_get): Add arg newer and pass on.
(ks_action_query): Ditto.
* dirmngr/ks-engine-ldap.c (extract_keys): Print new "chg" record.
(ks_ldap_get): Add arg newer.  Modify filter with newer arg.
(ks_ldap_search): Print the modifyTimestamp.
(ks_ldap_query): Add arg newer.  Modify filter with newer arg.
--

Note that the modifyTimestamp is also available on Windows, where its
value is more commonly known as whenChanged.  Both are constructed
attributes.

Note that the --newer option is a bit of a misnomer because LDAP has
only a greater-or-equal and no greater-than operator.
This commit is contained in:
Werner Koch 2023-04-04 08:49:55 +02:00
parent a5360ae4c7
commit 56d309133f
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
7 changed files with 195 additions and 27 deletions

View File

@ -337,7 +337,8 @@ ks_action_search (ctrl_t ctrl, uri_item_t keyservers,
keyservers and write the result to the provided output stream. */ keyservers and write the result to the provided output stream. */
gpg_error_t gpg_error_t
ks_action_get (ctrl_t ctrl, uri_item_t keyservers, ks_action_get (ctrl_t ctrl, uri_item_t keyservers,
strlist_t patterns, unsigned int ks_get_flags, estream_t outfp) strlist_t patterns, unsigned int ks_get_flags,
gnupg_isotime_t newer, estream_t outfp)
{ {
gpg_error_t err = 0; gpg_error_t err = 0;
gpg_error_t first_err = 0; gpg_error_t first_err = 0;
@ -382,7 +383,7 @@ ks_action_get (ctrl_t ctrl, uri_item_t keyservers,
#if USE_LDAP #if USE_LDAP
if (is_ldap) if (is_ldap)
err = ks_ldap_get (ctrl, uri->parsed_uri, sl->d, ks_get_flags, err = ks_ldap_get (ctrl, uri->parsed_uri, sl->d, ks_get_flags,
&infp); newer, &infp);
else else
#endif #endif
if (is_hkp_s) if (is_hkp_s)
@ -549,7 +550,8 @@ ks_action_put (ctrl_t ctrl, uri_item_t keyservers,
* the filter expression FILTER. Write the result to OUTFP. */ * the filter expression FILTER. Write the result to OUTFP. */
gpg_error_t gpg_error_t
ks_action_query (ctrl_t ctrl, const char *url, unsigned int ks_get_flags, ks_action_query (ctrl_t ctrl, const char *url, unsigned int ks_get_flags,
const char *filter, char **attrs, estream_t outfp) const char *filter, char **attrs,
gnupg_isotime_t newer, estream_t outfp)
{ {
#if USE_LDAP #if USE_LDAP
gpg_error_t err; gpg_error_t err;
@ -576,7 +578,7 @@ ks_action_query (ctrl_t ctrl, const char *url, unsigned int ks_get_flags,
|| puri->parsed_uri->opaque) || puri->parsed_uri->opaque)
{ {
err = ks_ldap_query (ctrl, puri->parsed_uri, ks_get_flags, filter, err = ks_ldap_query (ctrl, puri->parsed_uri, ks_get_flags, filter,
attrs, &infp); attrs, newer, &infp);
if (!err) if (!err)
err = copy_stream (infp, outfp); err = copy_stream (infp, outfp);
} }

View File

@ -28,14 +28,15 @@ gpg_error_t ks_action_search (ctrl_t ctrl, uri_item_t keyservers,
strlist_t patterns, estream_t outfp); strlist_t patterns, estream_t outfp);
gpg_error_t ks_action_get (ctrl_t ctrl, uri_item_t keyservers, gpg_error_t ks_action_get (ctrl_t ctrl, uri_item_t keyservers,
strlist_t patterns, unsigned int ks_get_flags, strlist_t patterns, unsigned int ks_get_flags,
estream_t outfp); gnupg_isotime_t newer, estream_t outfp);
gpg_error_t ks_action_fetch (ctrl_t ctrl, const char *url, estream_t outfp); gpg_error_t ks_action_fetch (ctrl_t ctrl, const char *url, estream_t outfp);
gpg_error_t ks_action_put (ctrl_t ctrl, uri_item_t keyservers, gpg_error_t ks_action_put (ctrl_t ctrl, uri_item_t keyservers,
void *data, size_t datalen, void *data, size_t datalen,
void *info, size_t infolen); void *info, size_t infolen);
gpg_error_t ks_action_query (ctrl_t ctrl, const char *ldapserver, gpg_error_t ks_action_query (ctrl_t ctrl, const char *ldapserver,
unsigned int ks_get_flags, unsigned int ks_get_flags,
const char *filter, char **attr, estream_t outfp); const char *filter, char **attr,
gnupg_isotime_t newer, estream_t outfp);
#endif /*DIRMNGR_KS_ACTION_H*/ #endif /*DIRMNGR_KS_ACTION_H*/

View File

@ -1004,6 +1004,15 @@ extract_keys (estream_t output,
} }
my_ldap_value_free (vals); my_ldap_value_free (vals);
vals = ldap_get_values (ldap_conn, message, "modifyTimestamp");
if (vals && vals[0])
{
gnupg_isotime_t atime;
if (!rfc4517toisotime (atime, vals[0]))
es_fprintf (output, "chg:%s:\n", atime);
}
my_ldap_value_free (vals);
es_fprintf (output, "INFO %s END\n", certid); es_fprintf (output, "INFO %s END\n", certid);
} }
@ -1368,7 +1377,7 @@ fetch_rootdse (ctrl_t ctrl, parsed_uri_t uri)
|| puri->parsed_uri->opaque) || puri->parsed_uri->opaque)
{ {
err = ks_ldap_query (ctrl, puri->parsed_uri, KS_GET_FLAG_ROOTDSE, err = ks_ldap_query (ctrl, puri->parsed_uri, KS_GET_FLAG_ROOTDSE,
"^&base&(objectclass=*)", NULL, &infp); "^&base&(objectclass=*)", NULL, NULL, &infp);
if (err) if (err)
log_error ("ldap: reading the rootDES failed: %s\n", log_error ("ldap: reading the rootDES failed: %s\n",
gpg_strerror (err)); gpg_strerror (err));
@ -1417,7 +1426,7 @@ basedn_from_rootdse (ctrl_t ctrl, parsed_uri_t uri)
* data. KS_GET_FLAGS conveys flags from the client. */ * data. KS_GET_FLAGS conveys flags from the client. */
gpg_error_t gpg_error_t
ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec,
unsigned int ks_get_flags, estream_t *r_fp) unsigned int ks_get_flags, gnupg_isotime_t newer, estream_t *r_fp)
{ {
gpg_error_t err; gpg_error_t err;
unsigned int serverinfo; unsigned int serverinfo;
@ -1442,7 +1451,7 @@ ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec,
{ {
"dummy", /* (to be be replaced.) */ "dummy", /* (to be be replaced.) */
"pgpcertid", "pgpuserid", "pgpkeyid", "pgprevoked", "pgpdisabled", "pgpcertid", "pgpuserid", "pgpkeyid", "pgprevoked", "pgpdisabled",
"pgpkeycreatetime", "modifytimestamp", "pgpkeysize", "pgpkeytype", "pgpkeycreatetime", "modifyTimestamp", "pgpkeysize", "pgpkeytype",
"gpgfingerprint", "gpgfingerprint",
NULL NULL
}; };
@ -1542,6 +1551,28 @@ ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec,
if (err) if (err)
goto leave; goto leave;
if (*newer)
{
char *tstr, *fstr;
tstr = isotime2rfc4517 (newer);
if (!tstr)
{
err = gpg_error_from_syserror ();
goto leave;
}
fstr = strconcat ("(&", filter,
"(modifyTimestamp>=", tstr, "))", NULL);
xfree (tstr);
if (!fstr)
{
err = gpg_error_from_syserror ();
goto leave;
}
xfree (filter);
filter = fstr;
}
if (opt.debug) if (opt.debug)
log_debug ("ks-ldap: using filter: %s\n", filter); log_debug ("ks-ldap: using filter: %s\n", filter);
@ -1697,7 +1728,7 @@ ks_ldap_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern,
char *attrs[] = char *attrs[] =
{ {
"pgpcertid", "pgpuserid", "pgprevoked", "pgpdisabled", "pgpcertid", "pgpuserid", "pgprevoked", "pgpdisabled",
"pgpkeycreatetime", "pgpkeyexpiretime", "modifytimestamp", "pgpkeycreatetime", "pgpkeyexpiretime", "modifyTimestamp",
"pgpkeysize", "pgpkeytype", "gpgfingerprint", "pgpkeysize", "pgpkeytype", "gpgfingerprint",
NULL NULL
}; };
@ -1851,19 +1882,17 @@ ks_ldap_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern,
} }
my_ldap_value_free (vals); my_ldap_value_free (vals);
#if 0
/* This is not yet specified in the keyserver
protocol, but may be someday. */
es_fputc (':', fp); es_fputc (':', fp);
vals = ldap_get_values (ldap_conn, each, "modifytimestamp"); vals = ldap_get_values (ldap_conn, each, "modifyTimestamp");
if(vals && vals[0] strlen (vals[0]) == 15) if(vals && vals[0])
{ {
es_fprintf (fp, "%u", gnupg_isotime_t atime;
(unsigned int) ldap2epochtime (vals[0])); if (rfc4517toisotime (atime, vals[0]))
*atime = 0;
es_fprintf (fp, "%s", atime);
} }
my_ldap_value_free (vals); my_ldap_value_free (vals);
#endif
es_fprintf (fp, "\n"); es_fprintf (fp, "\n");
@ -2785,7 +2814,8 @@ ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri,
* return or NULL for all. */ * return or NULL for all. */
gpg_error_t gpg_error_t
ks_ldap_query (ctrl_t ctrl, parsed_uri_t uri, unsigned int ks_get_flags, ks_ldap_query (ctrl_t ctrl, parsed_uri_t uri, unsigned int ks_get_flags,
const char *filter_arg, char **attrs, estream_t *r_fp) const char *filter_arg, char **attrs,
gnupg_isotime_t newer, estream_t *r_fp)
{ {
gpg_error_t err; gpg_error_t err;
unsigned int serverinfo; unsigned int serverinfo;
@ -2823,6 +2853,30 @@ ks_ldap_query (ctrl_t ctrl, parsed_uri_t uri, unsigned int ks_get_flags,
err = ldap_parse_extfilter (filter_arg, 0, &basedn, &scope, &filter); err = ldap_parse_extfilter (filter_arg, 0, &basedn, &scope, &filter);
if (err) if (err)
goto leave; goto leave;
if (newer && *newer)
{
char *tstr, *fstr;
tstr = isotime2rfc4517 (newer);
if (!tstr)
{
err = gpg_error_from_syserror ();
goto leave;
}
if (filter && *filter)
fstr = strconcat ("(&", filter,
"(modifyTimestamp>=", tstr, "))", NULL);
else
fstr = strconcat ("(modifyTimestamp>=", tstr, ")", NULL);
xfree (tstr);
if (!fstr)
{
err = gpg_error_from_syserror ();
goto leave;
}
xfree (filter);
filter = fstr;
}
} }

View File

@ -76,13 +76,14 @@ gpg_error_t ks_ldap_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern,
estream_t *r_fp); estream_t *r_fp);
gpg_error_t ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, gpg_error_t ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri,
const char *keyspec, unsigned int ks_get_flags, const char *keyspec, unsigned int ks_get_flags,
estream_t *r_fp); gnupg_isotime_t newer, estream_t *r_fp);
gpg_error_t ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri, gpg_error_t ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri,
void *data, size_t datalen, void *data, size_t datalen,
void *info, size_t infolen); void *info, size_t infolen);
gpg_error_t ks_ldap_query (ctrl_t ctrl, parsed_uri_t uri, gpg_error_t ks_ldap_query (ctrl_t ctrl, parsed_uri_t uri,
unsigned int ks_get_flags, unsigned int ks_get_flags,
const char *filter, char **attrs, estream_t *r_fp); const char *filter, char **attrs,
gnupg_isotime_t newer, estream_t *r_fp);
#endif /*DIRMNGR_KS_ENGINE_H*/ #endif /*DIRMNGR_KS_ENGINE_H*/

View File

@ -332,3 +332,90 @@ ldap_parse_extfilter (const char *string, int silent,
} }
return err; return err;
} }
/* Scan an ISO timestamp and return a Generalized Time according to
* RFC-4517. The only supported format is "yyyymmddThhmmss[Z]"
* delimited by white space, nul, a colon or a comma. Returns a
* malloced string or NULL for an invalid string or on memory
* error. */
char *
isotime2rfc4517 (const char *string)
{
int year, month, day, hour, minu, sec;
if (!isotime_p (string))
{
errno = 0;
return NULL;
}
year = atoi_4 (string);
month = atoi_2 (string + 4);
day = atoi_2 (string + 6);
hour = atoi_2 (string + 9);
minu = atoi_2 (string + 11);
sec = atoi_2 (string + 13);
/* Basic checks (1600 due to the LDAP time format base) */
if (year < 1600 || month < 1 || month > 12 || day < 1 || day > 31
|| hour > 23 || minu > 59 || sec > 61 )
{
errno = 0;
return NULL;
}
return gpgrt_bsprintf ("%04d%02d%02d%02d%02d%02d.0Z",
year, month, day, hour, minu, sec);
}
/* Parse an LDAP Generalized Time string and update the provided
* isotime buffer. On error return and error code. */
gpg_error_t
rfc4517toisotime (gnupg_isotime_t timebuf, const char *string)
{
int i;
int year, month, day, hour, minu, sec;
const char *s;
for (i=0, s=string; i < 10; i++, s++) /* Need yyyymmddhh */
if (!digitp (s))
return gpg_error (GPG_ERR_INV_TIME);
year = atoi_4 (string);
month = atoi_2 (string + 4);
day = atoi_2 (string + 6);
hour = atoi_2 (string + 9);
minu = 0;
sec = 0;
if (digitp (s) && digitp (s+1))
{
minu = atoi_2 (s);
s += 2;
if (digitp (s) && digitp (s+1))
{
sec = atoi_2 (s);
s += 2;
}
}
if (*s == '.' || *s == ',')
{
s++;
if (!digitp (s)) /* At least one digit of the fraction required. */
return gpg_error (GPG_ERR_INV_TIME);
s++;
while (digitp (s))
s++;
}
if (*s == 'Z' && (!s[1] || spacep (s+1)))
; /* stop here. */
else if (*s == '-' || *s == '+')
return gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* FIXME */
else
return gpg_error (GPG_ERR_INV_TIME);
snprintf (timebuf, sizeof (gnupg_isotime_t), "%04d%02d%02dT%02d%02d%02d",
year, month, day, hour, minu, sec);
return 0;
}

View File

@ -38,6 +38,8 @@ gpg_err_code_t ldap_err_to_gpg_err (int code);
gpg_err_code_t ldap_to_gpg_err (LDAP *ld); gpg_err_code_t ldap_to_gpg_err (LDAP *ld);
gpg_error_t ldap_parse_extfilter (const char *string, int silent, gpg_error_t ldap_parse_extfilter (const char *string, int silent,
char **r_base, int *r_scope, char **r_filter); char **r_base, int *r_scope, char **r_filter);
char *isotime2rfc4517 (const char *string);
gpg_error_t rfc4517toisotime (gnupg_isotime_t timebuf, const char *string);
#endif /*DIRMNGR_LDAP_MISC_H*/ #endif /*DIRMNGR_LDAP_MISC_H*/

View File

@ -2461,22 +2461,28 @@ cmd_ks_search (assuan_context_t ctx, char *line)
static const char hlp_ks_get[] = static const char hlp_ks_get[] =
"KS_GET [--quick] [--ldap] [--first|--next] {<pattern>}\n" "KS_GET [--quick] [--newer=TIME] [--ldap] [--first|--next] {<pattern>}\n"
"\n" "\n"
"Get the keys matching PATTERN from the configured OpenPGP keyservers\n" "Get the keys matching PATTERN from the configured OpenPGP keyservers\n"
"(see command KEYSERVER). Each pattern should be a keyid, a fingerprint,\n" "(see command KEYSERVER). Each pattern should be a keyid, a fingerprint,\n"
"or an exact name indicated by the '=' prefix. Option --quick uses a\n" "or an exact name indicated by the '=' prefix. Option --quick uses a\n"
"shorter timeout; --ldap will use only ldap servers. With --first only\n" "shorter timeout; --ldap will use only ldap servers. With --first only\n"
"the first item is returned; --next is used to return the next item"; "the first item is returned; --next is used to return the next item\n"
"Option --newer works only with certain LDAP servers.";
static gpg_error_t static gpg_error_t
cmd_ks_get (assuan_context_t ctx, char *line) cmd_ks_get (assuan_context_t ctx, char *line)
{ {
ctrl_t ctrl = assuan_get_pointer (ctx); ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err; gpg_error_t err;
strlist_t list, sl; strlist_t list = NULL;
strlist_t sl;
const char *s;
char *p; char *p;
estream_t outfp; estream_t outfp;
unsigned int flags = 0; unsigned int flags = 0;
gnupg_isotime_t opt_newer;
*opt_newer = 0;
if (has_option (line, "--quick")) if (has_option (line, "--quick"))
ctrl->timeout = opt.connect_quick_timeout; ctrl->timeout = opt.connect_quick_timeout;
@ -2486,13 +2492,18 @@ cmd_ks_get (assuan_context_t ctx, char *line)
flags |= KS_GET_FLAG_FIRST; flags |= KS_GET_FLAG_FIRST;
if (has_option (line, "--next")) if (has_option (line, "--next"))
flags |= KS_GET_FLAG_NEXT; flags |= KS_GET_FLAG_NEXT;
if ((s = option_value (line, "--newer"))
&& !string2isotime (opt_newer, s))
{
err = set_error (GPG_ERR_SYNTAX, "invalid time format");
goto leave;
}
line = skip_options (line); line = skip_options (line);
/* Break the line into a strlist. Each pattern is by /* Break the line into a strlist. Each pattern is by
definition percent-plus escaped. However we only support keyids definition percent-plus escaped. However we only support keyids
and fingerprints and thus the client has no need to apply the and fingerprints and thus the client has no need to apply the
escaping. */ escaping. */
list = NULL;
for (p=line; *p; line = p) for (p=line; *p; line = p)
{ {
while (*p && *p != ' ') while (*p && *p != ' ')
@ -2569,7 +2580,7 @@ cmd_ks_get (assuan_context_t ctx, char *line)
ctrl->server_local->inhibit_data_logging_now = 0; ctrl->server_local->inhibit_data_logging_now = 0;
ctrl->server_local->inhibit_data_logging_count = 0; ctrl->server_local->inhibit_data_logging_count = 0;
err = ks_action_get (ctrl, ctrl->server_local->keyservers, err = ks_action_get (ctrl, ctrl->server_local->keyservers,
list, flags, outfp); list, flags, opt_newer, outfp);
es_fclose (outfp); es_fclose (outfp);
ctrl->server_local->inhibit_data_logging = 0; ctrl->server_local->inhibit_data_logging = 0;
} }
@ -2710,6 +2721,10 @@ cmd_ad_query (assuan_context_t ctx, char *line)
estream_t outfp = NULL; estream_t outfp = NULL;
char *p; char *p;
char **opt_attr = NULL; char **opt_attr = NULL;
const char *s;
gnupg_isotime_t opt_newer;
*opt_newer = 0;
/* No options for now. */ /* No options for now. */
if (has_option (line, "--first")) if (has_option (line, "--first"))
@ -2718,6 +2733,12 @@ cmd_ad_query (assuan_context_t ctx, char *line)
flags |= KS_GET_FLAG_NEXT; flags |= KS_GET_FLAG_NEXT;
if (has_option (line, "--rootdse")) if (has_option (line, "--rootdse"))
flags |= KS_GET_FLAG_ROOTDSE; flags |= KS_GET_FLAG_ROOTDSE;
if ((s = option_value (line, "--newer"))
&& !string2isotime (opt_newer, s))
{
err = set_error (GPG_ERR_SYNTAX, "invalid time format");
goto leave;
}
err = get_option_value (line, "--attr", &p); err = get_option_value (line, "--attr", &p);
if (err) if (err)
goto leave; goto leave;
@ -2758,7 +2779,7 @@ cmd_ad_query (assuan_context_t ctx, char *line)
err = ks_action_query (ctrl, err = ks_action_query (ctrl,
(flags & KS_GET_FLAG_ROOTDSE)? NULL : "ldap:///", (flags & KS_GET_FLAG_ROOTDSE)? NULL : "ldap:///",
flags, filter, opt_attr, outfp); flags, filter, opt_attr, opt_newer, outfp);
leave: leave:
es_fclose (outfp); es_fclose (outfp);