Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

SSL reporrt incorrect Message Authentication Code - help.

20 views
Skip to first unread message

Nirave Kadakia

unread,
Apr 16, 2002, 3:46:59 PM4/16/02
to
I'm currently trying to implement a SMTP/TLS client with NSS. I'm
tyring to alter the example at
nss/mozilla/security/nss/cmd/SSLsample/client.c to get work with the
mail server.

For SMTP over TLS, an unencrypted socket connection has to be made, and
then TLS is started on that same connection later on. I've been able to
communicate with the server initially, import the socket in to the SSL
layer, and reset the handshake without any errors.

However, during the first encrypted write, I get the following error:
Error in function PR_Write: -12272
- SSL peer reports incorrect Message Authentication Code.

Does any one know the cause of this?

If needed, I can post the code is someone is nice enough to check it
out. I won't burden the newsgroup with it now since I bet this problem
is common and someone with experience can solve it easily (I hope).

-Nirave

Julien Pierre

unread,
Apr 16, 2002, 5:45:47 PM4/16/02
to
Nirave,

Nirave Kadakia wrote:
>
> However, during the first encrypted write, I get the following error:
> Error in function PR_Write: -12272
> - SSL peer reports incorrect Message Authentication Code.
>
> Does any one know the cause of this?

- Are you using the correct SSL options (eg. set as client, TLS protocol
enabled, ciphers set) ? If you bypass the import step and connect to an
SSL server directly, do you get the error ?

- Did you do SSL_ResetHandshake on your socket after the import ? If
not, you must do that in order for SSL to work after importing a non-SSL
socket to SSL.

--
"Except for the lack of debugging and the ps thing, [Linux] kernel
threads are generally fine right now. And if you're not too fussed
about the more fiddly details of POSIX threads, and your application
doesn't spend most of its time in thread creation, then LinuxThreads
is great too."

Linux-Kernel archive

Nirave Kadakia

unread,
Apr 16, 2002, 7:29:33 PM4/16/02
to
Julien Pierre wrote:

> Nirave,
>
> Nirave Kadakia wrote:
>
>>However, during the first encrypted write, I get the following error:
>>Error in function PR_Write: -12272
>> - SSL peer reports incorrect Message Authentication Code.
>>
>>Does any one know the cause of this?
>>
>
> - Are you using the correct SSL options (eg. set as client, TLS protocol
> enabled, ciphers set) ? If you bypass the import step and connect to an
> SSL server directly, do you get the error ?
>


I believe I'm using the correct options. I set the options with
SSL_ENABLE_TLS, SSL_HANDSHAKE_AS_CLIENT, and set the ciphers with TLS
cipher suites. I've included the diff between my modified client.c and
client.c.

I just tried bypassing the import step and received an assertion failure
that a file descripter was NULL.


> - Did you do SSL_ResetHandshake on your socket after the import ? If
> not, you must do that in order for SSL to work after importing a non-SSL
> socket to SSL.
>


Yes, I did. I ran the function after Importing and setting all my
options. I'll include the code because I probably did something
drastically stupid.

Thanks for your help!


THE DIFF between my client.c and the original client.c


