mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
kbx/
* keybox.h (KEYBOX_FLAG_BLOB_SECRET, KEYBOX_FLAG_BLOB_EPHEMERAL): New. * keybox-update.c (keybox_compress): Use it here instead of a magic constant. sm/ * fingerprint.c (gpgsm_get_fingerprint): Add caching. (gpgsm_get_fingerprint_string): Use bin2hexcolon(). (gpgsm_get_fingerprint_hexstring): Use bin2hex and allocate only as much memory as required. (gpgsm_get_keygrip_hexstring): Use bin2hex. * certchain.c (gpgsm_validate_chain): Keep track of the certificate chain and reset the ephemeral flags. * keydb.c (keydb_set_cert_flags): New args EPHEMERAL and MASK. Changed caller to use a mask of ~0. Return a proper error code if the certificate is not available.
This commit is contained in:
parent
75f761e6ef
commit
12cc96a176
1
TODO
1
TODO
@ -12,7 +12,6 @@
|
||||
(i.e. Assuan context).
|
||||
|
||||
* sm/certchain.c
|
||||
** When a certificate chain was sucessfully verified, make ephemeral certs used in this chain permanent.
|
||||
** Try to keep certificate references somewhere
|
||||
This will help with some of our caching code. We also need to test
|
||||
that caching; in particular "regtp_ca_chainlen".
|
||||
|
@ -1,3 +1,10 @@
|
||||
2007-03-20 Werner Koch <wk@g10code.com>
|
||||
|
||||
* keybox.h (KEYBOX_FLAG_BLOB_SECRET, KEYBOX_FLAG_BLOB_EPHEMERAL):
|
||||
New.
|
||||
* keybox-update.c (keybox_compress): Use it here instead of a
|
||||
magic constant.
|
||||
|
||||
2007-01-31 Werner Koch <wk@g10code.com>
|
||||
|
||||
* Makefile.am (kbxutil_LDADD): Use GPG_ERROR_LIBS instead of -l.
|
||||
|
@ -23,7 +23,7 @@
|
||||
/* The keybox data formats
|
||||
|
||||
The KeyBox uses an augmented OpenPGP/X.509 key format. This makes
|
||||
random access to a keyblock/Certificate easier and also gives the
|
||||
random access to a keyblock/certificate easier and also gives the
|
||||
opportunity to store additional information (e.g. the fingerprint)
|
||||
along with the key. All integers are stored in network byte order,
|
||||
offsets are counted from the beginning of the Blob.
|
||||
|
@ -828,7 +828,7 @@ keybox_search (KEYBOX_HANDLE hd, KEYBOX_SEARCH_DESC *desc, size_t ndesc)
|
||||
|
||||
blobflags = blob_get_blob_flags (blob);
|
||||
if (!hd->ephemeral && (blobflags & 2))
|
||||
continue; /* not in ephemeral mode but blob is flagged ephemeral */
|
||||
continue; /* Not in ephemeral mode but blob is flagged ephemeral. */
|
||||
|
||||
for (n=0; n < ndesc; n++)
|
||||
{
|
||||
|
@ -452,7 +452,7 @@ keybox_set_flags (KEYBOX_HANDLE hd, int what, int idx, unsigned int value)
|
||||
ec = _keybox_get_flag_location (buffer, length, what, &flag_pos, &flag_size);
|
||||
if (ec)
|
||||
return gpg_error (ec);
|
||||
|
||||
|
||||
off += flag_pos;
|
||||
|
||||
if (hd->fp)
|
||||
@ -631,7 +631,7 @@ keybox_compress (KEYBOX_HANDLE hd)
|
||||
|
||||
|
||||
/* Processing loop. By reading using _keybox_read_blob we
|
||||
automagically skip and blobs flagged as deleted. Thus what we
|
||||
automagically skip any blobs flagged as deleted. Thus what we
|
||||
only have to do is to check all ephemeral flagged blocks whether
|
||||
their time has come and write out all other blobs. */
|
||||
cut_time = time(NULL) - 86400;
|
||||
@ -682,7 +682,7 @@ keybox_compress (KEYBOX_HANDLE hd)
|
||||
break;
|
||||
}
|
||||
blobflags = ((buffer[pos] << 8) | (buffer[pos+1]));
|
||||
if ((blobflags & 2))
|
||||
if ((blobflags & KEYBOX_FLAG_BLOB_EPHEMERAL))
|
||||
{
|
||||
/* This is an ephemeral blob. */
|
||||
if (_keybox_get_flag_location (buffer, length,
|
||||
|
@ -58,6 +58,11 @@ typedef enum
|
||||
KEYBOX_FLAG_CREATED_AT /* The date the block was created. */
|
||||
} keybox_flag_t;
|
||||
|
||||
/* Flag values used with KEYBOX_FLAG_BLOB. */
|
||||
#define KEYBOX_FLAG_BLOB_SECRET 1
|
||||
#define KEYBOX_FLAG_BLOB_EPHEMERAL 2
|
||||
|
||||
|
||||
|
||||
/*-- keybox-init.c --*/
|
||||
void *keybox_register_file (const char *fname, int secret);
|
||||
|
12
sm/ChangeLog
12
sm/ChangeLog
@ -1,5 +1,17 @@
|
||||
2007-03-20 Werner Koch <wk@g10code.com>
|
||||
|
||||
* fingerprint.c (gpgsm_get_fingerprint): Add caching.
|
||||
(gpgsm_get_fingerprint_string): Use bin2hexcolon().
|
||||
(gpgsm_get_fingerprint_hexstring): Use bin2hex and allocate only
|
||||
as much memory as required.
|
||||
(gpgsm_get_keygrip_hexstring): Use bin2hex.
|
||||
|
||||
* certchain.c (gpgsm_validate_chain): Keep terack of the
|
||||
certificate chain and reset the ephemeral flags.
|
||||
* keydb.c (keydb_set_cert_flags): New args EPHEMERAL MASK.
|
||||
Changed caller to use a mask of ~0. Return a proper error code if
|
||||
the certificate is not available.
|
||||
|
||||
* gpgsm.c: Add option --p12-charset.
|
||||
* gpgsm.h (struct opt): Add p12_charset.
|
||||
* export.c (popen_protect_tool): Use new option.
|
||||
|
116
sm/certchain.c
116
sm/certchain.c
@ -50,6 +50,17 @@ struct marktrusted_info_s
|
||||
static struct marktrusted_info_s *marktrusted_info;
|
||||
|
||||
|
||||
/* While running the validation function we want to keep track of the
|
||||
certificates in the chain. This type is used for that. */
|
||||
struct chain_item_s
|
||||
{
|
||||
struct chain_item_s *next;
|
||||
ksba_cert_t cert; /* The certificate. */
|
||||
int is_root; /* The certificate is the root certificate. */
|
||||
};
|
||||
typedef struct chain_item_s *chain_item_t;
|
||||
|
||||
|
||||
static int get_regtp_ca_info (ksba_cert_t cert, int *chainlen);
|
||||
|
||||
|
||||
@ -222,7 +233,7 @@ check_cert_policy (ksba_cert_t cert, int listmode, estream_t fplist)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* STRING is a line delimited list of certifiate policies as stored
|
||||
/* STRING is a line delimited list of certificate policies as stored
|
||||
in the certificate. The line itself is colon delimited where the
|
||||
first field is the OID of the policy and the second field either
|
||||
N or C for normal or critical extension */
|
||||
@ -489,7 +500,7 @@ find_up (KEYDB_HANDLE kh, ksba_cert_t cert, const char *issuer, int find_next)
|
||||
|
||||
if (rc == -1 && keyid && !find_next)
|
||||
{
|
||||
/* Not found by AIK.issuer_sn. Lets try the AIY.ki
|
||||
/* Not found by AIK.issuer_sn. Lets try the AIK.ki
|
||||
instead. Loop over all certificates with that issuer as
|
||||
subject and stop for the one with a matching
|
||||
subjectKeyIdentifier. */
|
||||
@ -542,7 +553,7 @@ find_up (KEYDB_HANDLE kh, ksba_cert_t cert, const char *issuer, int find_next)
|
||||
rc = keydb_search_subject (kh, issuer);
|
||||
if (rc == -1 && !find_next)
|
||||
{
|
||||
/* Not found, lets see whether we have one in the ephemeral key DB. */
|
||||
/* Not found, let us see whether we have one in the ephemeral key DB. */
|
||||
int old = keydb_set_ephemeral (kh, 1);
|
||||
if (!old)
|
||||
{
|
||||
@ -602,8 +613,8 @@ gpgsm_walk_cert_chain (ksba_cert_t start, ksba_cert_t *r_next)
|
||||
rc = find_up (kh, start, issuer, 0);
|
||||
if (rc)
|
||||
{
|
||||
/* it is quite common not to have a certificate, so better don't
|
||||
print an error here */
|
||||
/* It is quite common not to have a certificate, so better don't
|
||||
print an error here. */
|
||||
if (rc != -1 && opt.verbose > 1)
|
||||
log_error ("failed to find issuer's certificate: rc=%d\n", rc);
|
||||
rc = gpg_error (GPG_ERR_MISSING_CERT);
|
||||
@ -669,8 +680,8 @@ is_cert_still_valid (ctrl_t ctrl, int lm, estream_t fp,
|
||||
/* Store that in the keybox so that key listings are
|
||||
able to return the revoked flag. We don't care
|
||||
about error, though. */
|
||||
keydb_set_cert_flags (subject_cert, KEYBOX_FLAG_VALIDITY, 0,
|
||||
VALIDITY_REVOKED);
|
||||
keydb_set_cert_flags (subject_cert, 1, KEYBOX_FLAG_VALIDITY, 0,
|
||||
~0, VALIDITY_REVOKED);
|
||||
break;
|
||||
case GPG_ERR_NO_CRL_KNOWN:
|
||||
do_list (1, lm, fp, _("no CRL found for certificate"));
|
||||
@ -722,6 +733,8 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
|
||||
from a qualified root certificate.
|
||||
-1 = unknown, 0 = no, 1 = yes. */
|
||||
int lm = listmode;
|
||||
chain_item_t chain = NULL; /* A list of all certificates in the chain. */
|
||||
|
||||
|
||||
gnupg_get_isotime (current_time);
|
||||
if (r_exptime)
|
||||
@ -755,6 +768,22 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
|
||||
gpg_error_t istrusted_rc = -1;
|
||||
struct rootca_flags_s rootca_flags;
|
||||
|
||||
/* Put the certificate on our list. */
|
||||
{
|
||||
chain_item_t ci;
|
||||
|
||||
ci = xtrycalloc (1, sizeof *ci);
|
||||
if (!ci)
|
||||
{
|
||||
rc = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
ksba_cert_ref (subject_cert);
|
||||
ci->cert = subject_cert;
|
||||
ci->next = chain;
|
||||
chain = ci;
|
||||
}
|
||||
|
||||
xfree (issuer);
|
||||
xfree (subject);
|
||||
issuer = ksba_cert_get_issuer (subject_cert, 0);
|
||||
@ -767,10 +796,12 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
|
||||
goto leave;
|
||||
}
|
||||
|
||||
|
||||
/* Is this a self-issued certificate (i.e. the root certificate)? */
|
||||
is_root = (subject && !strcmp (issuer, subject));
|
||||
if (is_root)
|
||||
{
|
||||
chain->is_root = 1;
|
||||
/* Check early whether the certificate is listed as trusted.
|
||||
We used to do this only later but changed it to call the
|
||||
check right here so that we can access special flags
|
||||
@ -1157,7 +1188,7 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for revocations etc. Note that for a root certioficate
|
||||
/* Check for revocations etc. Note that for a root certificate
|
||||
this test is done a second time later. This should eventually
|
||||
be fixed. */
|
||||
if ((flags & 1))
|
||||
@ -1209,32 +1240,69 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
|
||||
}
|
||||
|
||||
leave:
|
||||
if (is_qualified != -1)
|
||||
/* If we have traversed a complete chain up to the root we will
|
||||
reset the ephemeral flag for all these certificates. his is done
|
||||
regardless of any error because those errors may only be
|
||||
transient. */
|
||||
if (chain && chain->is_root)
|
||||
{
|
||||
/* We figured something about the qualified signature capability
|
||||
of the certificate under question. Store the result as user
|
||||
data in the certificate object. We do this even if the
|
||||
validation itself failed. */
|
||||
/* Fixme: We should set this flag for all certificates in the
|
||||
chain for optimizing reasons. */
|
||||
char buf[1];
|
||||
gpg_error_t err;
|
||||
|
||||
buf[0] = !!is_qualified;
|
||||
err = ksba_cert_set_user_data (cert, "is_qualified", buf, 1);
|
||||
if (err)
|
||||
chain_item_t ci;
|
||||
|
||||
for (ci = chain; ci; ci = ci->next)
|
||||
{
|
||||
log_error ("set_user_data(is_qualified) failed: %s\n",
|
||||
gpg_strerror (err));
|
||||
if (!rc)
|
||||
rc = err;
|
||||
/* Note that it is possible for the last certificate in the
|
||||
chain (i.e. our target certificate) that it has not yet
|
||||
been stored in the keybox and thus the flag can't be set.
|
||||
We ignore this error becuase it will later be stored
|
||||
anyway. */
|
||||
err = keydb_set_cert_flags (ci->cert, 1, KEYBOX_FLAG_BLOB, 0,
|
||||
KEYBOX_FLAG_BLOB_EPHEMERAL, 0);
|
||||
if (!ci->next && gpg_err_code (err) == GPG_ERR_NOT_FOUND)
|
||||
;
|
||||
else if (err)
|
||||
log_error ("clearing ephemeral flag failed: %s\n",
|
||||
gpg_strerror (err));
|
||||
}
|
||||
}
|
||||
|
||||
/* If we have figured something about the qualified signature
|
||||
capability of the certificate under question, store the result as
|
||||
user data in all certificates of the chain. We do this even if the
|
||||
validation itself failed. */
|
||||
if (is_qualified != -1)
|
||||
{
|
||||
gpg_error_t err;
|
||||
chain_item_t ci;
|
||||
char buf[1];
|
||||
|
||||
buf[0] = !!is_qualified;
|
||||
|
||||
for (ci = chain; ci; ci = ci->next)
|
||||
{
|
||||
err = ksba_cert_set_user_data (ci->cert, "is_qualified", buf, 1);
|
||||
if (err)
|
||||
{
|
||||
log_error ("set_user_data(is_qualified) failed: %s\n",
|
||||
gpg_strerror (err));
|
||||
if (!rc)
|
||||
rc = err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (r_exptime)
|
||||
gnupg_copy_time (r_exptime, exptime);
|
||||
xfree (issuer);
|
||||
xfree (subject);
|
||||
keydb_release (kh);
|
||||
while (chain)
|
||||
{
|
||||
chain_item_t ci_next = chain->next;
|
||||
ksba_cert_release (chain->cert);
|
||||
xfree (chain);
|
||||
chain = ci_next;
|
||||
}
|
||||
ksba_cert_release (issuer_cert);
|
||||
ksba_cert_release (subject_cert);
|
||||
return rc;
|
||||
|
@ -61,6 +61,19 @@ gpgsm_get_fingerprint (ksba_cert_t cert, int algo,
|
||||
if (r_len)
|
||||
*r_len = len;
|
||||
|
||||
/* Fist check whether we have cached the fingerprint. */
|
||||
if (algo == GCRY_MD_SHA1)
|
||||
{
|
||||
size_t buflen;
|
||||
|
||||
assert (len >= 20);
|
||||
if (!ksba_cert_get_user_data (cert, "sha1-fingerprint",
|
||||
array, len, &buflen)
|
||||
&& buflen == 20)
|
||||
return array;
|
||||
}
|
||||
|
||||
/* No, need to compute it. */
|
||||
rc = gcry_md_open (&md, algo, 0);
|
||||
if (rc)
|
||||
{
|
||||
@ -80,6 +93,11 @@ gpgsm_get_fingerprint (ksba_cert_t cert, int algo,
|
||||
gcry_md_final (md);
|
||||
memcpy (array, gcry_md_read(md, algo), len );
|
||||
gcry_md_close (md);
|
||||
|
||||
/* Cache an SHA-1 fingerprint. */
|
||||
if ( algo == GCRY_MD_SHA1 )
|
||||
ksba_cert_set_user_data (cert, "sha1-fingerprint", array, 20);
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
@ -90,7 +108,7 @@ gpgsm_get_fingerprint_string (ksba_cert_t cert, int algo)
|
||||
{
|
||||
unsigned char digest[MAX_DIGEST_LEN];
|
||||
char *buf;
|
||||
int len, i;
|
||||
int len;
|
||||
|
||||
if (!algo)
|
||||
algo = GCRY_MD_SHA1;
|
||||
@ -99,9 +117,7 @@ gpgsm_get_fingerprint_string (ksba_cert_t cert, int algo)
|
||||
assert (len <= MAX_DIGEST_LEN );
|
||||
gpgsm_get_fingerprint (cert, algo, digest, NULL);
|
||||
buf = xmalloc (len*3+1);
|
||||
*buf = 0;
|
||||
for (i=0; i < len; i++ )
|
||||
sprintf (buf+strlen(buf), i? ":%02X":"%02X", digest[i]);
|
||||
bin2hexcolon (digest, len, buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
@ -112,7 +128,7 @@ gpgsm_get_fingerprint_hexstring (ksba_cert_t cert, int algo)
|
||||
{
|
||||
unsigned char digest[MAX_DIGEST_LEN];
|
||||
char *buf;
|
||||
int len, i;
|
||||
int len;
|
||||
|
||||
if (!algo)
|
||||
algo = GCRY_MD_SHA1;
|
||||
@ -120,10 +136,8 @@ gpgsm_get_fingerprint_hexstring (ksba_cert_t cert, int algo)
|
||||
len = gcry_md_get_algo_dlen (algo);
|
||||
assert (len <= MAX_DIGEST_LEN );
|
||||
gpgsm_get_fingerprint (cert, algo, digest, NULL);
|
||||
buf = xmalloc (len*3+1);
|
||||
*buf = 0;
|
||||
for (i=0; i < len; i++ )
|
||||
sprintf (buf+strlen(buf), "%02X", digest[i]);
|
||||
buf = xmalloc (len*2+1);
|
||||
bin2hex (digest, len, buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
@ -190,13 +204,11 @@ char *
|
||||
gpgsm_get_keygrip_hexstring (ksba_cert_t cert)
|
||||
{
|
||||
unsigned char grip[20];
|
||||
char *buf, *p;
|
||||
int i;
|
||||
char *buf;
|
||||
|
||||
gpgsm_get_keygrip (cert, grip);
|
||||
buf = p = xmalloc (20*2+1);
|
||||
for (i=0; i < 20; i++, p += 2 )
|
||||
sprintf (p, "%02X", grip[i]);
|
||||
buf = xmalloc (20*2+1);
|
||||
bin2hex (grip, 20, buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,8 @@ print_imported_summary (ctrl_t ctrl, struct stats_s *stats)
|
||||
|
||||
|
||||
static void
|
||||
check_and_store (ctrl_t ctrl, struct stats_s *stats, ksba_cert_t cert, int depth)
|
||||
check_and_store (ctrl_t ctrl, struct stats_s *stats,
|
||||
ksba_cert_t cert, int depth)
|
||||
{
|
||||
int rc;
|
||||
|
||||
|
20
sm/keydb.c
20
sm/keydb.c
@ -722,7 +722,7 @@ keydb_insert_cert (KEYDB_HANDLE hd, ksba_cert_t cert)
|
||||
|
||||
|
||||
|
||||
/* update the current keyblock with KB */
|
||||
/* Update the current keyblock with KB. */
|
||||
int
|
||||
keydb_update_cert (KEYDB_HANDLE hd, ksba_cert_t cert)
|
||||
{
|
||||
@ -1366,7 +1366,9 @@ keydb_store_cert (ksba_cert_t cert, int ephemeral, int *existed)
|
||||
transaction by locating the certificate in the DB and updating the
|
||||
flags. */
|
||||
gpg_error_t
|
||||
keydb_set_cert_flags (ksba_cert_t cert, int which, int idx, unsigned int value)
|
||||
keydb_set_cert_flags (ksba_cert_t cert, int ephemeral,
|
||||
int which, int idx,
|
||||
unsigned int mask, unsigned int value)
|
||||
{
|
||||
KEYDB_HANDLE kh;
|
||||
gpg_error_t err;
|
||||
@ -1386,6 +1388,9 @@ keydb_set_cert_flags (ksba_cert_t cert, int which, int idx, unsigned int value)
|
||||
return gpg_error (GPG_ERR_ENOMEM);;
|
||||
}
|
||||
|
||||
if (ephemeral)
|
||||
keydb_set_ephemeral (kh, 1);
|
||||
|
||||
err = keydb_lock (kh);
|
||||
if (err)
|
||||
{
|
||||
@ -1397,8 +1402,11 @@ keydb_set_cert_flags (ksba_cert_t cert, int which, int idx, unsigned int value)
|
||||
err = keydb_search_fpr (kh, fpr);
|
||||
if (err)
|
||||
{
|
||||
log_error (_("problem re-searching certificate: %s\n"),
|
||||
gpg_strerror (err));
|
||||
if (err == -1)
|
||||
err = gpg_error (GPG_ERR_NOT_FOUND);
|
||||
else
|
||||
log_error (_("problem re-searching certificate: %s\n"),
|
||||
gpg_strerror (err));
|
||||
keydb_release (kh);
|
||||
return err;
|
||||
}
|
||||
@ -1410,6 +1418,9 @@ keydb_set_cert_flags (ksba_cert_t cert, int which, int idx, unsigned int value)
|
||||
keydb_release (kh);
|
||||
return err;
|
||||
}
|
||||
|
||||
value = ((old_value & ~mask) | (value & mask));
|
||||
|
||||
if (value != old_value)
|
||||
{
|
||||
err = keydb_set_flags (kh, which, idx, value);
|
||||
@ -1420,6 +1431,7 @@ keydb_set_cert_flags (ksba_cert_t cert, int which, int idx, unsigned int value)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
keydb_release (kh);
|
||||
return 0;
|
||||
}
|
||||
|
@ -74,8 +74,9 @@ int keydb_search_subject (KEYDB_HANDLE hd, const char *issuer);
|
||||
int keydb_classify_name (const char *name, KEYDB_SEARCH_DESC *desc);
|
||||
|
||||
int keydb_store_cert (ksba_cert_t cert, int ephemeral, int *existed);
|
||||
gpg_error_t keydb_set_cert_flags (ksba_cert_t cert, int which, int idx,
|
||||
unsigned int value);
|
||||
gpg_error_t keydb_set_cert_flags (ksba_cert_t cert, int ephemeral,
|
||||
int which, int idx,
|
||||
unsigned int mask, unsigned int value);
|
||||
|
||||
void keydb_clear_some_cert_flags (ctrl_t ctrl, strlist_t names);
|
||||
|
||||
|
@ -911,7 +911,7 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd,
|
||||
err = keydb_get_flags (hd, KEYBOX_FLAG_BLOB, 0, &blobflags);
|
||||
if (err)
|
||||
es_fprintf (fp, " [error getting keyflags: %s]\n",gpg_strerror (err));
|
||||
else if ((blobflags & 2))
|
||||
else if ((blobflags & KEYBOX_FLAG_BLOB_EPHEMERAL))
|
||||
es_fprintf (fp, " [stored as ephemeral]\n");
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user