Implement self-signing in 'openssl ca'. This makes it easier to have
the CA certificate part of the CA database, and combined with 'unique_subject=no', it should make operations like CA certificate roll-over easier.
This commit is contained in:
parent
e6526fbf4d
commit
16b1b03543
7
CHANGES
7
CHANGES
@ -4,6 +4,13 @@
|
|||||||
|
|
||||||
Changes between 0.9.7a and 0.9.8 [xx XXX xxxx]
|
Changes between 0.9.7a and 0.9.8 [xx XXX xxxx]
|
||||||
|
|
||||||
|
*) Make it possible to create self-signed certificates with 'openssl ca'
|
||||||
|
in such a way that the self-signed certificate becomes part of the
|
||||||
|
CA database and uses the same mechanisms for serial number generation
|
||||||
|
as all other certificate signing. The new flag '-selfsign' enables
|
||||||
|
this functionality. Adapt CA.sh and CA.pl.in.
|
||||||
|
[Richard Levitte]
|
||||||
|
|
||||||
*) Add functionality to check the public key of a certificate request
|
*) Add functionality to check the public key of a certificate request
|
||||||
against a given private. This is useful to check that a certificate
|
against a given private. This is useful to check that a certificate
|
||||||
request can be signed by that key (self-signing).
|
request can be signed by that key (self-signing).
|
||||||
|
@ -37,7 +37,8 @@
|
|||||||
# demoCA ... where everything is stored
|
# demoCA ... where everything is stored
|
||||||
|
|
||||||
$SSLEAY_CONFIG=$ENV{"SSLEAY_CONFIG"};
|
$SSLEAY_CONFIG=$ENV{"SSLEAY_CONFIG"};
|
||||||
$DAYS="-days 365";
|
$DAYS="-days 365"; # 1 year
|
||||||
|
$CADAYS="-days 1095"; # 3 years
|
||||||
$REQ="openssl req $SSLEAY_CONFIG";
|
$REQ="openssl req $SSLEAY_CONFIG";
|
||||||
$CA="openssl ca $SSLEAY_CONFIG";
|
$CA="openssl ca $SSLEAY_CONFIG";
|
||||||
$VERIFY="openssl verify";
|
$VERIFY="openssl verify";
|
||||||
@ -46,6 +47,7 @@ $PKCS12="openssl pkcs12";
|
|||||||
|
|
||||||
$CATOP="./demoCA";
|
$CATOP="./demoCA";
|
||||||
$CAKEY="cakey.pem";
|
$CAKEY="cakey.pem";
|
||||||
|
$CAREQ="careq.pem";
|
||||||
$CACERT="cacert.pem";
|
$CACERT="cacert.pem";
|
||||||
|
|
||||||
$DIRMODE = 0777;
|
$DIRMODE = 0777;
|
||||||
@ -101,8 +103,11 @@ foreach (@ARGV) {
|
|||||||
$RET=$?;
|
$RET=$?;
|
||||||
} else {
|
} else {
|
||||||
print "Making CA certificate ...\n";
|
print "Making CA certificate ...\n";
|
||||||
system ("$REQ -new -x509 -keyout " .
|
system ("$REQ -new -keyout " .
|
||||||
"${CATOP}/private/$CAKEY -out ${CATOP}/$CACERT $DAYS");
|
"${CATOP}/private/$CAKEY -out ${CATOP}/$CAREQ");
|
||||||
|
system ("$CA -out ${CATOP}/$CACERT $CADAYS -batch " .
|
||||||
|
"-keyfile ${CATOP}/private/$CAKEY -selfsign " .
|
||||||
|
"-infiles ${CATOP}/$CAREQ ");
|
||||||
$RET=$?;
|
$RET=$?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
13
apps/CA.sh
13
apps/CA.sh
@ -30,7 +30,8 @@
|
|||||||
# default openssl.cnf file has setup as per the following
|
# default openssl.cnf file has setup as per the following
|
||||||
# demoCA ... where everything is stored
|
# demoCA ... where everything is stored
|
||||||
|
|
||||||
DAYS="-days 365"
|
DAYS="-days 365" # 1 year
|
||||||
|
CADAYS="-days 1095" # 3 years
|
||||||
REQ="openssl req $SSLEAY_CONFIG"
|
REQ="openssl req $SSLEAY_CONFIG"
|
||||||
CA="openssl ca $SSLEAY_CONFIG"
|
CA="openssl ca $SSLEAY_CONFIG"
|
||||||
VERIFY="openssl verify"
|
VERIFY="openssl verify"
|
||||||
@ -38,6 +39,7 @@ X509="openssl x509"
|
|||||||
|
|
||||||
CATOP=./demoCA
|
CATOP=./demoCA
|
||||||
CAKEY=./cakey.pem
|
CAKEY=./cakey.pem
|
||||||
|
CAREQ=./careq.pem
|
||||||
CACERT=./cacert.pem
|
CACERT=./cacert.pem
|
||||||
|
|
||||||
for i
|
for i
|
||||||
@ -70,7 +72,7 @@ case $i in
|
|||||||
mkdir ${CATOP}/crl
|
mkdir ${CATOP}/crl
|
||||||
mkdir ${CATOP}/newcerts
|
mkdir ${CATOP}/newcerts
|
||||||
mkdir ${CATOP}/private
|
mkdir ${CATOP}/private
|
||||||
echo "01" > ${CATOP}/serial
|
echo "00" > ${CATOP}/serial
|
||||||
touch ${CATOP}/index.txt
|
touch ${CATOP}/index.txt
|
||||||
fi
|
fi
|
||||||
if [ ! -f ${CATOP}/private/$CAKEY ]; then
|
if [ ! -f ${CATOP}/private/$CAKEY ]; then
|
||||||
@ -83,8 +85,11 @@ case $i in
|
|||||||
RET=$?
|
RET=$?
|
||||||
else
|
else
|
||||||
echo "Making CA certificate ..."
|
echo "Making CA certificate ..."
|
||||||
$REQ -new -x509 -keyout ${CATOP}/private/$CAKEY \
|
$REQ -new -keyout ${CATOP}/private/$CAKEY \
|
||||||
-out ${CATOP}/$CACERT $DAYS
|
-out ${CATOP}/$CAREQ
|
||||||
|
$CA -out ${CATOP}/$CACERT $CADAYS -batch \
|
||||||
|
-keyfile ${CATOP}/private/$CAKEY -selfsign \
|
||||||
|
-infiles ${CATOP}/$CAREQ
|
||||||
RET=$?
|
RET=$?
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
90
apps/ca.c
90
apps/ca.c
@ -204,7 +204,7 @@ static int certify(X509 **xret, char *infile,EVP_PKEY *pkey,X509 *x509,
|
|||||||
BIGNUM *serial, char *subj, int email_dn, char *startdate,
|
BIGNUM *serial, char *subj, int email_dn, char *startdate,
|
||||||
char *enddate, long days, int batch, char *ext_sect, CONF *conf,
|
char *enddate, long days, int batch, char *ext_sect, CONF *conf,
|
||||||
int verbose, unsigned long certopt, unsigned long nameopt,
|
int verbose, unsigned long certopt, unsigned long nameopt,
|
||||||
int default_op, int ext_copy);
|
int default_op, int ext_copy, int selfsign);
|
||||||
static int certify_cert(X509 **xret, char *infile,EVP_PKEY *pkey,X509 *x509,
|
static int certify_cert(X509 **xret, char *infile,EVP_PKEY *pkey,X509 *x509,
|
||||||
const EVP_MD *dgst,STACK_OF(CONF_VALUE) *policy,
|
const EVP_MD *dgst,STACK_OF(CONF_VALUE) *policy,
|
||||||
CA_DB *db, BIGNUM *serial, char *subj, int email_dn,
|
CA_DB *db, BIGNUM *serial, char *subj, int email_dn,
|
||||||
@ -225,7 +225,7 @@ static int do_body(X509 **xret, EVP_PKEY *pkey, X509 *x509, const EVP_MD *dgst,
|
|||||||
int email_dn, char *startdate, char *enddate, long days, int batch,
|
int email_dn, char *startdate, char *enddate, long days, int batch,
|
||||||
int verbose, X509_REQ *req, char *ext_sect, CONF *conf,
|
int verbose, X509_REQ *req, char *ext_sect, CONF *conf,
|
||||||
unsigned long certopt, unsigned long nameopt, int default_op,
|
unsigned long certopt, unsigned long nameopt, int default_op,
|
||||||
int ext_copy);
|
int ext_copy, int selfsign);
|
||||||
static int do_revoke(X509 *x509, CA_DB *db, int ext, char *extval);
|
static int do_revoke(X509 *x509, CA_DB *db, int ext, char *extval);
|
||||||
static int get_certificate_status(const char *ser_status, CA_DB *db);
|
static int get_certificate_status(const char *ser_status, CA_DB *db);
|
||||||
static int do_updatedb(CA_DB *db);
|
static int do_updatedb(CA_DB *db);
|
||||||
@ -292,7 +292,8 @@ int MAIN(int argc, char **argv)
|
|||||||
unsigned long nameopt = 0, certopt = 0;
|
unsigned long nameopt = 0, certopt = 0;
|
||||||
int default_op = 1;
|
int default_op = 1;
|
||||||
int ext_copy = EXT_COPY_NONE;
|
int ext_copy = EXT_COPY_NONE;
|
||||||
X509 *x509=NULL;
|
int selfsign = 0;
|
||||||
|
X509 *x509=NULL, *x509p = NULL;
|
||||||
X509 *x=NULL;
|
X509 *x=NULL;
|
||||||
BIO *in=NULL,*out=NULL,*Sout=NULL,*Cout=NULL;
|
BIO *in=NULL,*out=NULL,*Sout=NULL,*Cout=NULL;
|
||||||
char *dbfile=NULL;
|
char *dbfile=NULL;
|
||||||
@ -406,6 +407,8 @@ EF_ALIGNMENT=0;
|
|||||||
if (--argc < 1) goto bad;
|
if (--argc < 1) goto bad;
|
||||||
certfile= *(++argv);
|
certfile= *(++argv);
|
||||||
}
|
}
|
||||||
|
else if (strcmp(*argv,"-selfsign") == 0)
|
||||||
|
selfsign=1;
|
||||||
else if (strcmp(*argv,"-in") == 0)
|
else if (strcmp(*argv,"-in") == 0)
|
||||||
{
|
{
|
||||||
if (--argc < 1) goto bad;
|
if (--argc < 1) goto bad;
|
||||||
@ -700,7 +703,7 @@ bad:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************/
|
/*****************************************************************/
|
||||||
/* we definitely need a public key, so let's get it */
|
/* we definitely need a private key, so let's get it */
|
||||||
|
|
||||||
if ((keyfile == NULL) && ((keyfile=NCONF_get_string(conf,
|
if ((keyfile == NULL) && ((keyfile=NCONF_get_string(conf,
|
||||||
section,ENV_PRIVATE_KEY)) == NULL))
|
section,ENV_PRIVATE_KEY)) == NULL))
|
||||||
@ -728,22 +731,27 @@ bad:
|
|||||||
|
|
||||||
/*****************************************************************/
|
/*****************************************************************/
|
||||||
/* we need a certificate */
|
/* we need a certificate */
|
||||||
if ((certfile == NULL) && ((certfile=NCONF_get_string(conf,
|
if (!selfsign || spkac_file || ss_cert_file || gencrl)
|
||||||
section,ENV_CERTIFICATE)) == NULL))
|
|
||||||
{
|
{
|
||||||
lookup_fail(section,ENV_CERTIFICATE);
|
if ((certfile == NULL)
|
||||||
goto err;
|
&& ((certfile=NCONF_get_string(conf,
|
||||||
}
|
section,ENV_CERTIFICATE)) == NULL))
|
||||||
x509=load_cert(bio_err, certfile, FORMAT_PEM, NULL, e,
|
{
|
||||||
"CA certificate");
|
lookup_fail(section,ENV_CERTIFICATE);
|
||||||
if (x509 == NULL)
|
goto err;
|
||||||
goto err;
|
}
|
||||||
|
x509=load_cert(bio_err, certfile, FORMAT_PEM, NULL, e,
|
||||||
|
"CA certificate");
|
||||||
|
if (x509 == NULL)
|
||||||
|
goto err;
|
||||||
|
|
||||||
if (!X509_check_private_key(x509,pkey))
|
if (!X509_check_private_key(x509,pkey))
|
||||||
{
|
{
|
||||||
BIO_printf(bio_err,"CA certificate and CA private key do not match\n");
|
BIO_printf(bio_err,"CA certificate and CA private key do not match\n");
|
||||||
goto err;
|
goto err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (!selfsign) x509p = x509;
|
||||||
|
|
||||||
f=NCONF_get_string(conf,BASE_SECTION,ENV_PRESERVE);
|
f=NCONF_get_string(conf,BASE_SECTION,ENV_PRESERVE);
|
||||||
if (f == NULL)
|
if (f == NULL)
|
||||||
@ -1175,10 +1183,10 @@ bad:
|
|||||||
if (infile != NULL)
|
if (infile != NULL)
|
||||||
{
|
{
|
||||||
total++;
|
total++;
|
||||||
j=certify(&x,infile,pkey,x509,dgst,attribs,db,
|
j=certify(&x,infile,pkey,x509p,dgst,attribs,db,
|
||||||
serial,subj,email_dn,startdate,enddate,days,batch,
|
serial,subj,email_dn,startdate,enddate,days,batch,
|
||||||
extensions,conf,verbose, certopt, nameopt,
|
extensions,conf,verbose, certopt, nameopt,
|
||||||
default_op, ext_copy);
|
default_op, ext_copy, selfsign);
|
||||||
if (j < 0) goto err;
|
if (j < 0) goto err;
|
||||||
if (j > 0)
|
if (j > 0)
|
||||||
{
|
{
|
||||||
@ -1195,10 +1203,10 @@ bad:
|
|||||||
for (i=0; i<argc; i++)
|
for (i=0; i<argc; i++)
|
||||||
{
|
{
|
||||||
total++;
|
total++;
|
||||||
j=certify(&x,argv[i],pkey,x509,dgst,attribs,db,
|
j=certify(&x,argv[i],pkey,x509p,dgst,attribs,db,
|
||||||
serial,subj,email_dn,startdate,enddate,days,batch,
|
serial,subj,email_dn,startdate,enddate,days,batch,
|
||||||
extensions,conf,verbose, certopt, nameopt,
|
extensions,conf,verbose, certopt, nameopt,
|
||||||
default_op, ext_copy);
|
default_op, ext_copy, selfsign);
|
||||||
if (j < 0) goto err;
|
if (j < 0) goto err;
|
||||||
if (j > 0)
|
if (j > 0)
|
||||||
{
|
{
|
||||||
@ -1515,7 +1523,7 @@ err:
|
|||||||
BN_free(serial);
|
BN_free(serial);
|
||||||
free_index(db);
|
free_index(db);
|
||||||
EVP_PKEY_free(pkey);
|
EVP_PKEY_free(pkey);
|
||||||
X509_free(x509);
|
if (x509) X509_free(x509);
|
||||||
X509_CRL_free(crl);
|
X509_CRL_free(crl);
|
||||||
NCONF_free(conf);
|
NCONF_free(conf);
|
||||||
OBJ_cleanup();
|
OBJ_cleanup();
|
||||||
@ -1533,7 +1541,7 @@ static int certify(X509 **xret, char *infile, EVP_PKEY *pkey, X509 *x509,
|
|||||||
BIGNUM *serial, char *subj, int email_dn, char *startdate, char *enddate,
|
BIGNUM *serial, char *subj, int email_dn, char *startdate, char *enddate,
|
||||||
long days, int batch, char *ext_sect, CONF *lconf, int verbose,
|
long days, int batch, char *ext_sect, CONF *lconf, int verbose,
|
||||||
unsigned long certopt, unsigned long nameopt, int default_op,
|
unsigned long certopt, unsigned long nameopt, int default_op,
|
||||||
int ext_copy)
|
int ext_copy, int selfsign)
|
||||||
{
|
{
|
||||||
X509_REQ *req=NULL;
|
X509_REQ *req=NULL;
|
||||||
BIO *in=NULL;
|
BIO *in=NULL;
|
||||||
@ -1558,6 +1566,12 @@ static int certify(X509 **xret, char *infile, EVP_PKEY *pkey, X509 *x509,
|
|||||||
|
|
||||||
BIO_printf(bio_err,"Check that the request matches the signature\n");
|
BIO_printf(bio_err,"Check that the request matches the signature\n");
|
||||||
|
|
||||||
|
if (selfsign && !X509_REQ_check_private_key(req,pkey))
|
||||||
|
{
|
||||||
|
BIO_printf(bio_err,"Certificate request and CA private key do not match\n");
|
||||||
|
ok=0;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
if ((pktmp=X509_REQ_get_pubkey(req)) == NULL)
|
if ((pktmp=X509_REQ_get_pubkey(req)) == NULL)
|
||||||
{
|
{
|
||||||
BIO_printf(bio_err,"error unpacking public key\n");
|
BIO_printf(bio_err,"error unpacking public key\n");
|
||||||
@ -1582,7 +1596,7 @@ static int certify(X509 **xret, char *infile, EVP_PKEY *pkey, X509 *x509,
|
|||||||
|
|
||||||
ok=do_body(xret,pkey,x509,dgst,policy,db,serial,subj, email_dn,
|
ok=do_body(xret,pkey,x509,dgst,policy,db,serial,subj, email_dn,
|
||||||
startdate,enddate,days,batch,verbose,req,ext_sect,lconf,
|
startdate,enddate,days,batch,verbose,req,ext_sect,lconf,
|
||||||
certopt, nameopt, default_op, ext_copy);
|
certopt, nameopt, default_op, ext_copy, selfsign);
|
||||||
|
|
||||||
err:
|
err:
|
||||||
if (req != NULL) X509_REQ_free(req);
|
if (req != NULL) X509_REQ_free(req);
|
||||||
@ -1636,7 +1650,7 @@ static int certify_cert(X509 **xret, char *infile, EVP_PKEY *pkey, X509 *x509,
|
|||||||
|
|
||||||
ok=do_body(xret,pkey,x509,dgst,policy,db,serial,subj,email_dn,startdate,enddate,
|
ok=do_body(xret,pkey,x509,dgst,policy,db,serial,subj,email_dn,startdate,enddate,
|
||||||
days,batch,verbose,rreq,ext_sect,lconf, certopt, nameopt, default_op,
|
days,batch,verbose,rreq,ext_sect,lconf, certopt, nameopt, default_op,
|
||||||
ext_copy);
|
ext_copy, 0);
|
||||||
|
|
||||||
err:
|
err:
|
||||||
if (rreq != NULL) X509_REQ_free(rreq);
|
if (rreq != NULL) X509_REQ_free(rreq);
|
||||||
@ -1649,7 +1663,7 @@ static int do_body(X509 **xret, EVP_PKEY *pkey, X509 *x509, const EVP_MD *dgst,
|
|||||||
int email_dn, char *startdate, char *enddate, long days, int batch,
|
int email_dn, char *startdate, char *enddate, long days, int batch,
|
||||||
int verbose, X509_REQ *req, char *ext_sect, CONF *lconf,
|
int verbose, X509_REQ *req, char *ext_sect, CONF *lconf,
|
||||||
unsigned long certopt, unsigned long nameopt, int default_op,
|
unsigned long certopt, unsigned long nameopt, int default_op,
|
||||||
int ext_copy)
|
int ext_copy, int selfsign)
|
||||||
{
|
{
|
||||||
X509_NAME *name=NULL,*CAname=NULL,*subject=NULL, *dn_subject=NULL;
|
X509_NAME *name=NULL,*CAname=NULL,*subject=NULL, *dn_subject=NULL;
|
||||||
ASN1_UTCTIME *tm,*tmptm;
|
ASN1_UTCTIME *tm,*tmptm;
|
||||||
@ -1753,7 +1767,10 @@ static int do_body(X509 **xret, EVP_PKEY *pkey, X509 *x509, const EVP_MD *dgst,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* take a copy of the issuer name before we mess with it. */
|
/* take a copy of the issuer name before we mess with it. */
|
||||||
CAname=X509_NAME_dup(x509->cert_info->subject);
|
if (selfsign)
|
||||||
|
CAname=X509_NAME_dup(name);
|
||||||
|
else
|
||||||
|
CAname=X509_NAME_dup(x509->cert_info->subject);
|
||||||
if (CAname == NULL) goto err;
|
if (CAname == NULL) goto err;
|
||||||
str=str2=NULL;
|
str=str2=NULL;
|
||||||
|
|
||||||
@ -1965,8 +1982,16 @@ again2:
|
|||||||
|
|
||||||
if (BN_to_ASN1_INTEGER(serial,ci->serialNumber) == NULL)
|
if (BN_to_ASN1_INTEGER(serial,ci->serialNumber) == NULL)
|
||||||
goto err;
|
goto err;
|
||||||
if (!X509_set_issuer_name(ret,X509_get_subject_name(x509)))
|
if (selfsign)
|
||||||
goto err;
|
{
|
||||||
|
if (!X509_set_issuer_name(ret,subject))
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!X509_set_issuer_name(ret,X509_get_subject_name(x509)))
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
if (strcmp(startdate,"today") == 0)
|
if (strcmp(startdate,"today") == 0)
|
||||||
X509_gmtime_adj(X509_get_notBefore(ret),0);
|
X509_gmtime_adj(X509_get_notBefore(ret),0);
|
||||||
@ -2001,7 +2026,10 @@ again2:
|
|||||||
ci->extensions = NULL;
|
ci->extensions = NULL;
|
||||||
|
|
||||||
/* Initialize the context structure */
|
/* Initialize the context structure */
|
||||||
X509V3_set_ctx(&ctx, x509, ret, req, NULL, 0);
|
if (selfsign)
|
||||||
|
X509V3_set_ctx(&ctx, ret, ret, req, NULL, 0);
|
||||||
|
else
|
||||||
|
X509V3_set_ctx(&ctx, x509, ret, req, NULL, 0);
|
||||||
|
|
||||||
if (extconf)
|
if (extconf)
|
||||||
{
|
{
|
||||||
@ -2344,7 +2372,7 @@ static int certify_spkac(X509 **xret, char *infile, EVP_PKEY *pkey, X509 *x509,
|
|||||||
EVP_PKEY_free(pktmp);
|
EVP_PKEY_free(pktmp);
|
||||||
ok=do_body(xret,pkey,x509,dgst,policy,db,serial,subj,email_dn,startdate,enddate,
|
ok=do_body(xret,pkey,x509,dgst,policy,db,serial,subj,email_dn,startdate,enddate,
|
||||||
days,1,verbose,req,ext_sect,lconf, certopt, nameopt, default_op,
|
days,1,verbose,req,ext_sect,lconf, certopt, nameopt, default_op,
|
||||||
ext_copy);
|
ext_copy, 0);
|
||||||
err:
|
err:
|
||||||
if (req != NULL) X509_REQ_free(req);
|
if (req != NULL) X509_REQ_free(req);
|
||||||
if (parms != NULL) CONF_free(parms);
|
if (parms != NULL) CONF_free(parms);
|
||||||
|
@ -66,14 +66,13 @@ Section 5 will tell you more on how to handle the certificate you
|
|||||||
received.
|
received.
|
||||||
|
|
||||||
|
|
||||||
4. Creating a self-signed certificate
|
4. Creating a self-signed test certificate
|
||||||
|
|
||||||
If you don't want to deal with another certificate authority, or just
|
If you don't want to deal with another certificate authority, or just
|
||||||
want to create a test certificate for yourself, or are setting up a
|
want to create a test certificate for yourself. This is similar to
|
||||||
certificate authority of your own, you may want to make the requested
|
creating a certificate request, but creates a certificate instead of
|
||||||
certificate a self-signed one. This is similar to creating a
|
a certificate request. This is NOT the recommended way to create a
|
||||||
certificate request, but creates a certificate instead of a
|
CA certificate, see ca.txt.
|
||||||
certificate request (1095 is 3 years):
|
|
||||||
|
|
||||||
openssl req -new -x509 -key privkey.pem -out cacert.pem -days 1095
|
openssl req -new -x509 -key privkey.pem -out cacert.pem -days 1095
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user