95,98d94
< char *readBuffer;
< int numBytes = 0;
< readBuffer = PORT_Alloc(RD_BUF_SIZE);
<
116c112
< #if 1
---
> #if 0
119,127d114
<
< PR_Write(tcpSocket, "ehlo\n", strlen("ehlo\n"));
< numBytes = PR_Read(tcpSocket, readBuffer, RD_BUF_SIZE);
< printf("test: read this many bytes %d\n", numBytes);
< PR_Write(tcpSocket, "STARTTLS\n", strlen("STARTTLS\n"));
< numBytes = PR_Read(tcpSocket, readBuffer, RD_BUF_SIZE);
< printf("test: read this many bytes %d\n", numBytes);
< numBytes = PR_Read(tcpSocket, readBuffer, RD_BUF_SIZE);
< printf("test: read this many bytes %d\n", numBytes);
140,142d126
<
< /* PR_Close(sslSocket);*/
<
158,168d141
<
< /* secStatus = SSL_OptionSet(sslSocket, SSL_ENABLE_SSL2, PR_FALSE);
< secStatus = SSL_OptionSet(sslSocket, SSL_ENABLE_SSL3, PR_FALSE);
< */
<
< secStatus = SSL_OptionSet(sslSocket, SSL_ENABLE_TLS, PR_TRUE);
< if (secStatus != SECSuccess) {
< errWarn("SSL_OptionSet:SSL_SECURITY");
< goto loser;
< }
<
175,193d147
<
< secStatus = SSL_CipherPrefSet(sslSocket,
TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, PR_TRUE);
< if (secStatus != SECSuccess) {
< errWarn("SSL_OptionSet:SSL_CipherPrefSet");
< goto loser;
< }
<
< secStatus = SSL_CipherPrefSet(sslSocket,
TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, PR_TRUE);
< if (secStatus != SECSuccess) {
< errWarn("SSL_OptionSet:SSL_CipherPrefSet");
< goto loser;
< }
<
< secStatus = SSL_CipherPrefSet(sslSocket,
TLS_DHE_DSS_WITH_RC4_128_SHA, PR_TRUE);
< if (secStatus != SECSuccess) {
< errWarn("SSL_OptionSet:SSL_CipherPrefSet");
< goto loser;
< }
<
235,236c189
< /*const char requestString[] = {"GET /testfile HTTP/1.0\r\n\r\n" };*/
< const char requestString[] = {"quit\n" };
---
> const char requestString[] = {"GET /testfile HTTP/1.0\r\n\r\n" };
305d257
<
338,339c290
< /*prStatus = PR_Connect(sslSocket, addr, PR_INTERVAL_NO_TIMEOUT);
<
---
> prStatus = PR_Connect(sslSocket, addr, PR_INTERVAL_NO_TIMEOUT);
343c294
< }*/
---
> }


THE ACTUAL SOURCE CODE for my client.c

/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Netscape security libraries.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/

/****************************************************************************
* SSL client program that sets up a connection to SSL server,
transmits *
* some data and then reads the reply
*

****************************************************************************/


#include <stdio.h>
#include <string.h>

#if defined(XP_UNIX)
#include <unistd.h>
#endif

#include "prerror.h"

#include "pk11func.h"
#include "secitem.h"


#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>

#include "nspr.h"
#include "plgetopt.h"
#include "prio.h"
#include "prnetdb.h"
#include "nss.h"

#include "sslsample.h"

#define RD_BUF_SIZE (60 * 1024)

extern int ssl2CipherSuites[];
extern int ssl3CipherSuites[];

GlobalThreadMgr threadMGR;
char *certNickname = NULL;
char *hostName = NULL;
char *password = NULL;
unsigned short port = 0;

static void
Usage(const char *progName)
{
fprintf(stderr,
"Usage: %s [-n rsa_nickname] [-p port] [-d dbdir] [-c connections]\n"
" [-w dbpasswd] [-C cipher(s)] hostname\n",
progName);
exit(1);
}

