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
) {