--- common/slp_auth.c +++ common/slp_auth.c 2003/12/15 17:15:46 @@ -0,0 +1,928 @@ +/***************************************************************************/ +/* */ +/* Project: OpenSLP - OpenSource implementation of Service Location */ +/* Protocol */ +/* */ +/* File: slp_auth.c */ +/* */ +/* Abstract: Common for OpenSLP's SLPv2 authentication implementation */ +/* Currently only bsd 0x0002 (DSA-SHA1) is supported */ +/* */ +/*-------------------------------------------------------------------------*/ +/* */ +/* Please submit patches to http://www.openslp.org */ +/* */ +/*-------------------------------------------------------------------------*/ +/* */ +/* Copyright (C) 2000 Caldera Systems, Inc */ +/* All rights reserved. */ +/* */ +/* Redistribution and use in source and binary forms, with or without */ +/* modification, are permitted provided that the following conditions are */ +/* met: */ +/* */ +/* Redistributions of source code must retain the above copyright */ +/* notice, this list of conditions and the following disclaimer. */ +/* */ +/* Redistributions in binary form must reproduce the above copyright */ +/* notice, this list of conditions and the following disclaimer in */ +/* the documentation and/or other materials provided with the */ +/* distribution. */ +/* */ +/* Neither the name of Caldera Systems nor the names of its */ +/* contributors may be used to endorse or promote products derived */ +/* from this software without specific prior written permission. */ +/* */ +/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */ +/* `AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */ +/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR */ +/* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE CALDERA */ +/* SYSTEMS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ +/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ +/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, */ +/* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON */ +/* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */ +/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE */ +/* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* */ +/***************************************************************************/ + +#include + +#include "slp_xmalloc.h" +#include "slp_auth.h" +#include "slp_crypto.h" + +/*-------------------------------------------------------------------------*/ +int SLPAuthDigestString(int spistrlen, + const char* spistr, + int stringlen, + const char* string, + unsigned long timestamp, + unsigned char* digest) +/*-------------------------------------------------------------------------*/ +{ + int result; + int tmpbufsize; + unsigned char* tmpbuf; + unsigned char* curpos; + + /* assume success */ + result = 0; + + /*-------------------------------------------------------*/ + /* Allocate temporary buffer for contiguous data */ + /*-------------------------------------------------------*/ + /* +8 makes room for: stringlen (2 bytes) */ + /* autharray[i].spistrlen (2 bytes) */ + /* timestamp 4 bytes */ + tmpbufsize = stringlen + spistrlen + 8; + tmpbuf = xmalloc(tmpbufsize); + if(tmpbuf == 0) + { + return SLP_ERROR_INTERNAL_ERROR; + } + + /*-----------------------------------*/ + /* Copy data into continguous buffer */ + /*-----------------------------------*/ + curpos = tmpbuf; + + ToUINT16(curpos,spistrlen); + curpos = curpos + 2; + memcpy(curpos, spistr, spistrlen); + curpos = curpos + spistrlen; + + ToUINT16(curpos,stringlen); + curpos = curpos + 2; + memcpy(curpos, string, stringlen); + curpos = curpos + stringlen; + + ToUINT32(curpos, timestamp); + + /*---------------------*/ + /* Generate the digest */ + /*---------------------*/ + if(SLPCryptoSHA1Digest(tmpbuf, + tmpbufsize, + digest)) + { + result = SLP_ERROR_INTERNAL_ERROR; + } + + /*------------------------------*/ + /* Cleanup the temporary buffer */ + /*------------------------------*/ + xfree(tmpbuf); + + return result; +} + +/*-------------------------------------------------------------------------*/ +int SLPAuthDigestDAAdvert(unsigned short spistrlen, + const char* spistr, + unsigned long timestamp, + unsigned long bootstamp, + unsigned short urllen, + const char* url, + unsigned short attrlistlen, + const char* attrlist, + unsigned short scopelistlen, + const char* scopelist, + unsigned short daspistrlen, + const char* daspistr, + unsigned char* digest) +/*-------------------------------------------------------------------------*/ +{ + int result; + int tmpbufsize; + unsigned char* tmpbuf; + unsigned char* curpos; + + /* assume success */ + result = 0; + + /*-------------------------------------------------------*/ + /* Allocate temporary buffer for contiguous data */ + /*-------------------------------------------------------*/ + /* +18 makes room for: spistrlen (2 bytes) */ + /* bootstamp (4 bytes) */ + /* urllen (2 bytes) */ + /* scopelistlen(2 bytes) */ + /* attrlistlen (2 bytes) */ + /* daspistrlen (2 bytes) */ + /* timestamp (4 bytes) */ + tmpbufsize = spistrlen + urllen + scopelistlen + attrlistlen + daspistrlen + 18; + tmpbuf = xmalloc(tmpbufsize); + if(tmpbuf == 0) + { + return SLP_ERROR_INTERNAL_ERROR; + } + + /*-----------------------------------*/ + /* Copy data into continguous buffer */ + /*-----------------------------------*/ + curpos = tmpbuf; + + ToUINT16(curpos,spistrlen); + curpos += 2; + memcpy(curpos, spistr, spistrlen); + curpos += spistrlen; + + ToUINT32(curpos,bootstamp); + curpos += 4; + + ToUINT16(curpos,urllen); + curpos += 2; + memcpy(curpos, url, urllen); + curpos += urllen; + + ToUINT16(curpos,scopelistlen); + curpos += 2; + memcpy(curpos, scopelist, scopelistlen); + curpos += scopelistlen; + + ToUINT16(curpos,attrlistlen); + curpos += 2; + memcpy(curpos, attrlist, attrlistlen); + curpos += attrlistlen; + + ToUINT16(curpos,daspistrlen); + curpos += 2; + memcpy(curpos, daspistr, daspistrlen); + curpos += daspistrlen; + + ToUINT32(curpos,timestamp); + + /*---------------------*/ + /* Generate the digest */ + /*---------------------*/ + if(SLPCryptoSHA1Digest(tmpbuf, + tmpbufsize, + digest)) + { + result = SLP_ERROR_INTERNAL_ERROR; + } + + /*------------------------------*/ + /* Cleanup the temporary buffer */ + /*------------------------------*/ + xfree(tmpbuf); + + return result; +} + + +/*-------------------------------------------------------------------------*/ +int SLPAuthSignDigest(int spistrlen, + const char* spistr, + SLPCryptoDSAKey* key, + unsigned char* digest, + int* authblocklen, + unsigned char** authblock) +/*-------------------------------------------------------------------------*/ +{ + int signaturelen; + int result; + unsigned char* curpos; + + /*----------------------------------------------*/ + /* Allocate memory for the authentication block */ + /*----------------------------------------------*/ + /* +10 makes room for: */ + /* - the bsd (2 bytes) */ + /* - the the authblock length (2 bytes) */ + /* - the spi string length (2 bytes) */ + /* - the timestamp (4 bytes) */ + signaturelen = SLPCryptoDSASignLen(key); + *authblocklen = spistrlen + signaturelen + 10; + *authblock = (unsigned char*)xmalloc(*authblocklen); + if(*authblock == 0) + { + result = SLP_ERROR_INTERNAL_ERROR; + goto ERROR; + } + + /*---------------------------------------------------------*/ + /* Fill in the Authblock with everything but the signature */ + /*---------------------------------------------------------*/ + curpos = *authblock; + ToUINT16(curpos,0x0002); /* the BSD for DSA-SHA1 */ + curpos += 2; + ToUINT16(curpos,*authblocklen); + curpos += 2; + ToUINT32(curpos,0xffffffff); /* very long expiration (for now) */ + curpos += 4; + ToUINT16(curpos, spistrlen); + curpos += 2; + memcpy(curpos, spistr, spistrlen); + curpos += spistrlen; + + /*---------------------------------------------*/ + /* Sign the digest and put it in the authblock */ + /*---------------------------------------------*/ + if( SLPCryptoDSASign(key, + digest, + SLPAUTH_SHA1_DIGEST_SIZE, + curpos, + &signaturelen)) + { + result = SLP_ERROR_INTERNAL_ERROR; + goto ERROR; + } + + /*---------*/ + /* Success */ + /*---------*/ + + return 0; + + +ERROR: + + /*-------------------------------*/ + /* Clean up and return errorcode */ + /*-------------------------------*/ + if(authblock) xfree(*authblock); + *authblock = 0; + *authblocklen = 0; + + return result; +} + +/*-------------------------------------------------------------------------*/ +int SLPVerifyDigest(SLPSpiHandle hspi, + int emptyisfail, + SLPCryptoDSAKey* key, + unsigned char* digest, + int authcount, + const SLPAuthBlock* autharray) +/*-------------------------------------------------------------------------*/ +{ + int i; + int signaturelen; + int result; + unsigned long timestamp; + + /*-----------------------------------*/ + /* Should we fail on emtpy authblock */ + /*-----------------------------------*/ + if(emptyisfail) + { + result = SLP_ERROR_AUTHENTICATION_FAILED; + } + else + { + result = SLP_ERROR_OK; + } + + /*-----------------*/ + /* Get a timestamp */ + /*-----------------*/ + timestamp = time(NULL); + + /*------------------------------------------------------*/ + /* Iterate and check all authentication blocks */ + /*------------------------------------------------------*/ + /* If any one of the authblocks can be verified then we */ + /* accept it */ + for(i=0;iurllen, + urlentry->url, + urlentry->authcount, + urlentry->autharray); +} + + +/*=========================================================================*/ +int SLPAuthVerifyDAAdvert(SLPSpiHandle hspi, + int emptyisfail, + const SLPDAAdvert* daadvert) +/* Verify authenticity of the specified DAAdvert */ +/* */ +/* Parameters: hspi (IN) open SPI handle */ +/* (IN) if non-zero, messages without authblocks */ +/* will fail */ +/* spistrlen (IN) length of the spi string */ +/* sprstr (IN) the spi string */ +/* daadvert (IN) the DAAdvert to verify */ +/* */ +/* Returns: 0 on success or SLP_ERROR_xxx code on failure */ +/*=========================================================================*/ +{ + int i; + int signaturelen; + int result; + unsigned long timestamp; + const SLPAuthBlock* autharray; + int authcount; + SLPCryptoDSAKey* key = 0; + unsigned char digest[SLPAUTH_SHA1_DIGEST_SIZE]; + + /*-----------------------------------*/ + /* Should we fail on emtpy authblock */ + /*-----------------------------------*/ + if(emptyisfail) + { + result = SLP_ERROR_AUTHENTICATION_FAILED; + } + else + { + result = SLP_ERROR_OK; + } + + /*-----------------*/ + /* Get a timestamp */ + /*-----------------*/ + timestamp = time(NULL); + + /*------------------------------------------------------*/ + /* Iterate and check all authentication blocks */ + /*------------------------------------------------------*/ + /* If any one of the authblocks can be verified then we */ + /* accept it */ + authcount = daadvert->authcount; + autharray = daadvert->autharray; + for(i=0;ibootstamp, + daadvert->urllen, + daadvert->url, + daadvert->attrlistlen, + daadvert->attrlist, + daadvert->scopelistlen, + daadvert->scopelist, + daadvert->spilistlen, + daadvert->spilist, + digest); + if(result == 0) + { + /*------------------------------------------------------------*/ + /* Calculate the size of the DSA signature from the authblock */ + /*------------------------------------------------------------*/ + /* we have to calculate the signature length since */ + /* autharray[i].length is (stupidly) the length of the entire */ + /* authblock */ + signaturelen = autharray[i].length - (autharray[i].spistrlen + 10); + + /*----------------------*/ + /* Verify the signature */ + /*----------------------*/ + if(SLPCryptoDSAVerify(key, + digest, + sizeof(digest), + autharray[i].authstruct, + signaturelen)) + { + break; + } + + result = SLP_ERROR_AUTHENTICATION_FAILED; + } + } + } + + if(key) SLPCryptoDSAKeyDestroy(key); + + return result; +} + + +/*=========================================================================*/ +int SLPAuthVerifySAAdvert(SLPSpiHandle hspi, + int emptyisfail, + const SLPSAAdvert* saadvert) +/* Verify authenticity of the specified SAAdvert */ +/* */ +/* Parameters: hspi (IN) open SPI handle */ +/* emptyisfail (IN) if non-zero, messages without authblocks */ +/* will fail */ +/* spistrlen (IN) length of the spi string */ +/* sprstr (IN) the spi string */ +/* saadvert (IN) the SAADVERT to verify */ +/* */ +/* Returns: 0 on success or SLP_ERROR_xxx code on failure */ +/*=========================================================================*/ +{ + return 0; +} + + +/*=========================================================================*/ +int SLPAuthSignString(SLPSpiHandle hspi, + int spistrlen, + const char* spistr, + unsigned short stringlen, + const char* string, + int* authblocklen, + unsigned char** authblock) +/* Generate an authblock signature for an attribute list */ +/* */ +/* Parameters: hspi (IN) open SPI handle */ +/* spistrlen (IN) length of the SPI string */ +/* spistr (IN) SPI to sign with */ +/* attrlistlen (IN) the length of the URL to sign */ +/* attrlist (IN) the url to sign */ +/* authblocklen (OUT) the length of the authblock signature */ +/* authblock (OUT) buffer containing authblock signature */ +/* must be freed by the caller */ +/* */ +/* Returns: 0 on success or SLP_ERROR_xxx code on failure */ +/*=========================================================================*/ +{ + int result; + SLPCryptoDSAKey* key; + unsigned char digest[20]; + int defaultspistrlen = 0; + char* defaultspistr = 0; + + /* NULL out the authblock and spistr just to be safe */ + key = 0; + *authblock = 0; + *authblocklen = 0; + spistr = 0; + spistrlen = 0; + + /*--------------------------------*/ + /* Get a private key for the SPI */ + /*--------------------------------*/ + if(spistr) + { + key = SLPSpiGetDSAKey(hspi, + SLPSPI_KEY_TYPE_PRIVATE, + spistrlen, + spistr, + &key); + } + else + { + if(SLPSpiGetDefaultSPI(hspi, + SLPSPI_KEY_TYPE_PRIVATE, + &defaultspistrlen, + &defaultspistr)) + { + spistr = defaultspistr; + spistrlen = defaultspistrlen; + + key = SLPSpiGetDSAKey(hspi, + SLPSPI_KEY_TYPE_PRIVATE, + spistrlen, + spistr, + &key); + } + } + + if(key == 0) + { + result = SLP_ERROR_AUTHENTICATION_UNKNOWN; + goto ERROR; + } + + /*--------------------------*/ + /* Generate the SHA1 digest */ + /*--------------------------*/ + result = SLPAuthDigestString(spistrlen, + spistr, + stringlen, + string, + 0xffffffff, /* very long expiration (for now) */ + digest); + + /*---------------------------------------------*/ + /* Sign the digest and put it in the authblock */ + /*---------------------------------------------*/ + if(result == 0) + { + result = SLPAuthSignDigest(spistrlen, + spistr, + key, + digest, + authblocklen, + authblock); + } + +ERROR: + /*---------*/ + /* Cleanup */ + /*---------*/ + if(defaultspistr) xfree(defaultspistr); + if(key) SLPCryptoDSAKeyDestroy(key); + return result; +} + + +/*=========================================================================*/ +int SLPAuthSignUrl(SLPSpiHandle hspi, + int spistrlen, + const char* spistr, + unsigned short urllen, + const char* url, + int* authblocklen, + unsigned char** authblock) +/* Generate an authblock signature for a Url */ +/* */ +/* Parameters: hspi (IN) open SPI handle */ +/* spistrlen (IN) length of the SPI string */ +/* spistr (IN) SPI to sign with */ +/* urllen (IN) the length of the URL to sign */ +/* url (IN) the url to sign */ +/* authblocklen (OUT) the length of the authblock signature */ +/* authblock (OUT) buffer containing authblock signature */ +/* must be freed by the caller */ +/* */ +/* Returns: 0 on success or SLP_ERROR_xxx code on failure */ +/*=========================================================================*/ +{ + return SLPAuthSignString(hspi, + spistrlen, + spistr, + urllen, + url, + authblocklen, + authblock); +} + + +/*=========================================================================*/ +int SLPAuthSignDAAdvert(SLPSpiHandle hspi, + unsigned short spistrlen, + const char* spistr, + unsigned long bootstamp, + unsigned short urllen, + const char* url, + unsigned short attrlistlen, + const char* attrlist, + unsigned short scopelistlen, + const char* scopelist, + unsigned short daspistrlen, + const char* daspistr, + int* authblocklen, + unsigned char** authblock) +/* Generate an authblock signature for a DAADVERT */ +/* */ +/* Parameters: hspi (IN) open SPI handle */ +/* spistrlen (IN) length of the spi string */ +/* sprstr (IN) the spi string */ +/* bootstamp (IN) the statless DA boot timestamp */ +/* urllen (IN) the length of the URL to sign */ +/* url (IN) the url to sign */ +/* attrlistlen (IN) the length of the URL to sign */ +/* attrlist (IN) the url to sign */ +/* scopelistlen (IN) the length of the DA's scope list */ +/* scopelist (IN) the DA's scope list */ +/* daspistrlen (IN) the length of the list of DA's SPIs */ +/* daspistr (IN) the list of the DA's SPI's */ +/* authblocklen (OUT) the length of the authblock signature */ +/* authblock (OUT) buffer containing authblock signature must */ +/* be freed by the caller */ +/* */ +/* Returns: 0 on success or SLP_ERROR_xxx code on failure */ +/*=========================================================================*/ +{ + int result; + SLPCryptoDSAKey* key; + unsigned char digest[20]; + int defaultspistrlen = 0; + char* defaultspistr = 0; + + /* NULL out the authblock and spistr just to be safe */ + key = 0; + *authblock = 0; + *authblocklen = 0; + spistr = 0; + spistrlen = 0; + + /*--------------------------------*/ + /* Get a private key for the SPI */ + /*--------------------------------*/ + if(spistr) + { + key = SLPSpiGetDSAKey(hspi, + SLPSPI_KEY_TYPE_PRIVATE, + spistrlen, + spistr, + &key); + } + else + { + if(SLPSpiGetDefaultSPI(hspi, + SLPSPI_KEY_TYPE_PRIVATE, + &defaultspistrlen, + &defaultspistr)) + { + spistr = defaultspistr; + spistrlen = defaultspistrlen; + + key = SLPSpiGetDSAKey(hspi, + SLPSPI_KEY_TYPE_PRIVATE, + spistrlen, + spistr, + &key); + } + } + + if(key == 0) + { + result = SLP_ERROR_AUTHENTICATION_UNKNOWN; + goto ERROR; + } + + /*--------------------------*/ + /* Generate the SHA1 digest */ + /*--------------------------*/ + result = SLPAuthDigestDAAdvert(spistrlen, + spistr, + 0xffffffff, + bootstamp, + urllen, + url, + attrlistlen, + attrlist, + scopelistlen, + scopelist, + daspistrlen, + daspistr, + digest); + + /*---------------------------------------------*/ + /* Sign the digest and put it in the authblock */ + /*---------------------------------------------*/ + if(result == 0) + { + result = SLPAuthSignDigest(spistrlen, + spistr, + key, + digest, + authblocklen, + authblock); + } + +ERROR: + /*---------*/ + /* Cleanup */ + /*---------*/ + if(defaultspistr) xfree(defaultspistr); + if(key) SLPCryptoDSAKeyDestroy(key); + return result; +} + + +/*=========================================================================*/ +int SLPAuthSignSAAdvert(unsigned short spistrlen, + const char* spistr, + unsigned short urllen, + const char* url, + unsigned short attrlistlen, + const char* attrlist, + unsigned short scopelistlen, + const char* scopelist, + int* authblocklen, + unsigned char** authblock) +/* Generate an authblock signature for a SAADVERT */ +/* */ +/* Parameters: spistrlen (IN) length of the spi string */ +/* sprstr (IN) the spi string */ +/* urllen (IN) the length of the URL to sign */ +/* url (IN) the url to sign */ +/* attrlistlen (IN) the length of the URL to sign */ +/* attrlist (IN) the url to sign */ +/* scopelistlen (IN) the length of the DA's scope list */ +/* scopelist (IN) the DA's scope list */ +/* authblocklen (OUT) the length of the authblock signature */ +/* authblock (OUT) buffer containing authblock signature */ +/* */ +/* Returns: 0 on success or SLP_ERROR_xxx code on failure */ +/*=========================================================================*/ +{ + *authblocklen = 0; + *authblock = 0; + return 0; +} + --- common/slp_crypto.c +++ common/slp_crypto.c 2003/12/15 17:15:46 @@ -0,0 +1,183 @@ +/***************************************************************************/ +/* */ +/* Project: OpenSLP - OpenSource implementation of Service Location */ +/* Protocol */ +/* */ +/* File: slp_crpyto.c */ +/* */ +/* Abstract: Primitive cryptographic functions to support DSA signature */ +/* of SHA1 digests. Current implementation is uses the */ +/* OpenSSL (http://www.openssl.org)) crypto library. */ +/* */ +/*-------------------------------------------------------------------------*/ +/* */ +/* Please submit patches to http://www.openslp.org */ +/* */ +/*-------------------------------------------------------------------------*/ +/* */ +/* Copyright (C) 2000 Caldera Systems, Inc */ +/* All rights reserved. */ +/* */ +/* Redistribution and use in source and binary forms, with or without */ +/* modification, are permitted provided that the following conditions are */ +/* met: */ +/* */ +/* Redistributions of source code must retain the above copyright */ +/* notice, this list of conditions and the following disclaimer. */ +/* */ +/* Redistributions in binary form must reproduce the above copyright */ +/* notice, this list of conditions and the following disclaimer in */ +/* the documentation and/or other materials provided with the */ +/* distribution. */ +/* */ +/* Neither the name of Caldera Systems nor the names of its */ +/* contributors may be used to endorse or promote products derived */ +/* from this software without specific prior written permission. */ +/* */ +/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */ +/* `AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */ +/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR */ +/* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE CALDERA */ +/* SYSTEMS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ +/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ +/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, */ +/* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON */ +/* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */ +/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE */ +/* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* */ +/***************************************************************************/ + +#include "slp_crypto.h" +#include "slp_message.h" + +/*=========================================================================*/ +int SLPCryptoSHA1Digest(const unsigned char* data, + int datalen, + unsigned char* digest) +/* Generate a SHA1 digest for the specified block data */ +/* */ +/* Parameters: data (IN) pointer to buffer that to be hashed */ +/* datalen (IN) size of the data buffer in bytes */ +/* digest (OUT) pointer to buffer of at least 20 bytes in */ +/* size where the digest will be copied */ +/* */ +/* Returns: zero on success. non-zero on failure */ +/*=========================================================================*/ +{ + if(SHA1(data,datalen,digest)) + { + return 0; + } + + return -1; +} + + +/*=========================================================================*/ +SLPCryptoDSAKey* SLPCryptoDSAKeyDup(SLPCryptoDSAKey* dsa) +/* Duplicates the specified key */ +/* */ +/* Parameters: dsa (IN) the key to duplicate */ +/* */ +/* Returns: Pointer to the duplicated key. NULL on failure. Caller is */ +/* responsible for SLPCryptoDSAKeyDestroy()ing the returned */ +/* pointer */ +/*=========================================================================*/ +{ + SLPCryptoDSAKey* result; + + result = DSA_new(); + if(result) + { + result->p = BN_dup(dsa->p); + result->q = BN_dup(dsa->q); + result->g = BN_dup(dsa->g); + result->priv_key = BN_dup(dsa->priv_key); + result->pub_key = BN_dup(dsa->pub_key); + } + + return result; +} + +/*=========================================================================*/ +void SLPCryptoDSAKeyDestroy(SLPCryptoDSAKey* dsa) +/* Destroy a key that was created by SLPCryptoDSAKeyCreate(). Care should */ +/* be taken to make sure all private keys are destroyed */ +/* */ +/* Parameters: dsa (IN) the key to destroy */ +/* */ +/* Returns: None */ +/*=========================================================================*/ +{ + DSA_free(dsa); +} + + +/*=========================================================================*/ +int SLPCryptoDSASignLen(SLPCryptoDSAKey* key) +/* Determine the length of a signatures produced with specified key. */ +/* */ +/* Parameters: key (IN) the key that will be used for signing */ +/* */ +/* Returns: The length of signatures in bytes */ +/*=========================================================================*/ +{ + return DSA_size(key); +} + + +/*=========================================================================*/ +int SLPCryptoDSASign(SLPCryptoDSAKey* key, + const unsigned char* digest, + int digestlen, + unsigned char* signature, + int* signaturelen) +/* Sign the specified digest with the specified DSA key */ +/* */ +/* Parameters: key (IN) Signing (private) key */ +/* digest (IN) pointer to digest buffer */ +/* digestlen (IN) length of the digest buffer */ +/* signature (OUT) buffer that will hold the ASN.1 DER */ +/* encoded signature. */ +/* signaturelen (OUT) The length of the signature buffer */ +/* SLPCryptoDSASignLen(key) should be */ +/* called to determine how big signature */ +/* should be. */ +/* */ +/* Returns: zero on success. non-zero on failure */ +/*=========================================================================*/ +{ + return DSA_sign(0, /* it does not look like the type param is used? */ + digest, + digestlen, + signature, + signaturelen, + key) == 0; +} + + +/*=========================================================================*/ +int SLPCryptoDSAVerify(SLPCryptoDSAKey* key, + const unsigned char* digest, + int digestlen, + const unsigned char* signature, + int signaturelen) +/* Verify a DSA signature to ensure it matches the specified digest */ +/* */ +/* Parameters: key (IN) Verifying (public) key */ +/* (IN) pointer to the digest buffer */ +/* (IN) length of the digest buffer */ +/* (IN) the ASN.1 DER encoded signature */ +/* (IN) the length of the signature */ +/* */ +/* Returns: 1 if the signature is valid, 0 of it is not */ +/*=========================================================================*/ +{ + return DSA_verify(0, /* it does not look like the type param is used? */ + digest, + digestlen, + (unsigned char*)signature, /* broken DSA_verify() declaration */ + signaturelen, + key); +} --- common/slp_spi.c +++ common/slp_spi.c 2003/12/15 17:15:46 @@ -0,0 +1,459 @@ +/***************************************************************************/ +/* */ +/* Project: OpenSLP - OpenSource implementation of Service Location */ +/* Protocol Version 2 */ +/* */ +/* File: slp_spi.h */ +/* */ +/* Abstract: Functions for fetching SPI information from the filesystem */ +/* Current implementation uses OpenSSL. For details see */ +/* (see http://www.openssl.org */ +/* */ +/*-------------------------------------------------------------------------*/ +/* */ +/* Please submit patches to http://www.openslp.org */ +/* */ +/*-------------------------------------------------------------------------*/ +/* */ +/* Copyright (C) 2000 Caldera Systems, Inc */ +/* All rights reserved. */ +/* */ +/* Redistribution and use in source and binary forms, with or without */ +/* modification, are permitted provided that the following conditions are */ +/* met: */ +/* */ +/* Redistributions of source code must retain the above copyright */ +/* notice, this list of conditions and the following disclaimer. */ +/* */ +/* Redistributions in binary form must reproduce the above copyright */ +/* notice, this list of conditions and the following disclaimer in */ +/* the documentation and/or other materials provided with the */ +/* distribution. */ +/* */ +/* Neither the name of Caldera Systems nor the names of its */ +/* contributors may be used to endorse or promote products derived */ +/* from this software without specific prior written permission. */ +/* */ +/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */ +/* `AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */ +/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR */ +/* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE CALDERA */ +/* SYSTEMS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ +/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ +/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, */ +/* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON */ +/* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */ +/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE */ +/* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* */ +/***************************************************************************/ + +#include "slp_spi.h" +#include "slp_xmalloc.h" + +#include +#include +#include +#include + + +#define MAX_SPI_ENTRY_LEN 1024 +#define PUBLIC_TOKEN "PUBLIC" +#define PRIVATE_TOKEN "PRIVATE" + + +/*-------------------------------------------------------------------------*/ +void SLPSpiEntryFree(SLPSpiEntry* victim) +/*-------------------------------------------------------------------------*/ +{ + if(victim->keyfilename) xfree(victim->keyfilename); + if(victim->spistr) xfree(victim->spistr); + if(victim->key) SLPCryptoDSAKeyDestroy(victim->key); + if(victim) xfree(victim); +} + +/*-------------------------------------------------------------------------*/ +SLPSpiEntry* SLPSpiEntryFind(SLPList* cache, + int keytype, + int spistrlen, + const char* spistr) +/* pass in null spistr to find the first Cached entry */ +/*-------------------------------------------------------------------------*/ +{ + SLPSpiEntry* entry = (SLPSpiEntry*)cache->head; + while(entry) + { + if(spistr) + { + if (entry->spistrlen == spistrlen && + memcmp(entry->spistr,spistr,spistrlen) == 0 && + entry->keytype == keytype) + { + return entry; + } + } + else + { + if(keytype == SLPSPI_KEY_TYPE_ANY || entry->keytype == keytype) + { + return entry; + } + } + entry = (SLPSpiEntry*)entry->listitem.next; + } + + return 0; +} + +/*-------------------------------------------------------------------------*/ +SLPCryptoDSAKey* SLPSpiReadKeyFile(const char* keyfile, int keytype) +/*-------------------------------------------------------------------------*/ +{ + FILE* fp; + SLPCryptoDSAKey* result = 0; + + fp = fopen(keyfile,"r"); + if(fp) + { + if(keytype == SLPSPI_KEY_TYPE_PUBLIC) + { + result = PEM_read_DSA_PUBKEY(fp, &result, NULL, NULL); + } + else if (keytype == SLPSPI_KEY_TYPE_PRIVATE) + { + result = PEM_read_DSAPrivateKey(fp, &result, NULL, NULL); + } + + fclose(fp); + } + + return result; +} + + +/*-------------------------------------------------------------------------*/ +SLPSpiEntry* SLPSpiReadSpiFile(FILE* fp, int keytype) +/* Caller needs to free returned memory SLPSpiEntryFree() */ +/*-------------------------------------------------------------------------*/ +{ + SLPSpiEntry* result; + char tmp; + char* line; + char* slider1; + char* slider2; + + /*----------------------------*/ + /* Allocate memory for result */ + /*----------------------------*/ + line = (char*) xmalloc(MAX_SPI_ENTRY_LEN); + result = (SLPSpiEntry*) xmalloc(sizeof(SLPSpiEntry)); + if(result == 0 || line == 0) + { + return 0; + } + memset(result,0,sizeof(SLPSpiEntry)); + + + /*---------------------------*/ + /* Read the next valid entry */ + /*---------------------------*/ + while(fgets(line, MAX_SPI_ENTRY_LEN, fp)) + { + /*----------------------*/ + /* read the first token */ + /*----------------------*/ + slider1 = line; + /* skip leading whitespace */ + while(*slider1 && *slider1 <= 0x20) slider1++; + /* skip all white lines */ + if(*slider1 == 0) continue; + /* skip commented lines */ + if(*slider1 == '#') continue; + /* PUBLIC|PRIVATE */ + slider2 = slider1; + while(*slider2 && *slider2 > 0x20) slider2++; + if(strncasecmp(PUBLIC_TOKEN,slider1,slider2-slider1) == 0) + { + if(keytype == SLPSPI_KEY_TYPE_PRIVATE) continue; + result->keytype = SLPSPI_KEY_TYPE_PUBLIC; + } + else if(strncasecmp(PRIVATE_TOKEN,slider1,slider2-slider1) == 0) + { + if(keytype == SLPSPI_KEY_TYPE_PUBLIC) continue; + result->keytype = SLPSPI_KEY_TYPE_PRIVATE; + } + else + { + /* unknown token */ + continue; + } + + /*-----------------------*/ + /* read the second token */ + /*-----------------------*/ + slider1=slider2; + /* skip leading whitespace */ + while(*slider1 && *slider1 <= 0x20) slider1++; + /* SPI string */ + slider2 = slider1; + while(*slider2 && *slider2 > 0x20) slider2++; + /* SPI string is at slider1 length slider2 - slider1 */ + + result->spistr = (char*)xmalloc(slider2-slider1); + if(result->spistr) + { + memcpy(result->spistr,slider1,slider2-slider1); + result->spistrlen = slider2-slider1; + } + + /*----------------------*/ + /* read the third token */ + /*----------------------*/ + slider1=slider2; + /* skip leading whitespace */ + while(*slider1 && *slider1 <= 0x20) slider1++; + /* SPI string */ + slider2 = slider1; + while(*slider2 && *slider2 > 0x20) slider2++; + /* key file path is at slider1 length slider2 - slider1 */ + tmp = *slider2; + *slider2 = 0; + result->keyfilename = xstrdup(slider1); + result->key = 0; /* read it later */ + *slider2 = tmp; + + /*-----------------*/ + /* See what we got */ + /*-----------------*/ + if(result && + result->spistr && + result->keyfilename) + { + goto SUCCESS; + } + + if(result->keyfilename) xfree(result->keyfilename); + if(result->spistr) xfree(result->spistr); + } + + if (result) + { + xfree(result); + result = 0; + } + +SUCCESS: + + if (line) xfree(line); + + return result; +} + + +/*=========================================================================*/ +SLPSpiHandle SLPSpiOpen(const char* spifile, int cacheprivate) +/* Initializes SLP SPI data storage. */ +/* */ +/* Parameters: spifile (IN) path of slp.spi file */ +/* cacheprivate (IN) should private keys be cached in handle */ +/* */ +/* Returns: valid pointer. NULL on failure */ +/*=========================================================================*/ +{ + FILE* fp; + SLPSpiHandle result = 0; + SLPSpiEntry* spientry; + + fp = fopen(spifile,"r"); + if(fp) + { + result = xmalloc(sizeof(struct _SLPSpiHandle)); + if(result == 0) return 0; + memset(result, 0, sizeof(struct _SLPSpiHandle)); + + result->spifile = xstrdup(spifile); + result->cacheprivate = cacheprivate; + while(1) + { + spientry = SLPSpiReadSpiFile(fp, SLPSPI_KEY_TYPE_ANY); + if(spientry == 0) break; + if(spientry->keytype == SLPSPI_KEY_TYPE_PRIVATE && + cacheprivate == 0) + { + /* destroy the key cause we're not suppose to cache it */ + SLPCryptoDSAKeyDestroy(spientry->key); + } + + SLPListLinkHead(&(result->cache),(SLPListItem*)spientry); + } + + fclose(fp); + } + + return result; +} + +/*=========================================================================*/ +void SLPSpiClose(SLPSpiHandle hspi) +/* Release SLP SPI data storage associated with the specified SLPSpiHandle */ +/* */ +/* Parameters: hspi (IN) SLPSpiHandle to deinitialize */ +/*=========================================================================*/ +{ + if(hspi) + { + if(hspi->spifile) xfree(hspi->spifile); + while(hspi->cache.count) + { + SLPSpiEntryFree((SLPSpiEntry*)SLPListUnlink(&(hspi->cache),hspi->cache.head)); + } + + xfree(hspi); + } +} + + +/*=========================================================================*/ +char* SLPSpiGetDefaultSPI(SLPSpiHandle hspi, + int keytype, + int* spistrlen, + char** spistr) +/* Gets a reference to the default SPI string for the specified keytype */ +/* */ +/* Parameters: hspi (IN) handle obtained from call to SLPSpiOpen() */ +/* keytype (IN) type of key */ +/* spistrlen (OUT) length or the returned spistr */ +/* spistr (OUT) pointer to spistr. MUST be freed by */ +/* caller!! */ +/* */ +/* Returns: Pointer to the default SPI string. Pointer may *not* be NULL */ +/* terminated */ +/*=========================================================================*/ +{ + SLPSpiEntry* entry; + + *spistr = 0; + *spistrlen = 0; + + if(hspi) + { + + entry = SLPSpiEntryFind(&(hspi->cache),keytype,0,0); + if(entry) + { + *spistr = xmalloc(entry->spistrlen); + if(*spistr) + { + memcpy(*spistr, entry->spistr, entry->spistrlen); + *spistrlen = entry->spistrlen; + } + } + } + + return *spistr; +} + + +/*=========================================================================*/ +SLPCryptoDSAKey* SLPSpiGetDSAKey(SLPSpiHandle hspi, + int keytype, + int spistrlen, + const char* spistr, + SLPCryptoDSAKey **key) +/* Fetches a copy of the private key file used to sign SLP messages. */ +/* */ +/* Parameters: hspi (IN) handle obtained from call to SLPSpiOpen() */ +/* keytype (IN) the type of key desired */ +/* spistrlen (IN) the length of the spistr */ +/* spistr (IN) spistr associated with the key */ +/* key (OUT) the private key. Caller should use */ +/* SLPCryptoDSAKeyDestroy() to free key memory */ +/* */ +/* Returns: A valid pointer. NULL on failure. Caller should use */ +/* SLPCryptoDSAKeyDestroy() to free key memory */ +/*=========================================================================*/ +{ + SLPSpiEntry* tmp = 0; + + /* For safety NULL out the key from the beginning */ + *key = 0; + + if(hspi) + { + tmp = SLPSpiEntryFind(&(hspi->cache), + keytype, + spistrlen, + spistr); + if(tmp) + { + if(tmp->key == 0) + { + if(keytype == SLPSPI_KEY_TYPE_PRIVATE && hspi->cacheprivate == 0) + { + *key = SLPSpiReadKeyFile(tmp->keyfilename,SLPSPI_KEY_TYPE_PRIVATE); + return *key; + } + + tmp->key = SLPSpiReadKeyFile(tmp->keyfilename,keytype); + } + + *key = SLPCryptoDSAKeyDup(tmp->key); + } + } + + return *key; +} + + +/*=========================================================================*/ +int SLPSpiCanVerify(SLPSpiHandle hspi, + int spistrlen, + const char* spistr) +/* Determine if we understand the specified SPI. No SPI is always */ +/* returns true */ +/* */ +/* Parameters: hspi (IN) handle obtained from call to SLPSpiOpen() */ +/* spistrlen (IN) the length of the spistr */ +/* spistr (IN) the SPI string */ +/* */ +/* Returns Non-zero if we verify specified the SPI */ +/*=========================================================================*/ +{ + if (hspi == 0) + { + return 0; + } + + if(spistrlen == 0 || spistr == NULL) + { + return 1; + } + + return (SLPSpiEntryFind(&(hspi->cache), + SLPSPI_KEY_TYPE_PUBLIC, + spistrlen, + spistr) != 0); +} + + +/*=========================================================================*/ +int SLPSpiCanSign(SLPSpiHandle hspi, + int spistrlen, + const char* spistr) +/* Determine if we understand the specified SPI. No SPI is always */ +/* return true */ +/* */ +/* Parameters: hspi (IN) handle obtained from call to SLPSpiOpen() */ +/* spistrlen (IN) the length of the spistr */ +/* spistr (IN) the SPI string */ +/* */ +/* Returns Non-zero if we sign using the specified SPI */ +/*=========================================================================*/ +{ + return (SLPSpiEntryFind(&(hspi->cache), + SLPSPI_KEY_TYPE_PRIVATE, + spistrlen, + spistr) != 0); +} + + --- slpd/slpd_spi.c +++ slpd/slpd_spi.c 2003/12/15 17:15:46 @@ -0,0 +1,83 @@ +/***************************************************************************/ +/* */ +/* Project: OpenSLP - OpenSource implementation of Service Location */ +/* Protocol Version 2 */ +/* */ +/* File: slpd_spi.c */ +/* */ +/* Abstract: Functions and variables for slpd usage of common SPI code */ +/* */ +/*-------------------------------------------------------------------------*/ +/* */ +/* Please submit patches to http://www.openslp.org */ +/* */ +/*-------------------------------------------------------------------------*/ +/* */ +/* Copyright (C) 2000 Caldera Systems, Inc */ +/* All rights reserved. */ +/* */ +/* Redistribution and use in source and binary forms, with or without */ +/* modification, are permitted provided that the following conditions are */ +/* met: */ +/* */ +/* Redistributions of source code must retain the above copyright */ +/* notice, this list of conditions and the following disclaimer. */ +/* */ +/* Redistributions in binary form must reproduce the above copyright */ +/* notice, this list of conditions and the following disclaimer in */ +/* the documentation and/or other materials provided with the */ +/* distribution. */ +/* */ +/* Neither the name of Caldera Systems nor the names of its */ +/* contributors may be used to endorse or promote products derived */ +/* from this software without specific prior written permission. */ +/* */ +/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */ +/* `AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */ +/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR */ +/* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE CALDERA */ +/* SYSTEMS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ +/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ +/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, */ +/* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON */ +/* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */ +/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE */ +/* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* */ +/***************************************************************************/ + + +/*=========================================================================*/ +/* slpd includes */ +/*=========================================================================*/ +#include "slpd_spi.h" + +/*=========================================================================*/ +SLPSpiHandle G_SlpdSpiHandle = 0; +/*=========================================================================*/ + + +/*=========================================================================*/ +int SLPDSpiInit(const char* spifile) +/*=========================================================================*/ +{ + if(G_SlpdSpiHandle) + { + SLPSpiClose(G_SlpdSpiHandle); + G_SlpdSpiHandle = 0; + } + + G_SlpdSpiHandle = SLPSpiOpen(spifile,1); + return (G_SlpdSpiHandle == 0); +} + +#ifdef DEBUG +/*=========================================================================*/ +void SLPDSpiDeinit() +/*=========================================================================*/ +{ + SLPSpiClose(G_SlpdSpiHandle); +} +#endif + + --- slpd/slpd_spi.h +++ slpd/slpd_spi.h 2003/12/15 17:15:46 @@ -0,0 +1,75 @@ +/***************************************************************************/ +/* */ +/* Project: OpenSLP - OpenSource implementation of Service Location */ +/* Protocol Version 2 */ +/* */ +/* File: slpd_spi.h */ +/* */ +/* Abstract: Functions and variables for slpd usage of common SPI code */ +/* */ +/*-------------------------------------------------------------------------*/ +/* */ +/* Please submit patches to http://www.openslp.org */ +/* */ +/*-------------------------------------------------------------------------*/ +/* */ +/* Copyright (C) 2000 Caldera Systems, Inc */ +/* All rights reserved. */ +/* */ +/* Redistribution and use in source and binary forms, with or without */ +/* modification, are permitted provided that the following conditions are */ +/* met: */ +/* */ +/* Redistributions of source code must retain the above copyright */ +/* notice, this list of conditions and the following disclaimer. */ +/* */ +/* Redistributions in binary form must reproduce the above copyright */ +/* notice, this list of conditions and the following disclaimer in */ +/* the documentation and/or other materials provided with the */ +/* distribution. */ +/* */ +/* Neither the name of Caldera Systems nor the names of its */ +/* contributors may be used to endorse or promote products derived */ +/* from this software without specific prior written permission. */ +/* */ +/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */ +/* `AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */ +/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR */ +/* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE CALDERA */ +/* SYSTEMS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ +/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ +/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, */ +/* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON */ +/* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */ +/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE */ +/* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* */ +/***************************************************************************/ + +#ifndef SLPD_SPI_H_INCLUDED + +/*=========================================================================*/ +/* slpd includes */ +/*=========================================================================*/ +#include "slpd.h" + +/*=========================================================================*/ +/* Common code code includes */ +/*=========================================================================*/ +#include "slp_spi.h" + +/*=========================================================================*/ +extern SLPSpiHandle G_SlpdSpiHandle; +/*=========================================================================*/ + + +/*=========================================================================*/ +int SLPDSpiInit(const char* spifile); +/*=========================================================================*/ + + +/*=========================================================================*/ +void SLPDSpiDeinit(); +/*=========================================================================*/ + +#endif