PRFileDesc *
setupSSLSocket(PRNetAddr *addr)
{
PRFileDesc *tcpSocket;
PRFileDesc *sslSocket;
PRSocketOptionData socketOption;
PRStatus prStatus;
SECStatus secStatus;

char *readBuffer;
int numBytes = 0;
readBuffer = PORT_Alloc(RD_BUF_SIZE);

retry:

tcpSocket = PR_NewTCPSocket();
if (tcpSocket == NULL) {
errWarn("PR_NewTCPSocket");
}

/* Make the socket blocking. */
socketOption.option = PR_SockOpt_Nonblocking;
socketOption.value.non_blocking = PR_FALSE;

prStatus = PR_SetSocketOption(tcpSocket, &socketOption);
if (prStatus != PR_SUCCESS) {
errWarn("PR_SetSocketOption");
goto loser;
}

#if 1
/* Verify that a connection can be made to the socket. */
prStatus = PR_Connect(tcpSocket, addr, PR_INTERVAL_NO_TIMEOUT);

PR_Write(tcpSocket, "ehlo\n", strlen("ehlo\n"));
numBytes = PR_Read(tcpSocket, readBuffer, RD_BUF_SIZE);
printf("test: read this many bytes %d\n", numBytes);
PR_Write(tcpSocket, "STARTTLS\n", strlen("STARTTLS\n"));
numBytes = PR_Read(tcpSocket, readBuffer, RD_BUF_SIZE);
printf("test: read this many bytes %d\n", numBytes);
numBytes = PR_Read(tcpSocket, readBuffer, RD_BUF_SIZE);
printf("test: read this many bytes %d\n", numBytes);
if (prStatus != PR_SUCCESS) {
PRErrorCode err = PR_GetError();
if (err == PR_CONNECT_REFUSED_ERROR) {
PR_Close(tcpSocket);
PR_Sleep(PR_MillisecondsToInterval(10));
fprintf(stderr, "Connection to port refused, retrying.\n");
goto retry;
}
errWarn("PR_Connect");
goto loser;
}
#endif

/* PR_Close(sslSocket);*/

/* Import the socket into the SSL layer. */
sslSocket = SSL_ImportFD(NULL, tcpSocket);
if (!sslSocket) {
errWarn("SSL_ImportFD");
goto loser;
}

/* Set configuration options. */
secStatus = SSL_OptionSet(sslSocket, SSL_SECURITY, PR_TRUE);
if (secStatus != SECSuccess) {
errWarn("SSL_OptionSet:SSL_SECURITY");
goto loser;
}


secStatus = SSL_OptionSet(sslSocket, SSL_ENABLE_SSL2, PR_FALSE);
secStatus = SSL_OptionSet(sslSocket, SSL_ENABLE_SSL3, PR_FALSE);


secStatus = SSL_OptionSet(sslSocket, SSL_ENABLE_TLS, PR_TRUE);
if (secStatus != SECSuccess) {
errWarn("SSL_OptionSet:SSL_SECURITY");
goto loser;
}

secStatus = SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
if (secStatus != SECSuccess) {
errWarn("SSL_OptionSet:SSL_HANDSHAKE_AS_CLIENT");
goto loser;
}


secStatus = SSL_CipherPrefSet(sslSocket,
TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, PR_TRUE);
if (secStatus != SECSuccess) {
errWarn("SSL_OptionSet:SSL_CipherPrefSet");
goto loser;
}

secStatus = SSL_CipherPrefSet(sslSocket,
TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, PR_TRUE);
if (secStatus != SECSuccess) {
errWarn("SSL_OptionSet:SSL_CipherPrefSet");
goto loser;
}

secStatus = SSL_CipherPrefSet(sslSocket, TLS_DHE_DSS_WITH_RC4_128_SHA,
PR_TRUE);
if (secStatus != SECSuccess) {
errWarn("SSL_OptionSet:SSL_CipherPrefSet");
goto loser;
}

/* Set SSL callback routines. */
secStatus = SSL_GetClientAuthDataHook(sslSocket,
(SSLGetClientAuthData)myGetClientAuthData,
(void *)certNickname);
if (secStatus != SECSuccess) {
errWarn("SSL_GetClientAuthDataHook");
goto loser;
}

secStatus = SSL_AuthCertificateHook(sslSocket,
(SSLAuthCertificate)myAuthCertificate,
(void *)CERT_GetDefaultCertDB());
if (secStatus != SECSuccess) {
errWarn("SSL_AuthCertificateHook");
goto loser;
}

secStatus = SSL_BadCertHook(sslSocket,
(SSLBadCertHandler)myBadCertHandler, NULL);
if (secStatus != SECSuccess) {
errWarn("SSL_BadCertHook");
goto loser;
}

secStatus = SSL_HandshakeCallback(sslSocket,
(SSLHandshakeCallback)myHandshakeCallback,
NULL);
if (secStatus != SECSuccess) {
errWarn("SSL_HandshakeCallback");
goto loser;
}

return sslSocket;

loser:

PR_Close(tcpSocket);
return NULL;
}


/*const char requestString[] = {"GET /testfile HTTP/1.0\r\n\r\n" };*/
const char requestString[] = {"quit\n" };

SECStatus
handle_connection(PRFileDesc *sslSocket, int connection)
{
int countRead = 0;
PRInt32 numBytes;
char *readBuffer;

readBuffer = PORT_Alloc(RD_BUF_SIZE);
if (!readBuffer) {
exitErr("PORT_Alloc");
}

/* compose the http request here. */

numBytes = PR_Write(sslSocket, requestString, strlen(requestString));
if (numBytes <= 0) {
errWarn("PR_Write");
PR_Free(readBuffer);
readBuffer = NULL;
return SECFailure;
}

/* read until EOF */
while (PR_TRUE) {
numBytes = PR_Read(sslSocket, readBuffer, RD_BUF_SIZE);
if (numBytes == 0) {
break; /* EOF */
}
if (numBytes < 0) {
errWarn("PR_Read");
break;
}
countRead += numBytes;
fprintf(stderr, "***** Connection %d read %d bytes (%d total).\n",
connection, numBytes, countRead );
readBuffer[numBytes] = '\0';
fprintf(stderr, "************\n%s\n************\n", readBuffer);
}

printSecurityInfo(sslSocket);

PR_Free(readBuffer);
readBuffer = NULL;

/* Caller closes the socket. */

fprintf(stderr,
"***** Connection %d read %d bytes total.\n",
connection, countRead);

return SECSuccess; /* success */
}

