Commit 8b78c993 authored by Francis Dupont's avatar Francis Dupont

explicit engine rt20230a

parent b56f3f5c
2703. [func] Introduce an OpenSSL "engine" argument with -E
for all binaries which can take benefit of
crypto hardware. [RT #20230]
2702. [func] Update PKCS#11 tools (bin/pkcs11) [RT #20225 & all]
2701. [doc] Correction to ARM: hmac-md5 is no longer the only
......
......@@ -13,7 +13,7 @@
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
# $Id: Makefile.in,v 1.40 2009/09/01 18:40:25 jinmei Exp $
# $Id: Makefile.in,v 1.41 2009/10/05 17:30:49 fdupont Exp $
srcdir = @srcdir@
VPATH = @srcdir@
......@@ -25,7 +25,7 @@ top_srcdir = @top_srcdir@
CINCLUDES = ${DNS_INCLUDES} ${ISC_INCLUDES}
CDEFINES = -DBIND9 -DVERSION=\"${VERSION}\"
CDEFINES = -DBIND9 -DVERSION=\"${VERSION}\" @USE_PKCS11@
CWARNINGS =
DNSLIBS = ../../lib/dns/libdns.@A@ @DNS_CRYPTO_LIBS@
......
......@@ -14,7 +14,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: dnssec-dsfromkey.c,v 1.14 2009/09/29 15:06:06 fdupont Exp $ */
/* $Id: dnssec-dsfromkey.c,v 1.15 2009/10/05 17:30:49 fdupont Exp $ */
/*! \file */
......@@ -440,7 +440,8 @@ main(int argc, char **argv) {
result = dst_lib_init(mctx, ectx,
ISC_ENTROPY_BLOCKING | ISC_ENTROPY_GOODONLY);
if (result != ISC_R_SUCCESS)
fatal("could not initialize dst");
fatal("could not initialize dst: %s",
isc_result_totext(result));
isc_entropy_stopcallbacksources(ectx);
setup_logging(verbose, mctx, &log);
......
......@@ -14,7 +14,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: dnssec-keyfromlabel.c,v 1.17 2009/10/03 18:03:53 each Exp $ */
/* $Id: dnssec-keyfromlabel.c,v 1.18 2009/10/05 17:30:49 fdupont Exp $ */
/*! \file */
......@@ -63,20 +63,28 @@ usage(void) {
fprintf(stderr, "Required options:\n");
fprintf(stderr, " -a algorithm: %s\n", algs);
fprintf(stderr, " -l label: label of the key pair\n");
#ifdef USE_PKCS11
fprintf(stderr, " (for instance \"pkcs11:foo\"\n");
#else
fprintf(stderr, " -E enginename\n");
#endif
fprintf(stderr, " name: owner of the key\n");
fprintf(stderr, "Other options:\n");
fprintf(stderr, " -c <class> (default: IN)\n");
fprintf(stderr, " -c class (default: IN)\n");
#ifdef USE_PKCS11
fprintf(stderr, " -E enginename (default: pkcs11)\n");
#endif
fprintf(stderr, " -f keyflag: KSK | REVOKE\n");
fprintf(stderr, " -K directory: directory in which to place "
"key files\n");
fprintf(stderr, " -k : generate a TYPE=KEY key\n");
fprintf(stderr, " -n nametype: ZONE | HOST | ENTITY | USER | OTHER\n");
fprintf(stderr, " (DNSKEY generation defaults to ZONE\n");
fprintf(stderr, " -p <protocol>: default: 3 [dnssec]\n");
fprintf(stderr, " -t <type>: "
fprintf(stderr, " -p protocol: default: 3 [dnssec]\n");
fprintf(stderr, " -t type: "
"AUTHCONF | NOAUTHCONF | NOAUTH | NOCONF "
"(default: AUTHCONF)\n");
fprintf(stderr, " -v <verbose level>\n");
fprintf(stderr, " -v verbose level\n");
fprintf(stderr, "Date options:\n");
fprintf(stderr, " -P date/[+-]offset: set key publication date\n");
fprintf(stderr, " -A date/[+-]offset: set key activation date\n");
......@@ -97,6 +105,11 @@ int
main(int argc, char **argv) {
char *algname = NULL, *nametype = NULL, *type = NULL;
const char *directory = NULL;
#ifdef USE_PKCS11
const char *engine = "pkcs11";
#else
const char *engine = NULL;
#endif
char *classname = NULL;
char *endp;
dst_key_t *key = NULL, *oldkey = NULL;
......@@ -116,7 +129,7 @@ main(int argc, char **argv) {
isc_entropy_t *ectx = NULL;
dns_rdataclass_t rdclass;
int options = DST_TYPE_PRIVATE | DST_TYPE_PUBLIC;
char *label = NULL, *engine = NULL;
char *label = NULL;
isc_stdtime_t publish = 0, activate = 0, revoke = 0;
isc_stdtime_t inactive = 0, delete = 0;
isc_stdtime_t now;
......@@ -140,7 +153,7 @@ main(int argc, char **argv) {
isc_stdtime_get(&now);
while ((ch = isc_commandline_parse(argc, argv,
"a:Cc:f:K:kl:n:p:t:v:FhGP:A:R:I:D:")) != -1)
"a:Cc:E:f:K:kl:n:p:t:v:FhGP:A:R:I:D:")) != -1)
{
switch (ch) {
case 'a':
......@@ -152,6 +165,9 @@ main(int argc, char **argv) {
case 'c':
classname = isc_commandline_argument;
break;
case 'E':
engine = isc_commandline_argument;
break;
case 'f':
if (toupper(isc_commandline_argument[0]) == 'K')
kskflag = DNS_KEYFLAG_KSK;
......@@ -270,10 +286,11 @@ main(int argc, char **argv) {
if (ectx == NULL)
setup_entropy(mctx, NULL, &ectx);
ret = dst_lib_init(mctx, ectx,
ISC_ENTROPY_BLOCKING | ISC_ENTROPY_GOODONLY);
ret = dst_lib_init2(mctx, ectx, engine,
ISC_ENTROPY_BLOCKING | ISC_ENTROPY_GOODONLY);
if (ret != ISC_R_SUCCESS)
fatal("could not initialize dst");
fatal("could not initialize dst: %s",
isc_result_totext(ret));
setup_logging(verbose, mctx, &log);
......
......@@ -17,7 +17,7 @@
- PERFORMANCE OF THIS SOFTWARE.
-->
<!-- $Id: dnssec-keyfromlabel.docbook,v 1.10 2009/09/14 18:45:45 each Exp $ -->
<!-- $Id: dnssec-keyfromlabel.docbook,v 1.11 2009/10/05 17:30:49 fdupont Exp $ -->
<refentry id="man.dnssec-keyfromlabel">
<refentryinfo>
<date>February 8, 2008</date>
......@@ -50,6 +50,7 @@
<arg><option>-A <replaceable class="parameter">date/offset</replaceable></option></arg>
<arg><option>-c <replaceable class="parameter">class</replaceable></option></arg>
<arg><option>-D <replaceable class="parameter">date/offset</replaceable></option></arg>
<arg><option>-E <replaceable class="parameter">engine</replaceable></option></arg>
<arg><option>-f <replaceable class="parameter">flag</replaceable></option></arg>
<arg><option>-G</option></arg>
<arg><option>-I <replaceable class="parameter">date/offset</replaceable></option></arg>
......@@ -102,12 +103,23 @@
</listitem>
</varlistentry>
<varlistentry>
<term>-E <replaceable class="parameter">engine</replaceable></term>
<listitem>
<para>
Specifies the name of the crypto hardware (OpenSSL engine).
When compiled with PKCS#11 support it defaults to pcks11.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-l <replaceable class="parameter">label</replaceable></term>
<listitem>
<para>
Specifies the label of keys in the crypto hardware
(PKCS#11 device).
Specifies the label of keys in the crypto hardware (OpenSSL
engine). An example for the pkcs11 engine is pkcs11:foo
(note the string pkcs11 is in both E and l options.)
</para>
</listitem>
</varlistentry>
......
......@@ -29,7 +29,7 @@
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: dnssec-keygen.c,v 1.98 2009/10/03 18:03:53 each Exp $ */
/* $Id: dnssec-keygen.c,v 1.99 2009/10/05 17:30:49 fdupont Exp $ */
/*! \file */
......@@ -115,6 +115,11 @@ usage(void) {
fprintf(stderr, " (DNSKEY generation defaults to ZONE)\n");
fprintf(stderr, " -c <class>: (default: IN)\n");
fprintf(stderr, " -d <digest bits> (0 => max, default)\n");
#ifdef USE_PKCS11
fprintf(stderr, " -E <engine name> (default \"pkcs11\")\n");
#else
fprintf(stderr, " -E <engine name>\n");
#endif
fprintf(stderr, " -e: use large exponent (RSAMD5/RSASHA1 only)\n");
fprintf(stderr, " -f <keyflag>: KSK | REVOKE\n");
fprintf(stderr, " -g <generator>: use specified generator "
......@@ -173,6 +178,11 @@ main(int argc, char **argv) {
isc_buffer_t buf;
isc_log_t *log = NULL;
isc_entropy_t *ectx = NULL;
#ifdef USE_PKCS11
const char *engine = "pkcs11";
#else
const char *engine = NULL;
#endif
dns_rdataclass_t rdclass;
int options = DST_TYPE_PRIVATE | DST_TYPE_PUBLIC;
int dbits = 0;
......@@ -198,7 +208,7 @@ main(int argc, char **argv) {
/*
* Process memory debugging argument first.
*/
#define CMDLINE_FLAGS "3a:b:Cc:d:eFf:g:K:km:n:p:r:s:T:t:v:hGP:A:R:I:D:"
#define CMDLINE_FLAGS "3a:b:Cc:d:E:eFf:g:K:km:n:p:r:s:T:t:v:hGP:A:R:I:D:"
while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
switch (ch) {
case 'm':
......@@ -247,6 +257,9 @@ main(int argc, char **argv) {
if (*endp != '\0' || dbits < 0)
fatal("-d requires a non-negative number");
break;
case 'E':
engine = isc_commandline_argument;
break;
case 'e':
rsa_exp = 1;
break;
......@@ -400,10 +413,11 @@ main(int argc, char **argv) {
if (ectx == NULL)
setup_entropy(mctx, NULL, &ectx);
ret = dst_lib_init(mctx, ectx,
ISC_ENTROPY_BLOCKING | ISC_ENTROPY_GOODONLY);
ret = dst_lib_init2(mctx, ectx, engine,
ISC_ENTROPY_BLOCKING | ISC_ENTROPY_GOODONLY);
if (ret != ISC_R_SUCCESS)
fatal("could not initialize dst");
fatal("could not initialize dst: %s",
isc_result_totext(ret));
setup_logging(verbose, mctx, &log);
......
......@@ -18,7 +18,7 @@
- PERFORMANCE OF THIS SOFTWARE.
-->
<!-- $Id: dnssec-keygen.docbook,v 1.28 2009/09/14 18:45:45 each Exp $ -->
<!-- $Id: dnssec-keygen.docbook,v 1.29 2009/10/05 17:30:49 fdupont Exp $ -->
<refentry id="man.dnssec-keygen">
<refentryinfo>
<date>June 30, 2000</date>
......@@ -64,6 +64,7 @@
<arg><option>-C</option></arg>
<arg><option>-c <replaceable class="parameter">class</replaceable></option></arg>
<arg><option>-D <replaceable class="parameter">date/offset</replaceable></option></arg>
<arg><option>-E <replaceable class="parameter">engine</replaceable></option></arg>
<arg><option>-e</option></arg>
<arg><option>-f <replaceable class="parameter">flag</replaceable></option></arg>
<arg><option>-G</option></arg>
......@@ -206,6 +207,18 @@
</listitem>
</varlistentry>
<varlistentry>
<term>-E <replaceable class="parameter">engine</replaceable></term>
<listitem>
<para>
Uses a crypto hardware (OpenSSL engine) for random number
and, when supported, key generation. When compiled with PKCS#11
support it defaults to pcks11, the empty name resets it to
no engine.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-e</term>
<listitem>
......
......@@ -14,7 +14,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: dnssec-revoke.c,v 1.13 2009/09/29 15:06:06 fdupont Exp $ */
/* $Id: dnssec-revoke.c,v 1.14 2009/10/05 17:30:49 fdupont Exp $ */
/*! \file */
......@@ -54,6 +54,13 @@ usage(void) {
fprintf(stderr, "Usage:\n");
fprintf(stderr, " %s [options] keyfile\n\n", program);
fprintf(stderr, "Version: %s\n", VERSION);
fprintf(stderr, "\t-E engine:\n");
#ifdef USE_PKCS11
fprintf(stderr, "\t\tname of an OpenSSL engine to use "
"(default is \"pkcs11\")\n");
#else
fprintf(stderr, "\t\tname of an OpenSSL engine to use\n");
#endif
fprintf(stderr, " -f: force overwrite\n");
fprintf(stderr, " -K directory: use directory for key files\n");
fprintf(stderr, " -h: help\n");
......@@ -70,6 +77,11 @@ usage(void) {
int
main(int argc, char **argv) {
isc_result_t result;
#ifdef USE_PKCS11
const char *engine = "pkcs11";
#else
const char *engine = NULL;
#endif
char *filename = NULL, *dir = NULL;
char newname[1024], oldname[1024];
char keystr[KEY_FORMATSIZE];
......@@ -93,8 +105,11 @@ main(int argc, char **argv) {
isc_commandline_errprint = ISC_FALSE;
while ((ch = isc_commandline_parse(argc, argv, "fK:rhv:")) != -1) {
while ((ch = isc_commandline_parse(argc, argv, "EfK:rhv:")) != -1) {
switch (ch) {
case 'E':
engine = isc_commandline_argument;
break;
case 'f':
force = ISC_TRUE;
break;
......@@ -150,10 +165,11 @@ main(int argc, char **argv) {
result = isc_hash_create(mctx, ectx, DNS_NAME_MAXWIRE);
if (result != ISC_R_SUCCESS)
fatal("Could not initialize hash");
result = dst_lib_init(mctx, ectx,
ISC_ENTROPY_BLOCKING | ISC_ENTROPY_GOODONLY);
result = dst_lib_init2(mctx, ectx, engine,
ISC_ENTROPY_BLOCKING | ISC_ENTROPY_GOODONLY);
if (result != ISC_R_SUCCESS)
fatal("Could not initialize dst");
fatal("Could not initialize dst: %s",
isc_result_totext(result));
isc_entropy_stopcallbacksources(ectx);
result = dst_key_fromnamedfile(filename, dir,
......
......@@ -17,7 +17,7 @@
- PERFORMANCE OF THIS SOFTWARE.
-->
<!-- $Id: dnssec-revoke.docbook,v 1.5 2009/07/20 11:56:35 fdupont Exp $ -->
<!-- $Id: dnssec-revoke.docbook,v 1.6 2009/10/05 17:30:49 fdupont Exp $ -->
<refentry id="man.dnssec-revoke">
<refentryinfo>
<date>June 1, 2009</date>
......@@ -47,6 +47,7 @@
<arg><option>-hr</option></arg>
<arg><option>-v <replaceable class="parameter">level</replaceable></option></arg>
<arg><option>-K <replaceable class="parameter">directory</replaceable></option></arg>
<arg><option>-E <replaceable class="parameter">engine</replaceable></option></arg>
<arg><option>-f</option></arg>
<arg choice="req">keyfile</arg>
</cmdsynopsis>
......@@ -102,6 +103,16 @@
</listitem>
</varlistentry>
<varlistentry>
<term>-E <replaceable class="parameter">engine</replaceable></term>
<listitem>
<para>
Use the given OpenSSL engine. When compiled with PKCS#11 support
it defaults to pcks11, the empty name resets it to no engine.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-f</term>
<listitem>
......
......@@ -14,7 +14,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: dnssec-settime.c,v 1.14 2009/09/29 15:06:06 fdupont Exp $ */
/* $Id: dnssec-settime.c,v 1.15 2009/10/05 17:30:49 fdupont Exp $ */
/*! \file */
......@@ -57,6 +57,12 @@ usage(void) {
fprintf(stderr, " %s [options] keyfile\n\n", program);
fprintf(stderr, "Version: %s\n", VERSION);
fprintf(stderr, "General options:\n");
#ifdef USE_PKCS11
fprintf(stderr, "\t\tname of an OpenSSL engine to use "
"(default is \"pkcs11\")\n");
#else
fprintf(stderr, "\t\tname of an OpenSSL engine to use\n");
#endif
fprintf(stderr, " -f: force update of old-style "
"keys\n");
fprintf(stderr, " -K directory: set key file location\n");
......@@ -112,6 +118,11 @@ printtime(dst_key_t *key, int type, const char *tag, isc_boolean_t epoch,
int
main(int argc, char **argv) {
isc_result_t result;
#ifdef USE_PKCS11
const char *engine = "pkcs11";
#else
const char *engine = NULL;
#endif
char *filename = NULL, *directory = NULL;
char newname[1024];
char keystr[KEY_FORMATSIZE];
......@@ -150,8 +161,11 @@ main(int argc, char **argv) {
isc_stdtime_get(&now);
while ((ch = isc_commandline_parse(argc, argv,
"fK:uhp:v:P:A:R:I:D:")) != -1) {
"EfK:uhp:v:P:A:R:I:D:")) != -1) {
switch (ch) {
case 'E':
engine = isc_commandline_argument;
break;
case 'f':
forceupdate = ISC_TRUE;
break;
......@@ -313,10 +327,11 @@ main(int argc, char **argv) {
result = isc_hash_create(mctx, ectx, DNS_NAME_MAXWIRE);
if (result != ISC_R_SUCCESS)
fatal("Could not initialize hash");
result = dst_lib_init(mctx, ectx,
ISC_ENTROPY_BLOCKING | ISC_ENTROPY_GOODONLY);
result = dst_lib_init2(mctx, ectx, engine,
ISC_ENTROPY_BLOCKING | ISC_ENTROPY_GOODONLY);
if (result != ISC_R_SUCCESS)
fatal("Could not initialize dst");
fatal("Could not initialize dst: %s",
isc_result_totext(result));
isc_entropy_stopcallbacksources(ectx);
result = dst_key_fromnamedfile(filename, directory,
......
......@@ -17,7 +17,7 @@
- PERFORMANCE OF THIS SOFTWARE.
-->
<!-- $Id: dnssec-settime.docbook,v 1.4 2009/09/14 18:45:45 each Exp $ -->
<!-- $Id: dnssec-settime.docbook,v 1.5 2009/10/05 17:30:49 fdupont Exp $ -->
<refentry id="man.dnssec-settime">
<refentryinfo>
<date>July 15, 2009</date>
......@@ -53,6 +53,7 @@
<arg><option>-D <replaceable class="parameter">date/offset</replaceable></option></arg>
<arg><option>-h</option></arg>
<arg><option>-v <replaceable class="parameter">level</replaceable></option></arg>
<arg><option>-E <replaceable class="parameter">engine</replaceable></option></arg>
<arg choice="req">keyfile</arg>
</cmdsynopsis>
</refsynopsisdiv>
......@@ -127,6 +128,16 @@
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-E <replaceable class="parameter">engine</replaceable></term>
<listitem>
<para>
Use the given OpenSSL engine. When compiled with PKCS#11 support
it defaults to pcks11, the empty name resets it to no engine.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
......
......@@ -29,7 +29,7 @@
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: dnssec-signzone.c,v 1.240 2009/10/03 18:03:54 each Exp $ */
/* $Id: dnssec-signzone.c,v 1.241 2009/10/05 17:30:49 fdupont Exp $ */
/*! \file */
......@@ -3324,6 +3324,13 @@ usage(void) {
fprintf(stderr, "\t-a:\t");
fprintf(stderr, "verify generated signatures\n");
fprintf(stderr, "\t-c class (IN)\n");
fprintf(stderr, "\t-E engine:\n");
#ifdef USE_PKCS11
fprintf(stderr, "\t\tname of an OpenSSL engine to use "
"(default is \"pkcs11\")\n");
#else
fprintf(stderr, "\t\tname of an OpenSSL engine to use\n");
#endif
fprintf(stderr, "\t-p:\t");
fprintf(stderr, "use pseudorandom data (faster but less secure)\n");
fprintf(stderr, "\t-P:\t");
......@@ -3398,6 +3405,11 @@ main(int argc, char *argv[]) {
isc_result_t result;
isc_log_t *log = NULL;
isc_boolean_t pseudorandom = ISC_FALSE;
#ifdef USE_PKCS11
const char *engine = "pkcs11";
#else
const char *engine = NULL;
#endif
unsigned int eflags;
isc_boolean_t free_output = ISC_FALSE;
int tempfilelen;
......@@ -3412,7 +3424,7 @@ main(int argc, char *argv[]) {
isc_boolean_t set_iter = ISC_FALSE;
#define CMDLINE_FLAGS \
"3:AaCc:Dd:e:f:FghH:i:I:j:K:k:l:m:n:N:o:O:pPr:s:ST:tuUv:z"
"3:AaCc:Dd:Ee:f:FghH:i:I:j:K:k:l:m:n:N:o:O:pPr:s:ST:tuUv:z"
/*
* Process memory debugging argument first.
......@@ -3494,8 +3506,8 @@ main(int argc, char *argv[]) {
fatal("DS directory must be non-empty string");
break;
case 'K':
directory = isc_commandline_argument;
case 'E':
engine = isc_commandline_argument;
break;
case 'e':
......@@ -3523,6 +3535,10 @@ main(int argc, char *argv[]) {
usage();
break;
case 'I':
inputformatstr = isc_commandline_argument;
break;
case 'i':
endp = NULL;
cycle = strtol(isc_commandline_argument, &endp, 0);
......@@ -3531,10 +3547,6 @@ main(int argc, char *argv[]) {
"positive");
break;
case 'I':
inputformatstr = isc_commandline_argument;
break;
case 'j':
endp = NULL;
jitter = strtol(isc_commandline_argument, &endp, 0);
......@@ -3542,6 +3554,10 @@ main(int argc, char *argv[]) {
fatal("jitter must be numeric and positive");
break;
case 'K':
directory = isc_commandline_argument;
break;
case 'k':
if (ndskeys == MAXDSKEYS)
fatal("too many key-signing keys specified");
......@@ -3563,6 +3579,10 @@ main(int argc, char *argv[]) {
case 'm':
break;
case 'N':
serialformatstr = isc_commandline_argument;
break;
case 'n':
endp = NULL;
ntasks = strtol(isc_commandline_argument, &endp, 0);
......@@ -3570,39 +3590,35 @@ main(int argc, char *argv[]) {
fatal("number of cpus must be numeric");
break;
case 'N':
serialformatstr = isc_commandline_argument;
case 'O':
outputformatstr = isc_commandline_argument;
break;
case 'o':
origin = isc_commandline_argument;
break;
case 'O':
outputformatstr = isc_commandline_argument;
case 'P':
disable_zone_check = ISC_TRUE;
break;
case 'p':
pseudorandom = ISC_TRUE;
break;
case 'P':
disable_zone_check = ISC_TRUE;
break;
case 'r':
setup_entropy(mctx, isc_commandline_argument, &ectx);
break;
case 's':
startstr = isc_commandline_argument;
break;
case 'S':
smartsign = ISC_TRUE;
generateds = ISC_TRUE;
break;
case 's':
startstr = isc_commandline_argument;
break;
case 'T':
endp = NULL;
set_keyttl = ISC_TRUE;
......@@ -3659,9 +3675,10 @@ main(int argc, char *argv[]) {
if (result != ISC_R_SUCCESS)
fatal("could not create hash context");
result = dst_lib_init(mctx, ectx, eflags);
result = dst_lib_init2(mctx, ectx, engine, eflags);
if (result != ISC_R_SUCCESS)