1
/* ***** BEGIN LICENSE BLOCK *****
2
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
4
* The contents of this file are subject to the Mozilla Public License Version
5
* 1.1 (the "License"); you may not use this file except in compliance with
6
* the License. You may obtain a copy of the License at
7
* http://www.mozilla.org/MPL/
9
* Software distributed under the License is distributed on an "AS IS" basis,
10
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11
* for the specific language governing rights and limitations under the
14
* The Original Code is the Netscape security libraries.
16
* The Initial Developer of the Original Code is
17
* Netscape Communications Corporation.
18
* Portions created by the Initial Developer are Copyright (C) 1994-2000
19
* the Initial Developer. All Rights Reserved.
23
* Alternatively, the contents of this file may be used under the terms of
24
* either the GNU General Public License Version 2 or later (the "GPL"), or
25
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26
* in which case the provisions of the GPL or the LGPL are applicable instead
27
* of those above. If you wish to allow use of your version of this file only
28
* under the terms of either the GPL or the LGPL, and not to allow others to
29
* use your version of this file under the terms of the MPL, indicate your
30
* decision by deleting the provisions above and replace them with the notice
31
* and other provisions required by the GPL or the LGPL. If you do not delete
32
* the provisions above, a recipient may use your version of this file under
33
* the terms of any one of the MPL, the GPL or the LGPL.
35
* ***** END LICENSE BLOCK ***** */
37
/****************************************************************************
38
* SSL server program listens on a port, accepts client connection, reads *
39
* request and responds to it *
40
****************************************************************************/
42
/* Generic header files */
47
/* NSPR header files */
54
/* NSS header files */
65
/* Custom header files */
67
#include "sslsample.h"
70
#define PORT_Sprintf sprintf
73
#define REQUEST_CERT_ONCE 1
74
#define REQUIRE_CERT_ONCE 2
75
#define REQUEST_CERT_ALL 3
76
#define REQUIRE_CERT_ALL 4
78
/* Global variables */
79
GlobalThreadMgr threadMGR;
80
char *password = NULL;
81
CERTCertificate *cert = NULL;
82
SECKEYPrivateKey *privKey = NULL;
86
Usage(const char *progName)
90
"Usage: %s -n rsa_nickname -p port [-3RFrf] [-w password]\n"
91
" [-c ciphers] [-d dbdir] \n"
92
"-3 means disable SSL v3\n"
93
"-r means request certificate on first handshake.\n"
94
"-f means require certificate on first handshake.\n"
95
"-R means request certificate on all handshakes.\n"
96
"-F means require certificate on all handshakes.\n"
97
"-c ciphers Letter(s) chosen from the following list\n"
98
"A SSL2 RC4 128 WITH MD5\n"
99
"B SSL2 RC4 128 EXPORT40 WITH MD5\n"
100
"C SSL2 RC2 128 CBC WITH MD5\n"
101
"D SSL2 RC2 128 CBC EXPORT40 WITH MD5\n"
102
"E SSL2 DES 64 CBC WITH MD5\n"
103
"F SSL2 DES 192 EDE3 CBC WITH MD5\n"
105
"c SSL3 RSA WITH RC4 128 MD5\n"
106
"d SSL3 RSA WITH 3DES EDE CBC SHA\n"
107
"e SSL3 RSA WITH DES CBC SHA\n"
108
"f SSL3 RSA EXPORT WITH RC4 40 MD5\n"
109
"g SSL3 RSA EXPORT WITH RC2 CBC 40 MD5\n"
110
"i SSL3 RSA WITH NULL MD5\n"
111
"j SSL3 RSA FIPS WITH 3DES EDE CBC SHA\n"
112
"k SSL3 RSA FIPS WITH DES CBC SHA\n"
113
"l SSL3 RSA EXPORT WITH DES CBC SHA\t(new)\n"
114
"m SSL3 RSA EXPORT WITH RC4 56 SHA\t(new)\n",
119
/* Function: readDataFromSocket()
121
* Purpose: Parse an HTTP request by reading data from a GET or POST.
125
readDataFromSocket(PRFileDesc *sslSocket, DataBuffer *buffer, char **fileName)
129
int newln = 0; /* # of consecutive newlns */
131
/* Read data while it comes in from the socket. */
136
/* Read the buffer. */
137
numBytes = PR_Read(sslSocket, &buffer->data[buffer->index],
143
buffer->dataEnd = buffer->dataStart + numBytes;
145
/* Parse the input, starting at the beginning of the buffer.
146
* Stop when we detect two consecutive \n's (or \r\n's)
147
* as this signifies the end of the GET or POST portion.
148
* The posted data follows.
150
while (buffer->index < buffer->dataEnd && newln < 2) {
151
int octet = buffer->data[buffer->index++];
154
} else if (octet != '\r') {
159
/* Came to the end of the buffer, or second newline.
160
* If we didn't get an empty line ("\r\n\r\n"), then keep on reading.
165
/* we're at the end of the HTTP request.
166
* If the request is a POST, then there will be one more
168
* This parsing is a hack, but ok for SSL test purposes.
170
post = PORT_Strstr(buffer->data, "POST ");
171
if (!post || *post != 'P')
174
/* It's a post, so look for the next and final CR/LF. */
175
/* We should parse content length here, but ... */
176
while (buffer->index < buffer->dataEnd && newln < 3) {
177
int octet = buffer->data[buffer->index++];
187
/* Have either (a) a complete get, (b) a complete post, (c) EOF */
189
/* Execute a "GET " operation. */
190
if (buffer->index > 0 && PORT_Strncmp(buffer->data, "GET ", 4) == 0) {
193
/* File name is the part after "GET ". */
194
fnLength = strcspn(buffer->data + 5, " \r\n");
195
*fileName = (char *)PORT_Alloc(fnLength + 1);
196
PORT_Strncpy(*fileName, buffer->data + 5, fnLength);
197
(*fileName)[fnLength] = '\0';
203
/* Function: authenticateSocket()
205
* Purpose: Configure a socket for SSL.
210
setupSSLSocket(PRFileDesc *tcpSocket, int requestCert)
212
PRFileDesc *sslSocket;
217
/* Set the appropriate flags. */
219
sslSocket = SSL_ImportFD(NULL, tcpSocket);
220
if (sslSocket == NULL) {
221
errWarn("SSL_ImportFD");
225
secStatus = SSL_OptionSet(sslSocket, SSL_SECURITY, PR_TRUE);
226
if (secStatus != SECSuccess) {
227
errWarn("SSL_OptionSet SSL_SECURITY");
231
secStatus = SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_SERVER, PR_TRUE);
232
if (secStatus != SECSuccess) {
233
errWarn("SSL_OptionSet:SSL_HANDSHAKE_AS_SERVER");
237
secStatus = SSL_OptionSet(sslSocket, SSL_REQUEST_CERTIFICATE,
238
(requestCert >= REQUEST_CERT_ONCE));
239
if (secStatus != SECSuccess) {
240
errWarn("SSL_OptionSet:SSL_REQUEST_CERTIFICATE");
244
secStatus = SSL_OptionSet(sslSocket, SSL_REQUIRE_CERTIFICATE,
245
(requestCert == REQUIRE_CERT_ONCE));
246
if (secStatus != SECSuccess) {
247
errWarn("SSL_OptionSet:SSL_REQUIRE_CERTIFICATE");
251
/* Set the appropriate callback routines. */
253
secStatus = SSL_AuthCertificateHook(sslSocket, myAuthCertificate,
254
CERT_GetDefaultCertDB());
255
if (secStatus != SECSuccess) {
256
errWarn("SSL_AuthCertificateHook");
260
secStatus = SSL_BadCertHook(sslSocket,
261
(SSLBadCertHandler)myBadCertHandler, &certErr);
262
if (secStatus != SECSuccess) {
263
errWarn("SSL_BadCertHook");
267
secStatus = SSL_HandshakeCallback(sslSocket,
270
if (secStatus != SECSuccess) {
271
errWarn("SSL_HandshakeCallback");
275
secStatus = SSL_SetPKCS11PinArg(sslSocket, password);
276
if (secStatus != SECSuccess) {
277
errWarn("SSL_HandshakeCallback");
281
certKEA = NSS_FindCertKEAType(cert);
283
secStatus = SSL_ConfigSecureServer(sslSocket, cert, privKey, certKEA);
284
if (secStatus != SECSuccess) {
285
errWarn("SSL_ConfigSecureServer");
297
/* Function: authenticateSocket()
299
* Purpose: Perform client authentication on the socket.
303
authenticateSocket(PRFileDesc *sslSocket, PRBool requireCert)
305
CERTCertificate *cert;
308
/* Returns NULL if client authentication is not enabled or if the
309
* client had no certificate. */
310
cert = SSL_PeerCertificate(sslSocket);
312
/* Client had a certificate, so authentication is through. */
313
CERT_DestroyCertificate(cert);
317
/* Request client to authenticate itself. */
318
secStatus = SSL_OptionSet(sslSocket, SSL_REQUEST_CERTIFICATE, PR_TRUE);
319
if (secStatus != SECSuccess) {
320
errWarn("SSL_OptionSet:SSL_REQUEST_CERTIFICATE");
324
/* If desired, require client to authenticate itself. Note
325
* SSL_REQUEST_CERTIFICATE must also be on, as above. */
326
secStatus = SSL_OptionSet(sslSocket, SSL_REQUIRE_CERTIFICATE, requireCert);
327
if (secStatus != SECSuccess) {
328
errWarn("SSL_OptionSet:SSL_REQUIRE_CERTIFICATE");
332
/* Having changed socket configuration parameters, redo handshake. */
333
secStatus = SSL_ReHandshake(sslSocket, PR_TRUE);
334
if (secStatus != SECSuccess) {
335
errWarn("SSL_ReHandshake");
339
/* Force the handshake to complete before moving on. */
340
secStatus = SSL_ForceHandshake(sslSocket);
341
if (secStatus != SECSuccess) {
342
errWarn("SSL_ForceHandshake");
349
/* Function: writeDataToSocket
351
* Purpose: Write the client's request back to the socket. If the client
352
* requested a file, dump it to the socket.
356
writeDataToSocket(PRFileDesc *sslSocket, DataBuffer *buffer, char *fileName)
360
char messageBuffer[120];
361
PRFileDesc *local_file_fd = NULL;
362
char header[] = "<html><body><h1>Sample SSL server</h1><br><br>";
363
char filehd[] = "<h2>The file you requested:</h2><br>";
364
char reqhd[] = "<h2>This is your request:</h2><br>";
365
char link[] = "Try getting a <a HREF=\"../testfile\">file</a><br>";
366
char footer[] = "<br><h2>End of request.</h2><br></body></html>";
368
headerLength = PORT_Strlen(defaultHeader);
370
/* Write a header to the socket. */
371
numBytes = PR_Write(sslSocket, header, PORT_Strlen(header));
381
/* Try to open the local file named.
382
* If successful, then write it to the client.
384
prStatus = PR_GetFileInfo(fileName, &info);
385
if (prStatus != PR_SUCCESS ||
386
info.type != PR_FILE_FILE ||
389
/* Maybe a GET not sent from client.c? */
393
local_file_fd = PR_Open(fileName, PR_RDONLY, 0);
394
if (local_file_fd == NULL) {
399
/* Write a header to the socket. */
400
numBytes = PR_Write(sslSocket, filehd, PORT_Strlen(filehd));
406
/* Transmit the local file prepended by the default header
409
numBytes = PR_TransmitFile(sslSocket, local_file_fd,
410
defaultHeader, headerLength,
411
PR_TRANSMITFILE_KEEP_OPEN,
412
PR_INTERVAL_NO_TIMEOUT);
414
/* Error in transmission. */
416
errWarn("PR_TransmitFile");
418
i = PORT_Strlen(errString);
419
PORT_Memcpy(buf, errString, i);
421
/* Transmitted bytes successfully. */
423
numBytes -= headerLength;
424
fprintf(stderr, "PR_TransmitFile wrote %d bytes from %s\n",
429
PR_Close(local_file_fd);
434
/* Write a header to the socket. */
435
numBytes = PR_Write(sslSocket, reqhd, PORT_Strlen(reqhd));
441
/* Write the buffer data to the socket. */
442
if (buffer->index <= 0) {
443
/* Reached the EOF. Report incomplete transaction to socket. */
444
PORT_Sprintf(messageBuffer,
445
"GET or POST incomplete after %d bytes.\r\n",
447
numBytes = PR_Write(sslSocket, messageBuffer,
448
PORT_Strlen(messageBuffer));
454
/* Display the buffer data. */
455
fwrite(buffer->data, 1, buffer->index, stdout);
456
/* Write the buffer data to the socket. */
457
numBytes = PR_Write(sslSocket, buffer->data, buffer->index);
462
/* Display security information for the socket. */
463
printSecurityInfo(sslSocket);
464
/* Write any discarded data out to the socket. */
465
if (buffer->index < buffer->dataEnd) {
466
PORT_Sprintf(buffer->data, "Discarded %d characters.\r\n",
467
buffer->dataEnd - buffer->index);
468
numBytes = PR_Write(sslSocket, buffer->data,
469
PORT_Strlen(buffer->data));
477
/* Write a footer to the socket. */
478
numBytes = PR_Write(sslSocket, footer, PORT_Strlen(footer));
484
/* Write a link to the socket. */
485
numBytes = PR_Write(sslSocket, link, PORT_Strlen(link));
491
/* Complete the HTTP transaction. */
492
numBytes = PR_Write(sslSocket, "EOF\r\n\r\n\r\n", 9);
498
/* Do a nice shutdown if asked. */
499
if (!strncmp(buffer->data, stopCmd, strlen(stopCmd))) {
506
/* Do a nice shutdown if asked. */
507
if (!strncmp(buffer->data, stopCmd, strlen(stopCmd))) {
513
/* Function: int handle_connection()
515
* Purpose: Thread to handle a connection to a socket.
519
handle_connection(void *tcp_sock, int requestCert)
521
PRFileDesc * tcpSocket = (PRFileDesc *)tcp_sock;
522
PRFileDesc * sslSocket = NULL;
523
SECStatus secStatus = SECFailure;
525
PRSocketOptionData socketOption;
527
char * fileName = NULL;
529
/* Initialize the data buffer. */
530
memset(buffer.data, 0, BUFFER_SIZE);
531
buffer.remaining = BUFFER_SIZE;
533
buffer.dataStart = 0;
536
/* Make sure the socket is blocking. */
537
socketOption.option = PR_SockOpt_Nonblocking;
538
socketOption.value.non_blocking = PR_FALSE;
539
PR_SetSocketOption(tcpSocket, &socketOption);
541
sslSocket = setupSSLSocket(tcpSocket, requestCert);
542
if (sslSocket == NULL) {
543
errWarn("setupSSLSocket");
547
secStatus = SSL_ResetHandshake(sslSocket, /* asServer */ PR_TRUE);
548
if (secStatus != SECSuccess) {
549
errWarn("SSL_ResetHandshake");
553
/* Read data from the socket, parse it for HTTP content.
554
* If the user is requesting/requiring authentication, authenticate
555
* the socket. Then write the result back to the socket. */
556
fprintf(stdout, "\nReading data from socket...\n\n");
557
secStatus = readDataFromSocket(sslSocket, &buffer, &fileName);
558
if (secStatus != SECSuccess) {
561
if (requestCert >= REQUEST_CERT_ALL) {
562
fprintf(stdout, "\nAuthentication requested.\n\n");
563
secStatus = authenticateSocket(sslSocket,
564
(requestCert == REQUIRE_CERT_ALL));
565
if (secStatus != SECSuccess) {
570
fprintf(stdout, "\nWriting data to socket...\n\n");
571
secStatus = writeDataToSocket(sslSocket, &buffer, fileName);
575
/* Close down the socket. */
576
prStatus = PR_Close(tcpSocket);
577
if (prStatus != PR_SUCCESS) {
584
/* Function: int accept_connection()
586
* Purpose: Thread to accept a connection to the socket.
590
accept_connection(void *listener, int requestCert)
592
PRFileDesc *listenSocket = (PRFileDesc*)listener;
596
/* XXX need an SSL socket here? */
598
PRFileDesc *tcpSocket;
601
fprintf(stderr, "\n\n\nAbout to call accept.\n");
603
/* Accept a connection to the socket. */
604
tcpSocket = PR_Accept(listenSocket, &addr, PR_INTERVAL_NO_TIMEOUT);
605
if (tcpSocket == NULL) {
606
errWarn("PR_Accept");
610
/* Accepted the connection, now handle it. */
611
result = launch_thread(&threadMGR, handle_connection,
612
tcpSocket, requestCert);
614
if (result != SECSuccess) {
615
prStatus = PR_Close(tcpSocket);
616
if (prStatus != PR_SUCCESS) {
623
fprintf(stderr, "Closing listen socket.\n");
625
prStatus = PR_Close(listenSocket);
626
if (prStatus != PR_SUCCESS) {
632
/* Function: void server_main()
634
* Purpose: This is the server's main function. It configures a socket
642
SECKEYPrivateKey * privKey,
643
CERTCertificate * cert,
648
PRFileDesc * listenSocket;
650
PRSocketOptionData socketOption;
652
/* Create a new socket. */
653
listenSocket = PR_NewTCPSocket();
654
if (listenSocket == NULL) {
655
exitErr("PR_NewTCPSocket");
658
/* Set socket to be blocking -
659
* on some platforms the default is nonblocking.
661
socketOption.option = PR_SockOpt_Nonblocking;
662
socketOption.value.non_blocking = PR_FALSE;
664
prStatus = PR_SetSocketOption(listenSocket, &socketOption);
665
if (prStatus != PR_SUCCESS) {
666
exitErr("PR_SetSocketOption");
669
/* This cipher is not on by default. The Acceptance test
670
* would like it to be. Turn this cipher on.
672
secStatus = SSL_CipherPrefSetDefault(SSL_RSA_WITH_NULL_MD5, PR_TRUE);
673
if (secStatus != SECSuccess) {
674
exitErr("SSL_CipherPrefSetDefault:SSL_RSA_WITH_NULL_MD5");
677
/* Configure the network connection. */
678
addr.inet.family = PR_AF_INET;
679
addr.inet.ip = PR_INADDR_ANY;
680
addr.inet.port = PR_htons(port);
682
/* Bind the address to the listener socket. */
683
prStatus = PR_Bind(listenSocket, &addr);
684
if (prStatus != PR_SUCCESS) {
688
/* Listen for connection on the socket. The second argument is
689
* the maximum size of the queue for pending connections.
691
prStatus = PR_Listen(listenSocket, 5);
692
if (prStatus != PR_SUCCESS) {
693
exitErr("PR_Listen");
696
/* Launch thread to handle connections to the socket. */
697
secStatus = launch_thread(&threadMGR, accept_connection,
698
listenSocket, requestCert);
699
if (secStatus != SECSuccess) {
700
PR_Close(listenSocket);
702
reap_threads(&threadMGR);
703
destroy_thread_data(&threadMGR);
707
/* Function: int main()
709
* Purpose: Parses command arguments and configures SSL server.
713
main(int argc, char **argv)
715
char * progName = NULL;
716
char * nickName = NULL;
717
char * cipherString = NULL;
720
unsigned short port = 0;
722
PRBool disableSSL3 = PR_FALSE;
723
PLOptState * optstate;
726
/* Zero out the thread manager. */
727
PORT_Memset(&threadMGR, 0, sizeof(threadMGR));
729
progName = PL_strdup(argv[0]);
731
optstate = PL_CreateOptState(argc, argv, "3FRc:d:fp:n:rw:");
732
while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
733
switch(optstate->option) {
734
case '3': disableSSL3 = PR_TRUE; break;
735
case 'F': requestCert = REQUIRE_CERT_ALL; break;
736
case 'R': requestCert = REQUEST_CERT_ALL; break;
737
case 'c': cipherString = PL_strdup(optstate->value); break;
738
case 'd': dir = PL_strdup(optstate->value); break;
739
case 'f': requestCert = REQUIRE_CERT_ONCE; break;
740
case 'n': nickName = PL_strdup(optstate->value); break;
741
case 'p': port = PORT_Atoi(optstate->value); break;
742
case 'r': requestCert = REQUEST_CERT_ONCE; break;
743
case 'w': password = PL_strdup(optstate->value); break;
745
case '?': Usage(progName);
749
if (nickName == NULL || port == 0)
752
/* Call the NSPR initialization routines. */
753
PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
755
/* Set the cert database password callback. */
756
PK11_SetPasswordFunc(myPasswd);
758
/* Initialize NSS. */
759
secStatus = NSS_Init(dir);
760
if (secStatus != SECSuccess) {
764
/* Set the policy for this server (REQUIRED - no default). */
765
secStatus = NSS_SetDomesticPolicy();
766
if (secStatus != SECSuccess) {
767
exitErr("NSS_SetDomesticPolicy");
771
/* all the SSL2 and SSL3 cipher suites are enabled by default. */
775
/* disable all the ciphers, then enable the ones we want. */
776
disableAllSSLCiphers();
778
while (0 != (ndx = *cipherString++)) {
784
cptr = islower(ndx) ? ssl3CipherSuites : ssl2CipherSuites;
785
for (ndx &= 0x1f; (cipher = *cptr++) != 0 && --ndx > 0; )
789
status = SSL_CipherPrefSetDefault(cipher, PR_TRUE);
790
if (status != SECSuccess)
791
errWarn("SSL_CipherPrefSetDefault()");
796
/* Get own certificate and private key. */
797
cert = PK11_FindCertFromNickname(nickName, password);
799
exitErr("PK11_FindCertFromNickname");
802
privKey = PK11_FindKeyByAnyCert(cert, password);
803
if (privKey == NULL) {
804
exitErr("PK11_FindKeyByAnyCert");
807
/* Configure the server's cache for a multi-process application
808
* using default timeout values (24 hrs) and directory location (/tmp).
810
SSL_ConfigMPServerSIDCache(256, 0, 0, NULL);
813
server_main(port, requestCert, privKey, cert, disableSSL3);
815
/* Shutdown NSS and exit NSPR gracefully. */
816
if (NSS_Shutdown() != SECSuccess) {