/* one copy of this function is launched in a separate thread for each
** connection to be made.
*/
SECStatus
do_connects(void *a, int connection)
{
PRNetAddr *addr = (PRNetAddr *)a;
PRFileDesc *sslSocket;
PRHostEnt hostEntry;
char buffer[PR_NETDB_BUF_SIZE];
PRStatus prStatus;
PRIntn hostenum;
SECStatus secStatus;


/* Set up SSL secure socket. */
sslSocket = setupSSLSocket(addr);
if (sslSocket == NULL) {
errWarn("setupSSLSocket");
return SECFailure;
}

secStatus = SSL_SetPKCS11PinArg(sslSocket, password);
if (secStatus != SECSuccess) {
errWarn("SSL_SetPKCS11PinArg");
return secStatus;
}

secStatus = SSL_SetURL(sslSocket, hostName);
if (secStatus != SECSuccess) {
errWarn("SSL_SetURL");
return secStatus;
}

/* Prepare and setup network connection. */
prStatus = PR_GetHostByName(hostName, buffer, sizeof(buffer), &hostEntry);
if (prStatus != PR_SUCCESS) {
errWarn("PR_GetHostByName");
return SECFailure;
}

hostenum = PR_EnumerateHostEnt(0, &hostEntry, port, addr);
if (hostenum == -1) {
errWarn("PR_EnumerateHostEnt");
return SECFailure;
}

/*prStatus = PR_Connect(sslSocket, addr, PR_INTERVAL_NO_TIMEOUT);

if (prStatus != PR_SUCCESS) {
errWarn("PR_Connect");
return SECFailure;
}*/

/* Established SSL connection, ready to send data. */
#if 0
secStatus = SSL_ForceHandshake(sslSocket);
if (secStatus != SECSuccess) {
errWarn("SSL_ForceHandshake");
return secStatus;
}
#endif

secStatus = SSL_ResetHandshake(sslSocket, /* asServer */ PR_FALSE);
if (secStatus != SECSuccess) {
errWarn("SSL_ResetHandshake");
prStatus = PR_Close(sslSocket);
if (prStatus != PR_SUCCESS) {
errWarn("PR_Close");
}
return secStatus;
}

secStatus = handle_connection(sslSocket, connection);
if (secStatus != SECSuccess) {
errWarn("handle_connection");
return secStatus;
}

PR_Close(sslSocket);
return SECSuccess;
}

void
client_main(unsigned short port,
int connections,
const char * hostName)
{
int i;
SECStatus secStatus;
PRStatus prStatus;
PRInt32 rv;
PRNetAddr addr;
PRHostEnt hostEntry;
char buffer[256];

/* Setup network connection. */
prStatus = PR_GetHostByName(hostName, buffer, 256, &hostEntry);
if (prStatus != PR_SUCCESS) {
exitErr("PR_GetHostByName");
}

rv = PR_EnumerateHostEnt(0, &hostEntry, port, &addr);
if (rv < 0) {
exitErr("PR_EnumerateHostEnt");
}

secStatus = launch_thread(&threadMGR, do_connects, &addr, 1);
if (secStatus != SECSuccess) {
exitErr("launch_thread");
}

if (connections > 1) {
/* wait for the first connection to terminate, then launch the rest. */
reap_threads(&threadMGR);
/* Start up the connections */
for (i = 2; i <= connections; ++i) {
secStatus = launch_thread(&threadMGR, do_connects, &addr, i);
if (secStatus != SECSuccess) {
errWarn("launch_thread");
}
}
}

reap_threads(&threadMGR);
destroy_thread_data(&threadMGR);
}

int
main(int argc, char **argv)
{
char * certDir = ".";
char * progName = NULL;
int connections = 1;
char * cipherString = NULL;
SECStatus secStatus;
PLOptState * optstate;
PLOptStatus status;

/* Call the NSPR initialization routines */
PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);

