зеркало из https://github.com/mozilla/gecko-dev.git
822 строки
22 KiB
C
822 строки
22 KiB
C
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* 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 the Initial Developer are Copyright (C) 1994-2000
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
/****************************************************************************
|
|
* SSL server program listens on a port, accepts client connection, reads *
|
|
* request and responds to it *
|
|
****************************************************************************/
|
|
|
|
/* Generic header files */
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
/* NSPR header files */
|
|
|
|
#include "nspr.h"
|
|
#include "plgetopt.h"
|
|
#include "prerror.h"
|
|
#include "prnetdb.h"
|
|
|
|
/* NSS header files */
|
|
|
|
#include "pk11func.h"
|
|
#include "secitem.h"
|
|
#include "ssl.h"
|
|
#include "certt.h"
|
|
#include "nss.h"
|
|
#include "secder.h"
|
|
#include "key.h"
|
|
#include "sslproto.h"
|
|
|
|
/* Custom header files */
|
|
|
|
#include "sslsample.h"
|
|
|
|
#ifndef PORT_Sprintf
|
|
#define PORT_Sprintf sprintf
|
|
#endif
|
|
|
|
#define REQUEST_CERT_ONCE 1
|
|
#define REQUIRE_CERT_ONCE 2
|
|
#define REQUEST_CERT_ALL 3
|
|
#define REQUIRE_CERT_ALL 4
|
|
|
|
/* Global variables */
|
|
GlobalThreadMgr threadMGR;
|
|
char *password = NULL;
|
|
CERTCertificate *cert = NULL;
|
|
SECKEYPrivateKey *privKey = NULL;
|
|
int stopping;
|
|
|
|
static void
|
|
Usage(const char *progName)
|
|
{
|
|
fprintf(stderr,
|
|
|
|
"Usage: %s -n rsa_nickname -p port [-3RFrf] [-w password]\n"
|
|
" [-c ciphers] [-d dbdir] \n"
|
|
"-3 means disable SSL v3\n"
|
|
"-r means request certificate on first handshake.\n"
|
|
"-f means require certificate on first handshake.\n"
|
|
"-R means request certificate on all handshakes.\n"
|
|
"-F means require certificate on all handshakes.\n"
|
|
"-c ciphers Letter(s) chosen from the following list\n"
|
|
"A SSL2 RC4 128 WITH MD5\n"
|
|
"B SSL2 RC4 128 EXPORT40 WITH MD5\n"
|
|
"C SSL2 RC2 128 CBC WITH MD5\n"
|
|
"D SSL2 RC2 128 CBC EXPORT40 WITH MD5\n"
|
|
"E SSL2 DES 64 CBC WITH MD5\n"
|
|
"F SSL2 DES 192 EDE3 CBC WITH MD5\n"
|
|
"\n"
|
|
"c SSL3 RSA WITH RC4 128 MD5\n"
|
|
"d SSL3 RSA WITH 3DES EDE CBC SHA\n"
|
|
"e SSL3 RSA WITH DES CBC SHA\n"
|
|
"f SSL3 RSA EXPORT WITH RC4 40 MD5\n"
|
|
"g SSL3 RSA EXPORT WITH RC2 CBC 40 MD5\n"
|
|
"i SSL3 RSA WITH NULL MD5\n"
|
|
"j SSL3 RSA FIPS WITH 3DES EDE CBC SHA\n"
|
|
"k SSL3 RSA FIPS WITH DES CBC SHA\n"
|
|
"l SSL3 RSA EXPORT WITH DES CBC SHA\t(new)\n"
|
|
"m SSL3 RSA EXPORT WITH RC4 56 SHA\t(new)\n",
|
|
progName);
|
|
exit(1);
|
|
}
|
|
|
|
/* Function: readDataFromSocket()
|
|
*
|
|
* Purpose: Parse an HTTP request by reading data from a GET or POST.
|
|
*
|
|
*/
|
|
SECStatus
|
|
readDataFromSocket(PRFileDesc *sslSocket, DataBuffer *buffer, char **fileName)
|
|
{
|
|
char *post;
|
|
int numBytes = 0;
|
|
int newln = 0; /* # of consecutive newlns */
|
|
|
|
/* Read data while it comes in from the socket. */
|
|
while (PR_TRUE) {
|
|
buffer->index = 0;
|
|
newln = 0;
|
|
|
|
/* Read the buffer. */
|
|
numBytes = PR_Read(sslSocket, &buffer->data[buffer->index],
|
|
buffer->remaining);
|
|
if (numBytes <= 0) {
|
|
errWarn("PR_Read");
|
|
return SECFailure;
|
|
}
|
|
buffer->dataEnd = buffer->dataStart + numBytes;
|
|
|
|
/* Parse the input, starting at the beginning of the buffer.
|
|
* Stop when we detect two consecutive \n's (or \r\n's)
|
|
* as this signifies the end of the GET or POST portion.
|
|
* The posted data follows.
|
|
*/
|
|
while (buffer->index < buffer->dataEnd && newln < 2) {
|
|
int octet = buffer->data[buffer->index++];
|
|
if (octet == '\n') {
|
|
newln++;
|
|
} else if (octet != '\r') {
|
|
newln = 0;
|
|
}
|
|
}
|
|
|
|
/* Came to the end of the buffer, or second newline.
|
|
* If we didn't get an empty line ("\r\n\r\n"), then keep on reading.
|
|
*/
|
|
if (newln < 2)
|
|
continue;
|
|
|
|
/* we're at the end of the HTTP request.
|
|
* If the request is a POST, then there will be one more
|
|
* line of data.
|
|
* This parsing is a hack, but ok for SSL test purposes.
|
|
*/
|
|
post = PORT_Strstr(buffer->data, "POST ");
|
|
if (!post || *post != 'P')
|
|
break;
|
|
|
|
/* It's a post, so look for the next and final CR/LF. */
|
|
/* We should parse content length here, but ... */
|
|
while (buffer->index < buffer->dataEnd && newln < 3) {
|
|
int octet = buffer->data[buffer->index++];
|
|
if (octet == '\n') {
|
|
newln++;
|
|
}
|
|
}
|
|
|
|
if (newln == 3)
|
|
break;
|
|
}
|
|
|
|
/* Have either (a) a complete get, (b) a complete post, (c) EOF */
|
|
|
|
/* Execute a "GET " operation. */
|
|
if (buffer->index > 0 && PORT_Strncmp(buffer->data, "GET ", 4) == 0) {
|
|
int fnLength;
|
|
|
|
/* File name is the part after "GET ". */
|
|
fnLength = strcspn(buffer->data + 5, " \r\n");
|
|
*fileName = (char *)PORT_Alloc(fnLength + 1);
|
|
PORT_Strncpy(*fileName, buffer->data + 5, fnLength);
|
|
(*fileName)[fnLength] = '\0';
|
|
}
|
|
|
|
return SECSuccess;
|
|
}
|
|
|
|
/* Function: authenticateSocket()
|
|
*
|
|
* Purpose: Configure a socket for SSL.
|
|
*
|
|
*
|
|
*/
|
|
PRFileDesc *
|
|
setupSSLSocket(PRFileDesc *tcpSocket, int requestCert)
|
|
{
|
|
PRFileDesc *sslSocket;
|
|
SSLKEAType certKEA;
|
|
int certErr = 0;
|
|
SECStatus secStatus;
|
|
|
|
/* Set the appropriate flags. */
|
|
|
|
sslSocket = SSL_ImportFD(NULL, tcpSocket);
|
|
if (sslSocket == NULL) {
|
|
errWarn("SSL_ImportFD");
|
|
goto loser;
|
|
}
|
|
|
|
secStatus = SSL_OptionSet(sslSocket, SSL_SECURITY, PR_TRUE);
|
|
if (secStatus != SECSuccess) {
|
|
errWarn("SSL_OptionSet SSL_SECURITY");
|
|
goto loser;
|
|
}
|
|
|
|
secStatus = SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_SERVER, PR_TRUE);
|
|
if (secStatus != SECSuccess) {
|
|
errWarn("SSL_OptionSet:SSL_HANDSHAKE_AS_SERVER");
|
|
goto loser;
|
|
}
|
|
|
|
secStatus = SSL_OptionSet(sslSocket, SSL_REQUEST_CERTIFICATE,
|
|
(requestCert >= REQUEST_CERT_ONCE));
|
|
if (secStatus != SECSuccess) {
|
|
errWarn("SSL_OptionSet:SSL_REQUEST_CERTIFICATE");
|
|
goto loser;
|
|
}
|
|
|
|
secStatus = SSL_OptionSet(sslSocket, SSL_REQUIRE_CERTIFICATE,
|
|
(requestCert == REQUIRE_CERT_ONCE));
|
|
if (secStatus != SECSuccess) {
|
|
errWarn("SSL_OptionSet:SSL_REQUIRE_CERTIFICATE");
|
|
goto loser;
|
|
}
|
|
|
|
/* Set the appropriate callback routines. */
|
|
|
|
secStatus = SSL_AuthCertificateHook(sslSocket, myAuthCertificate,
|
|
CERT_GetDefaultCertDB());
|
|
if (secStatus != SECSuccess) {
|
|
errWarn("SSL_AuthCertificateHook");
|
|
goto loser;
|
|
}
|
|
|
|
secStatus = SSL_BadCertHook(sslSocket,
|
|
(SSLBadCertHandler)myBadCertHandler, &certErr);
|
|
if (secStatus != SECSuccess) {
|
|
errWarn("SSL_BadCertHook");
|
|
goto loser;
|
|
}
|
|
|
|
secStatus = SSL_HandshakeCallback(sslSocket,
|
|
(SSLHandshakeCallback)myHandshakeCallback,
|
|
NULL);
|
|
if (secStatus != SECSuccess) {
|
|
errWarn("SSL_HandshakeCallback");
|
|
goto loser;
|
|
}
|
|
|
|
secStatus = SSL_SetPKCS11PinArg(sslSocket, password);
|
|
if (secStatus != SECSuccess) {
|
|
errWarn("SSL_HandshakeCallback");
|
|
goto loser;
|
|
}
|
|
|
|
certKEA = NSS_FindCertKEAType(cert);
|
|
|
|
secStatus = SSL_ConfigSecureServer(sslSocket, cert, privKey, certKEA);
|
|
if (secStatus != SECSuccess) {
|
|
errWarn("SSL_ConfigSecureServer");
|
|
goto loser;
|
|
}
|
|
|
|
return sslSocket;
|
|
|
|
loser:
|
|
|
|
PR_Close(tcpSocket);
|
|
return NULL;
|
|
}
|
|
|
|
/* Function: authenticateSocket()
|
|
*
|
|
* Purpose: Perform client authentication on the socket.
|
|
*
|
|
*/
|
|
SECStatus
|
|
authenticateSocket(PRFileDesc *sslSocket, PRBool requireCert)
|
|
{
|
|
CERTCertificate *cert;
|
|
SECStatus secStatus;
|
|
|
|
/* Returns NULL if client authentication is not enabled or if the
|
|
* client had no certificate. */
|
|
cert = SSL_PeerCertificate(sslSocket);
|
|
if (cert) {
|
|
/* Client had a certificate, so authentication is through. */
|
|
CERT_DestroyCertificate(cert);
|
|
return SECSuccess;
|
|
}
|
|
|
|
/* Request client to authenticate itself. */
|
|
secStatus = SSL_OptionSet(sslSocket, SSL_REQUEST_CERTIFICATE, PR_TRUE);
|
|
if (secStatus != SECSuccess) {
|
|
errWarn("SSL_OptionSet:SSL_REQUEST_CERTIFICATE");
|
|
return SECFailure;
|
|
}
|
|
|
|
/* If desired, require client to authenticate itself. Note
|
|
* SSL_REQUEST_CERTIFICATE must also be on, as above. */
|
|
secStatus = SSL_OptionSet(sslSocket, SSL_REQUIRE_CERTIFICATE, requireCert);
|
|
if (secStatus != SECSuccess) {
|
|
errWarn("SSL_OptionSet:SSL_REQUIRE_CERTIFICATE");
|
|
return SECFailure;
|
|
}
|
|
|
|
/* Having changed socket configuration parameters, redo handshake. */
|
|
secStatus = SSL_ReHandshake(sslSocket, PR_TRUE);
|
|
if (secStatus != SECSuccess) {
|
|
errWarn("SSL_ReHandshake");
|
|
return SECFailure;
|
|
}
|
|
|
|
/* Force the handshake to complete before moving on. */
|
|
secStatus = SSL_ForceHandshake(sslSocket);
|
|
if (secStatus != SECSuccess) {
|
|
errWarn("SSL_ForceHandshake");
|
|
return SECFailure;
|
|
}
|
|
|
|
return SECSuccess;
|
|
}
|
|
|
|
/* Function: writeDataToSocket
|
|
*
|
|
* Purpose: Write the client's request back to the socket. If the client
|
|
* requested a file, dump it to the socket.
|
|
*
|
|
*/
|
|
SECStatus
|
|
writeDataToSocket(PRFileDesc *sslSocket, DataBuffer *buffer, char *fileName)
|
|
{
|
|
int headerLength;
|
|
int numBytes;
|
|
char messageBuffer[120];
|
|
PRFileDesc *local_file_fd = NULL;
|
|
char header[] = "<html><body><h1>Sample SSL server</h1><br><br>";
|
|
char filehd[] = "<h2>The file you requested:</h2><br>";
|
|
char reqhd[] = "<h2>This is your request:</h2><br>";
|
|
char link[] = "Try getting a <a HREF=\"../testfile\">file</a><br>";
|
|
char footer[] = "<br><h2>End of request.</h2><br></body></html>";
|
|
|
|
headerLength = PORT_Strlen(defaultHeader);
|
|
|
|
/* Write a header to the socket. */
|
|
numBytes = PR_Write(sslSocket, header, PORT_Strlen(header));
|
|
if (numBytes < 0) {
|
|
errWarn("PR_Write");
|
|
goto loser;
|
|
}
|
|
|
|
if (fileName) {
|
|
PRFileInfo info;
|
|
PRStatus prStatus;
|
|
|
|
/* Try to open the local file named.
|
|
* If successful, then write it to the client.
|
|
*/
|
|
prStatus = PR_GetFileInfo(fileName, &info);
|
|
if (prStatus != PR_SUCCESS ||
|
|
info.type != PR_FILE_FILE ||
|
|
info.size < 0) {
|
|
PORT_Free(fileName);
|
|
/* Maybe a GET not sent from client.c? */
|
|
goto writerequest;
|
|
}
|
|
|
|
local_file_fd = PR_Open(fileName, PR_RDONLY, 0);
|
|
if (local_file_fd == NULL) {
|
|
PORT_Free(fileName);
|
|
goto writerequest;
|
|
}
|
|
|
|
/* Write a header to the socket. */
|
|
numBytes = PR_Write(sslSocket, filehd, PORT_Strlen(filehd));
|
|
if (numBytes < 0) {
|
|
errWarn("PR_Write");
|
|
goto loser;
|
|
}
|
|
|
|
/* Transmit the local file prepended by the default header
|
|
* across the socket.
|
|
*/
|
|
numBytes = PR_TransmitFile(sslSocket, local_file_fd,
|
|
defaultHeader, headerLength,
|
|
PR_TRANSMITFILE_KEEP_OPEN,
|
|
PR_INTERVAL_NO_TIMEOUT);
|
|
|
|
/* Error in transmission. */
|
|
if (numBytes < 0) {
|
|
errWarn("PR_TransmitFile");
|
|
/*
|
|
i = PORT_Strlen(errString);
|
|
PORT_Memcpy(buf, errString, i);
|
|
*/
|
|
/* Transmitted bytes successfully. */
|
|
} else {
|
|
numBytes -= headerLength;
|
|
fprintf(stderr, "PR_TransmitFile wrote %d bytes from %s\n",
|
|
numBytes, fileName);
|
|
}
|
|
|
|
PORT_Free(fileName);
|
|
PR_Close(local_file_fd);
|
|
}
|
|
|
|
writerequest:
|
|
|
|
/* Write a header to the socket. */
|
|
numBytes = PR_Write(sslSocket, reqhd, PORT_Strlen(reqhd));
|
|
if (numBytes < 0) {
|
|
errWarn("PR_Write");
|
|
goto loser;
|
|
}
|
|
|
|
/* Write the buffer data to the socket. */
|
|
if (buffer->index <= 0) {
|
|
/* Reached the EOF. Report incomplete transaction to socket. */
|
|
PORT_Sprintf(messageBuffer,
|
|
"GET or POST incomplete after %d bytes.\r\n",
|
|
buffer->dataEnd);
|
|
numBytes = PR_Write(sslSocket, messageBuffer,
|
|
PORT_Strlen(messageBuffer));
|
|
if (numBytes < 0) {
|
|
errWarn("PR_Write");
|
|
goto loser;
|
|
}
|
|
} else {
|
|
/* Display the buffer data. */
|
|
fwrite(buffer->data, 1, buffer->index, stdout);
|
|
/* Write the buffer data to the socket. */
|
|
numBytes = PR_Write(sslSocket, buffer->data, buffer->index);
|
|
if (numBytes < 0) {
|
|
errWarn("PR_Write");
|
|
goto loser;
|
|
}
|
|
/* Display security information for the socket. */
|
|
printSecurityInfo(sslSocket);
|
|
/* Write any discarded data out to the socket. */
|
|
if (buffer->index < buffer->dataEnd) {
|
|
PORT_Sprintf(buffer->data, "Discarded %d characters.\r\n",
|
|
buffer->dataEnd - buffer->index);
|
|
numBytes = PR_Write(sslSocket, buffer->data,
|
|
PORT_Strlen(buffer->data));
|
|
if (numBytes < 0) {
|
|
errWarn("PR_Write");
|
|
goto loser;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Write a footer to the socket. */
|
|
numBytes = PR_Write(sslSocket, footer, PORT_Strlen(footer));
|
|
if (numBytes < 0) {
|
|
errWarn("PR_Write");
|
|
goto loser;
|
|
}
|
|
|
|
/* Write a link to the socket. */
|
|
numBytes = PR_Write(sslSocket, link, PORT_Strlen(link));
|
|
if (numBytes < 0) {
|
|
errWarn("PR_Write");
|
|
goto loser;
|
|
}
|
|
|
|
/* Complete the HTTP transaction. */
|
|
numBytes = PR_Write(sslSocket, "EOF\r\n\r\n\r\n", 9);
|
|
if (numBytes < 0) {
|
|
errWarn("PR_Write");
|
|
goto loser;
|
|
}
|
|
|
|
/* Do a nice shutdown if asked. */
|
|
if (!strncmp(buffer->data, stopCmd, strlen(stopCmd))) {
|
|
stopping = 1;
|
|
}
|
|
return SECSuccess;
|
|
|
|
loser:
|
|
|
|
/* Do a nice shutdown if asked. */
|
|
if (!strncmp(buffer->data, stopCmd, strlen(stopCmd))) {
|
|
stopping = 1;
|
|
}
|
|
return SECFailure;
|
|
}
|
|
|
|
/* Function: int handle_connection()
|
|
*
|
|
* Purpose: Thread to handle a connection to a socket.
|
|
*
|
|
*/
|
|
SECStatus
|
|
handle_connection(void *tcp_sock, int requestCert)
|
|
{
|
|
PRFileDesc * tcpSocket = (PRFileDesc *)tcp_sock;
|
|
PRFileDesc * sslSocket = NULL;
|
|
SECStatus secStatus = SECFailure;
|
|
PRStatus prStatus;
|
|
PRSocketOptionData socketOption;
|
|
DataBuffer buffer;
|
|
char * fileName = NULL;
|
|
|
|
/* Initialize the data buffer. */
|
|
memset(buffer.data, 0, BUFFER_SIZE);
|
|
buffer.remaining = BUFFER_SIZE;
|
|
buffer.index = 0;
|
|
buffer.dataStart = 0;
|
|
buffer.dataEnd = 0;
|
|
|
|
/* Make sure the socket is blocking. */
|
|
socketOption.option = PR_SockOpt_Nonblocking;
|
|
socketOption.value.non_blocking = PR_FALSE;
|
|
PR_SetSocketOption(tcpSocket, &socketOption);
|
|
|
|
sslSocket = setupSSLSocket(tcpSocket, requestCert);
|
|
if (sslSocket == NULL) {
|
|
errWarn("setupSSLSocket");
|
|
goto cleanup;
|
|
}
|
|
|
|
secStatus = SSL_ResetHandshake(sslSocket, /* asServer */ PR_TRUE);
|
|
if (secStatus != SECSuccess) {
|
|
errWarn("SSL_ResetHandshake");
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Read data from the socket, parse it for HTTP content.
|
|
* If the user is requesting/requiring authentication, authenticate
|
|
* the socket. Then write the result back to the socket. */
|
|
fprintf(stdout, "\nReading data from socket...\n\n");
|
|
secStatus = readDataFromSocket(sslSocket, &buffer, &fileName);
|
|
if (secStatus != SECSuccess) {
|
|
goto cleanup;
|
|
}
|
|
if (requestCert >= REQUEST_CERT_ALL) {
|
|
fprintf(stdout, "\nAuthentication requested.\n\n");
|
|
secStatus = authenticateSocket(sslSocket,
|
|
(requestCert == REQUIRE_CERT_ALL));
|
|
if (secStatus != SECSuccess) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
fprintf(stdout, "\nWriting data to socket...\n\n");
|
|
secStatus = writeDataToSocket(sslSocket, &buffer, fileName);
|
|
|
|
cleanup:
|
|
|
|
/* Close down the socket. */
|
|
prStatus = PR_Close(tcpSocket);
|
|
if (prStatus != PR_SUCCESS) {
|
|
errWarn("PR_Close");
|
|
}
|
|
|
|
return secStatus;
|
|
}
|
|
|
|
/* Function: int accept_connection()
|
|
*
|
|
* Purpose: Thread to accept a connection to the socket.
|
|
*
|
|
*/
|
|
SECStatus
|
|
accept_connection(void *listener, int requestCert)
|
|
{
|
|
PRFileDesc *listenSocket = (PRFileDesc*)listener;
|
|
PRNetAddr addr;
|
|
PRStatus prStatus;
|
|
|
|
/* XXX need an SSL socket here? */
|
|
while (!stopping) {
|
|
PRFileDesc *tcpSocket;
|
|
SECStatus result;
|
|
|
|
fprintf(stderr, "\n\n\nAbout to call accept.\n");
|
|
|
|
/* Accept a connection to the socket. */
|
|
tcpSocket = PR_Accept(listenSocket, &addr, PR_INTERVAL_NO_TIMEOUT);
|
|
if (tcpSocket == NULL) {
|
|
errWarn("PR_Accept");
|
|
break;
|
|
}
|
|
|
|
/* Accepted the connection, now handle it. */
|
|
result = launch_thread(&threadMGR, handle_connection,
|
|
tcpSocket, requestCert);
|
|
|
|
if (result != SECSuccess) {
|
|
prStatus = PR_Close(tcpSocket);
|
|
if (prStatus != PR_SUCCESS) {
|
|
exitErr("PR_Close");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
fprintf(stderr, "Closing listen socket.\n");
|
|
|
|
prStatus = PR_Close(listenSocket);
|
|
if (prStatus != PR_SUCCESS) {
|
|
exitErr("PR_Close");
|
|
}
|
|
return SECSuccess;
|
|
}
|
|
|
|
/* Function: void server_main()
|
|
*
|
|
* Purpose: This is the server's main function. It configures a socket
|
|
* and listens to it.
|
|
*
|
|
*/
|
|
void
|
|
server_main(
|
|
unsigned short port,
|
|
int requestCert,
|
|
SECKEYPrivateKey * privKey,
|
|
CERTCertificate * cert,
|
|
PRBool disableSSL3)
|
|
{
|
|
SECStatus secStatus;
|
|
PRStatus prStatus;
|
|
PRFileDesc * listenSocket;
|
|
PRNetAddr addr;
|
|
PRSocketOptionData socketOption;
|
|
|
|
/* Create a new socket. */
|
|
listenSocket = PR_NewTCPSocket();
|
|
if (listenSocket == NULL) {
|
|
exitErr("PR_NewTCPSocket");
|
|
}
|
|
|
|
/* Set socket to be blocking -
|
|
* on some platforms the default is nonblocking.
|
|
*/
|
|
socketOption.option = PR_SockOpt_Nonblocking;
|
|
socketOption.value.non_blocking = PR_FALSE;
|
|
|
|
prStatus = PR_SetSocketOption(listenSocket, &socketOption);
|
|
if (prStatus != PR_SUCCESS) {
|
|
exitErr("PR_SetSocketOption");
|
|
}
|
|
|
|
/* This cipher is not on by default. The Acceptance test
|
|
* would like it to be. Turn this cipher on.
|
|
*/
|
|
secStatus = SSL_CipherPrefSetDefault(SSL_RSA_WITH_NULL_MD5, PR_TRUE);
|
|
if (secStatus != SECSuccess) {
|
|
exitErr("SSL_CipherPrefSetDefault:SSL_RSA_WITH_NULL_MD5");
|
|
}
|
|
|
|
/* Configure the network connection. */
|
|
addr.inet.family = PR_AF_INET;
|
|
addr.inet.ip = PR_INADDR_ANY;
|
|
addr.inet.port = PR_htons(port);
|
|
|
|
/* Bind the address to the listener socket. */
|
|
prStatus = PR_Bind(listenSocket, &addr);
|
|
if (prStatus != PR_SUCCESS) {
|
|
exitErr("PR_Bind");
|
|
}
|
|
|
|
/* Listen for connection on the socket. The second argument is
|
|
* the maximum size of the queue for pending connections.
|
|
*/
|
|
prStatus = PR_Listen(listenSocket, 5);
|
|
if (prStatus != PR_SUCCESS) {
|
|
exitErr("PR_Listen");
|
|
}
|
|
|
|
/* Launch thread to handle connections to the socket. */
|
|
secStatus = launch_thread(&threadMGR, accept_connection,
|
|
listenSocket, requestCert);
|
|
if (secStatus != SECSuccess) {
|
|
PR_Close(listenSocket);
|
|
} else {
|
|
reap_threads(&threadMGR);
|
|
destroy_thread_data(&threadMGR);
|
|
}
|
|
}
|
|
|
|
/* Function: int main()
|
|
*
|
|
* Purpose: Parses command arguments and configures SSL server.
|
|
*
|
|
*/
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
char * progName = NULL;
|
|
char * nickName = NULL;
|
|
char * cipherString = NULL;
|
|
char * dir = ".";
|
|
int requestCert = 0;
|
|
unsigned short port = 0;
|
|
SECStatus secStatus;
|
|
PRBool disableSSL3 = PR_FALSE;
|
|
PLOptState * optstate;
|
|
PLOptStatus status;
|
|
|
|
/* Zero out the thread manager. */
|
|
PORT_Memset(&threadMGR, 0, sizeof(threadMGR));
|
|
|
|
progName = PL_strdup(argv[0]);
|
|
|
|
optstate = PL_CreateOptState(argc, argv, "3FRc:d:fp:n:rw:");
|
|
while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
|
|
switch(optstate->option) {
|
|
case '3': disableSSL3 = PR_TRUE; break;
|
|
case 'F': requestCert = REQUIRE_CERT_ALL; break;
|
|
case 'R': requestCert = REQUEST_CERT_ALL; break;
|
|
case 'c': cipherString = PL_strdup(optstate->value); break;
|
|
case 'd': dir = PL_strdup(optstate->value); break;
|
|
case 'f': requestCert = REQUIRE_CERT_ONCE; break;
|
|
case 'n': nickName = PL_strdup(optstate->value); break;
|
|
case 'p': port = PORT_Atoi(optstate->value); break;
|
|
case 'r': requestCert = REQUEST_CERT_ONCE; break;
|
|
case 'w': password = PL_strdup(optstate->value); break;
|
|
default:
|
|
case '?': Usage(progName);
|
|
}
|
|
}
|
|
|
|
if (nickName == NULL || port == 0)
|
|
Usage(progName);
|
|
|
|
/* Call the NSPR initialization routines. */
|
|
PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
|
|
|
|
/* Set the cert database password callback. */
|
|
PK11_SetPasswordFunc(myPasswd);
|
|
|
|
/* Initialize NSS. */
|
|
secStatus = NSS_Init(dir);
|
|
if (secStatus != SECSuccess) {
|
|
exitErr("NSS_Init");
|
|
}
|
|
|
|
/* Set the policy for this server (REQUIRED - no default). */
|
|
secStatus = NSS_SetDomesticPolicy();
|
|
if (secStatus != SECSuccess) {
|
|
exitErr("NSS_SetDomesticPolicy");
|
|
}
|
|
|
|
/* XXX keep this? */
|
|
/* 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) {
|
|
SECStatus status;
|
|
status = SSL_CipherPrefSetDefault(cipher, PR_TRUE);
|
|
if (status != SECSuccess)
|
|
errWarn("SSL_CipherPrefSetDefault()");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Get own certificate and private key. */
|
|
cert = PK11_FindCertFromNickname(nickName, password);
|
|
if (cert == NULL) {
|
|
exitErr("PK11_FindCertFromNickname");
|
|
}
|
|
|
|
privKey = PK11_FindKeyByAnyCert(cert, password);
|
|
if (privKey == NULL) {
|
|
exitErr("PK11_FindKeyByAnyCert");
|
|
}
|
|
|
|
/* Configure the server's cache for a multi-process application
|
|
* using default timeout values (24 hrs) and directory location (/tmp).
|
|
*/
|
|
SSL_ConfigMPServerSIDCache(256, 0, 0, NULL);
|
|
|
|
/* Launch server. */
|
|
server_main(port, requestCert, privKey, cert, disableSSL3);
|
|
|
|
/* Shutdown NSS and exit NSPR gracefully. */
|
|
if (NSS_Shutdown() != SECSuccess) {
|
|
exit(1);
|
|
}
|
|
PR_Cleanup();
|
|
return 0;
|
|
}
|