progName = PL_strdup(argv[0]);

hostName = NULL;
optstate = PL_CreateOptState(argc, argv, "C:c:d:n:p:w:");
while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
switch(optstate->option) {
case 'C' : cipherString = PL_strdup(optstate->value); break;
case 'c' : connections = PORT_Atoi(optstate->value); break;
case 'd' : certDir = PL_strdup(optstate->value); break;
case 'n' : certNickname = PL_strdup(optstate->value); break;
case 'p' : port = PORT_Atoi(optstate->value); break;
case 'w' : password = PL_strdup(optstate->value); break;
case '\0': hostName = PL_strdup(optstate->value); break;
default : Usage(progName);
}
}

if (port == 0 || hostName == NULL)
Usage(progName);

if (certDir == NULL) {
certDir = PR_smprintf("%s/.netscape", getenv("HOME"));
}

/* Set our password function callback. */
PK11_SetPasswordFunc(myPasswd);

/* Initialize the NSS libraries. */
secStatus = NSS_Init(certDir);
if (secStatus != SECSuccess) {
exitErr("NSS_Init");
}

/* All cipher suites except RSA_NULL_MD5 are enabled by Domestic Policy. */
NSS_SetDomesticPolicy();
SSL_CipherPrefSetDefault(SSL_RSA_WITH_NULL_MD5, PR_TRUE);

/* all the SSL2 and SSL3 cipher suites are enabled by default. */
if (cipherString) {
int ndx;

/* disable all the ciphers, then enable the ones we want. */
disableAllSSLCiphers();

while (0 != (ndx = *cipherString++)) {
int *cptr;
int cipher;

if (! isalpha(ndx))
Usage(progName);
cptr = islower(ndx) ? ssl3CipherSuites : ssl2CipherSuites;
for (ndx &= 0x1f; (cipher = *cptr++) != 0 && --ndx > 0; )
/* do nothing */;
if (cipher) {
SSL_CipherPrefSetDefault(cipher, PR_TRUE);
}
}
}

client_main(port, connections, hostName);

NSS_Shutdown();
PR_Cleanup();
return 0;
}


Julien Pierre

unread,
Apr 17, 2002, 7:45:02 PM4/17/02
to Nirave Kadakia
Nirave,

Nirave Kadakia wrote:
>
> > - Are you using the correct SSL options (eg. set as client, TLS protocol
> > enabled, ciphers set) ? If you bypass the import step and connect to an
> > SSL server directly, do you get the error ?
> >
>

> I just tried bypassing the import step and received an assertion failure
> that a file descripter was NULL.

Sorry I wasn't clear.
I meant that you create an SSL socket before you connect or do any I/O,
do the SSL_ImportFD, and connect directly to an SSL server.

> > - Did you do SSL_ResetHandshake on your socket after the import ? If
> > not, you must do that in order for SSL to work after importing a non-SSL
> > socket to SSL.
> >
>
> Yes, I did. I ran the function after Importing and setting all my
> options.

OK, that's correct.
I tried your code. There doesn't appear to be anything wrong with it. I
don't have any server that does STARTTLS in the way you want to test it
against. I just slightly modified your program to not do the PR_Write
after your insecure PR_Connect . So the SSL import was done after the
connect. Everything worked just fine. Perhaps your problem is with the
server side, and the Bad MAC error is indicative of that ? Does that
server work with any other client ?

Thomas Kwan

unread,
Apr 17, 2002, 8:48:18 PM4/17/02
to jpi...@netscape.com, nir...@iplanet.com
Normally, you get "incorrect Message Authentication Code" error if
you have a certificate in the client's cert7.db that is conflict with the
certificates that are just sent from the server. For example, you may
have a CA certificate that has the same subject name but different
key material than the CA certificate that is returned from the server.
Are you using your own test CA, or multiple CA with the same
subject name?

Nirave Kadakia

unread,
Apr 17, 2002, 8:42:24 PM4/17/02
to Julien Pierre
You're right.  Thanks!

As it turns out, this client program did not work with the local server I was working on.  It did, however, work with the company wide mail server using the certificates generated by the Netscape browser.  What confused me was that my local mail server worked with Mozilla 0.99.  

My guess is that the certificates I generated were the problem, or that I placed them incorrectly on my local server.

My apologies for taking your time.  

-Nirave
0 new messages