Back to index

lightning-sunbird  0.9+nobinonly
server.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 /****************************************************************************
00038  *  SSL server program listens on a port, accepts client connection, reads  *
00039  *  request and responds to it                                              *
00040  ****************************************************************************/
00041 
00042 /* Generic header files */
00043 
00044 #include <stdio.h>
00045 #include <string.h>
00046 
00047 /* NSPR header files */
00048 
00049 #include "nspr.h"
00050 #include "plgetopt.h"
00051 #include "prerror.h"
00052 #include "prnetdb.h"
00053 
00054 /* NSS header files */
00055 
00056 #include "pk11func.h"
00057 #include "secitem.h"
00058 #include "ssl.h"
00059 #include "certt.h"
00060 #include "nss.h"
00061 #include "secder.h"
00062 #include "key.h"
00063 #include "sslproto.h"
00064 
00065 /* Custom header files */
00066 
00067 #include "sslsample.h"
00068 
00069 #ifndef PORT_Sprintf
00070 #define PORT_Sprintf sprintf
00071 #endif
00072 
00073 #define REQUEST_CERT_ONCE 1
00074 #define REQUIRE_CERT_ONCE 2
00075 #define REQUEST_CERT_ALL  3
00076 #define REQUIRE_CERT_ALL  4
00077 
00078 /* Global variables */
00079 GlobalThreadMgr   threadMGR;
00080 char             *password = NULL;
00081 CERTCertificate  *cert = NULL;
00082 SECKEYPrivateKey *privKey = NULL;
00083 int               stopping;
00084 
00085 static void
00086 Usage(const char *progName)
00087 {
00088        fprintf(stderr, 
00089 
00090 "Usage: %s -n rsa_nickname -p port [-3RFrf] [-w password]\n"
00091 "                                  [-c ciphers] [-d dbdir] \n"
00092 "-3 means disable SSL v3\n"
00093 "-r means request certificate on first handshake.\n"
00094 "-f means require certificate on first handshake.\n"
00095 "-R means request certificate on all handshakes.\n"
00096 "-F means require certificate on all handshakes.\n"
00097 "-c ciphers   Letter(s) chosen from the following list\n"
00098 "A       SSL2 RC4 128 WITH MD5\n"
00099 "B       SSL2 RC4 128 EXPORT40 WITH MD5\n"
00100 "C       SSL2 RC2 128 CBC WITH MD5\n"
00101 "D       SSL2 RC2 128 CBC EXPORT40 WITH MD5\n"
00102 "E       SSL2 DES 64 CBC WITH MD5\n"
00103 "F       SSL2 DES 192 EDE3 CBC WITH MD5\n"
00104 "\n"
00105 "c       SSL3 RSA WITH RC4 128 MD5\n"
00106 "d       SSL3 RSA WITH 3DES EDE CBC SHA\n"
00107 "e       SSL3 RSA WITH DES CBC SHA\n"
00108 "f       SSL3 RSA EXPORT WITH RC4 40 MD5\n"
00109 "g       SSL3 RSA EXPORT WITH RC2 CBC 40 MD5\n"
00110 "i       SSL3 RSA WITH NULL MD5\n"
00111 "j       SSL3 RSA FIPS WITH 3DES EDE CBC SHA\n"
00112 "k       SSL3 RSA FIPS WITH DES CBC SHA\n"
00113 "l       SSL3 RSA EXPORT WITH DES CBC SHA\t(new)\n"
00114 "m       SSL3 RSA EXPORT WITH RC4 56 SHA\t(new)\n",
00115        progName);
00116        exit(1);
00117 }
00118 
00119 /* Function:  readDataFromSocket()
00120  *
00121  * Purpose:  Parse an HTTP request by reading data from a GET or POST.
00122  *
00123  */
00124 SECStatus
00125 readDataFromSocket(PRFileDesc *sslSocket, DataBuffer *buffer, char **fileName)
00126 {
00127        char  *post;
00128        int    numBytes = 0;
00129        int    newln    = 0;  /* # of consecutive newlns */
00130 
00131        /* Read data while it comes in from the socket. */
00132        while (PR_TRUE) {
00133               buffer->index = 0;
00134               newln = 0;
00135 
00136               /* Read the buffer. */
00137               numBytes = PR_Read(sslSocket, &buffer->data[buffer->index], 
00138                                  buffer->remaining);
00139               if (numBytes <= 0) {
00140                      errWarn("PR_Read");
00141                      return SECFailure;
00142               }
00143               buffer->dataEnd = buffer->dataStart + numBytes;
00144 
00145               /* Parse the input, starting at the beginning of the buffer.
00146                * Stop when we detect two consecutive \n's (or \r\n's) 
00147                * as this signifies the end of the GET or POST portion.
00148                * The posted data follows.
00149                */
00150               while (buffer->index < buffer->dataEnd && newln < 2) {
00151                      int octet = buffer->data[buffer->index++];
00152                      if (octet == '\n') {
00153                             newln++;
00154                      } else if (octet != '\r') {
00155                             newln = 0;
00156                      }
00157               }
00158 
00159               /* Came to the end of the buffer, or second newline.
00160                * If we didn't get an empty line ("\r\n\r\n"), then keep on reading.
00161                */
00162               if (newln < 2) 
00163                      continue;
00164 
00165               /* we're at the end of the HTTP request.
00166                * If the request is a POST, then there will be one more
00167                * line of data.
00168                * This parsing is a hack, but ok for SSL test purposes.
00169                */
00170               post = PORT_Strstr(buffer->data, "POST ");
00171               if (!post || *post != 'P') 
00172                      break;
00173 
00174               /* It's a post, so look for the next and final CR/LF. */
00175               /* We should parse content length here, but ... */
00176               while (buffer->index < buffer->dataEnd && newln < 3) {
00177                      int octet = buffer->data[buffer->index++];
00178                      if (octet == '\n') {
00179                             newln++;
00180                      }
00181               }
00182 
00183               if (newln == 3)
00184                      break;
00185        }
00186 
00187        /* Have either (a) a complete get, (b) a complete post, (c) EOF */
00188 
00189        /*  Execute a "GET " operation. */
00190        if (buffer->index > 0 && PORT_Strncmp(buffer->data, "GET ", 4) == 0) {
00191               int fnLength;
00192 
00193               /* File name is the part after "GET ". */
00194               fnLength = strcspn(buffer->data + 5, " \r\n");
00195               *fileName = (char *)PORT_Alloc(fnLength + 1);
00196               PORT_Strncpy(*fileName, buffer->data + 5, fnLength);
00197               (*fileName)[fnLength] = '\0';
00198        }
00199 
00200        return SECSuccess;
00201 }
00202 
00203 /* Function:  authenticateSocket()
00204  *
00205  * Purpose:  Configure a socket for SSL.
00206  *
00207  *
00208  */
00209 PRFileDesc * 
00210 setupSSLSocket(PRFileDesc *tcpSocket, int requestCert)
00211 {
00212        PRFileDesc *sslSocket;
00213        SSLKEAType  certKEA;
00214        int         certErr = 0;
00215        SECStatus   secStatus;
00216 
00217        /* Set the appropriate flags. */
00218 
00219        sslSocket = SSL_ImportFD(NULL, tcpSocket);
00220        if (sslSocket == NULL) {
00221               errWarn("SSL_ImportFD");
00222               goto loser;
00223        }
00224    
00225        secStatus = SSL_OptionSet(sslSocket, SSL_SECURITY, PR_TRUE);
00226        if (secStatus != SECSuccess) {
00227               errWarn("SSL_OptionSet SSL_SECURITY");
00228               goto loser;
00229        }
00230 
00231        secStatus = SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_SERVER, PR_TRUE);
00232        if (secStatus != SECSuccess) {
00233               errWarn("SSL_OptionSet:SSL_HANDSHAKE_AS_SERVER");
00234               goto loser;
00235        }
00236 
00237        secStatus = SSL_OptionSet(sslSocket, SSL_REQUEST_CERTIFICATE, 
00238                               (requestCert >= REQUEST_CERT_ONCE));
00239        if (secStatus != SECSuccess) {
00240               errWarn("SSL_OptionSet:SSL_REQUEST_CERTIFICATE");
00241               goto loser;
00242        }
00243 
00244        secStatus = SSL_OptionSet(sslSocket, SSL_REQUIRE_CERTIFICATE, 
00245                               (requestCert == REQUIRE_CERT_ONCE));
00246        if (secStatus != SECSuccess) {
00247               errWarn("SSL_OptionSet:SSL_REQUIRE_CERTIFICATE");
00248               goto loser;
00249        }
00250 
00251        /* Set the appropriate callback routines. */
00252 
00253        secStatus = SSL_AuthCertificateHook(sslSocket, myAuthCertificate, 
00254                                            CERT_GetDefaultCertDB());
00255        if (secStatus != SECSuccess) {
00256               errWarn("SSL_AuthCertificateHook");
00257               goto loser;
00258        }
00259 
00260        secStatus = SSL_BadCertHook(sslSocket, 
00261                                    (SSLBadCertHandler)myBadCertHandler, &certErr);
00262        if (secStatus != SECSuccess) {
00263               errWarn("SSL_BadCertHook");
00264               goto loser;
00265        }
00266 
00267        secStatus = SSL_HandshakeCallback(sslSocket,
00268                                         (SSLHandshakeCallback)myHandshakeCallback,
00269                                                                  NULL);
00270        if (secStatus != SECSuccess) {
00271               errWarn("SSL_HandshakeCallback");
00272               goto loser;
00273        }
00274 
00275        secStatus = SSL_SetPKCS11PinArg(sslSocket, password);
00276        if (secStatus != SECSuccess) {
00277               errWarn("SSL_HandshakeCallback");
00278               goto loser;
00279        }
00280 
00281        certKEA = NSS_FindCertKEAType(cert);
00282 
00283        secStatus = SSL_ConfigSecureServer(sslSocket, cert, privKey, certKEA);
00284        if (secStatus != SECSuccess) {
00285               errWarn("SSL_ConfigSecureServer");
00286               goto loser;
00287        }
00288 
00289        return sslSocket;
00290 
00291 loser:
00292 
00293        PR_Close(tcpSocket);
00294        return NULL;
00295 }
00296 
00297 /* Function:  authenticateSocket()
00298  *
00299  * Purpose:  Perform client authentication on the socket.
00300  *
00301  */
00302 SECStatus
00303 authenticateSocket(PRFileDesc *sslSocket, PRBool requireCert)
00304 {
00305        CERTCertificate *cert;
00306        SECStatus secStatus;
00307 
00308        /* Returns NULL if client authentication is not enabled or if the
00309         * client had no certificate. */
00310        cert = SSL_PeerCertificate(sslSocket);
00311        if (cert) {
00312               /* Client had a certificate, so authentication is through. */
00313               CERT_DestroyCertificate(cert);
00314               return SECSuccess;
00315        }
00316 
00317        /* Request client to authenticate itself. */
00318        secStatus = SSL_OptionSet(sslSocket, SSL_REQUEST_CERTIFICATE, PR_TRUE);
00319        if (secStatus != SECSuccess) {
00320               errWarn("SSL_OptionSet:SSL_REQUEST_CERTIFICATE");
00321               return SECFailure;
00322        }
00323 
00324        /* If desired, require client to authenticate itself.  Note
00325         * SSL_REQUEST_CERTIFICATE must also be on, as above.  */
00326        secStatus = SSL_OptionSet(sslSocket, SSL_REQUIRE_CERTIFICATE, requireCert);
00327        if (secStatus != SECSuccess) {
00328               errWarn("SSL_OptionSet:SSL_REQUIRE_CERTIFICATE");
00329               return SECFailure;
00330        }
00331 
00332        /* Having changed socket configuration parameters, redo handshake. */
00333        secStatus = SSL_ReHandshake(sslSocket, PR_TRUE);
00334        if (secStatus != SECSuccess) {
00335               errWarn("SSL_ReHandshake");
00336               return SECFailure;
00337        }
00338 
00339        /* Force the handshake to complete before moving on. */
00340        secStatus = SSL_ForceHandshake(sslSocket);
00341        if (secStatus != SECSuccess) {
00342               errWarn("SSL_ForceHandshake");
00343               return SECFailure;
00344        }
00345 
00346        return SECSuccess;
00347 }
00348 
00349 /* Function:  writeDataToSocket
00350  *
00351  * Purpose:  Write the client's request back to the socket.  If the client
00352  *           requested a file, dump it to the socket.
00353  *
00354  */
00355 SECStatus
00356 writeDataToSocket(PRFileDesc *sslSocket, DataBuffer *buffer, char *fileName)
00357 {
00358        int headerLength;
00359        int numBytes;
00360        char messageBuffer[120];
00361        PRFileDesc *local_file_fd = NULL;
00362        char header[] = "<html><body><h1>Sample SSL server</h1><br><br>";
00363        char filehd[] = "<h2>The file you requested:</h2><br>";
00364        char reqhd[]  = "<h2>This is your request:</h2><br>";
00365        char link[]   = "Try getting a <a HREF=\"../testfile\">file</a><br>";
00366        char footer[] = "<br><h2>End of request.</h2><br></body></html>";
00367 
00368        headerLength = PORT_Strlen(defaultHeader);
00369 
00370        /* Write a header to the socket. */
00371        numBytes = PR_Write(sslSocket, header, PORT_Strlen(header));
00372        if (numBytes < 0) {
00373               errWarn("PR_Write");
00374               goto loser;
00375        }
00376 
00377        if (fileName) {
00378               PRFileInfo  info;
00379               PRStatus    prStatus;
00380 
00381               /* Try to open the local file named.      
00382                * If successful, then write it to the client.
00383                */
00384               prStatus = PR_GetFileInfo(fileName, &info);
00385               if (prStatus != PR_SUCCESS ||
00386                   info.type != PR_FILE_FILE ||
00387                   info.size < 0) {
00388                      PORT_Free(fileName);
00389                      /* Maybe a GET not sent from client.c? */
00390                      goto writerequest;
00391               }
00392 
00393               local_file_fd = PR_Open(fileName, PR_RDONLY, 0);
00394               if (local_file_fd == NULL) {
00395                      PORT_Free(fileName);
00396                      goto writerequest;
00397               }
00398 
00399               /* Write a header to the socket. */
00400               numBytes = PR_Write(sslSocket, filehd, PORT_Strlen(filehd));
00401               if (numBytes < 0) {
00402                      errWarn("PR_Write");
00403                      goto loser;
00404               }
00405 
00406               /* Transmit the local file prepended by the default header
00407                * across the socket.
00408                */
00409               numBytes = PR_TransmitFile(sslSocket, local_file_fd, 
00410                                          defaultHeader, headerLength,
00411                                          PR_TRANSMITFILE_KEEP_OPEN,
00412                                          PR_INTERVAL_NO_TIMEOUT);
00413 
00414               /* Error in transmission. */
00415               if (numBytes < 0) {
00416                      errWarn("PR_TransmitFile");
00417                      /*
00418                      i = PORT_Strlen(errString);
00419                      PORT_Memcpy(buf, errString, i);
00420                      */
00421               /* Transmitted bytes successfully. */
00422               } else {
00423                      numBytes -= headerLength;
00424                      fprintf(stderr, "PR_TransmitFile wrote %d bytes from %s\n",
00425                              numBytes, fileName);
00426               }
00427 
00428               PORT_Free(fileName);
00429               PR_Close(local_file_fd);
00430        }
00431 
00432 writerequest:
00433 
00434        /* Write a header to the socket. */
00435        numBytes = PR_Write(sslSocket, reqhd, PORT_Strlen(reqhd));
00436        if (numBytes < 0) {
00437               errWarn("PR_Write");
00438               goto loser;
00439        }
00440 
00441        /* Write the buffer data to the socket. */
00442        if (buffer->index <= 0) {
00443               /* Reached the EOF.  Report incomplete transaction to socket. */
00444               PORT_Sprintf(messageBuffer,
00445                            "GET or POST incomplete after %d bytes.\r\n",
00446                            buffer->dataEnd);
00447               numBytes = PR_Write(sslSocket, messageBuffer, 
00448                                   PORT_Strlen(messageBuffer));
00449               if (numBytes < 0) {
00450                      errWarn("PR_Write");
00451                      goto loser;
00452               }
00453        } else {
00454               /* Display the buffer data. */
00455               fwrite(buffer->data, 1, buffer->index, stdout);
00456               /* Write the buffer data to the socket. */
00457               numBytes = PR_Write(sslSocket, buffer->data, buffer->index);
00458               if (numBytes < 0) {
00459                      errWarn("PR_Write");
00460                      goto loser;
00461               }
00462               /* Display security information for the socket. */
00463               printSecurityInfo(sslSocket);
00464               /* Write any discarded data out to the socket. */
00465               if (buffer->index < buffer->dataEnd) {
00466                      PORT_Sprintf(buffer->data, "Discarded %d characters.\r\n", 
00467                                   buffer->dataEnd - buffer->index);
00468                      numBytes = PR_Write(sslSocket, buffer->data, 
00469                                          PORT_Strlen(buffer->data));
00470                      if (numBytes < 0) {
00471                             errWarn("PR_Write");
00472                             goto loser;
00473                      }
00474               }
00475        }
00476 
00477        /* Write a footer to the socket. */
00478        numBytes = PR_Write(sslSocket, footer, PORT_Strlen(footer));
00479        if (numBytes < 0) {
00480               errWarn("PR_Write");
00481               goto loser;
00482        }
00483 
00484        /* Write a link to the socket. */
00485        numBytes = PR_Write(sslSocket, link, PORT_Strlen(link));
00486        if (numBytes < 0) {
00487               errWarn("PR_Write");
00488               goto loser;
00489        }
00490 
00491        /* Complete the HTTP transaction. */
00492        numBytes = PR_Write(sslSocket, "EOF\r\n\r\n\r\n", 9);
00493        if (numBytes < 0) {
00494               errWarn("PR_Write");
00495               goto loser;
00496        }
00497 
00498        /* Do a nice shutdown if asked. */
00499        if (!strncmp(buffer->data, stopCmd, strlen(stopCmd))) {
00500               stopping = 1;
00501        }
00502        return SECSuccess;
00503 
00504 loser:
00505 
00506        /* Do a nice shutdown if asked. */
00507        if (!strncmp(buffer->data, stopCmd, strlen(stopCmd))) {
00508               stopping = 1;
00509        }
00510        return SECFailure;
00511 }
00512 
00513 /* Function:  int handle_connection()
00514  *
00515  * Purpose:  Thread to handle a connection to a socket.
00516  *
00517  */
00518 SECStatus
00519 handle_connection(void *tcp_sock, int requestCert)
00520 {
00521        PRFileDesc *       tcpSocket = (PRFileDesc *)tcp_sock;
00522        PRFileDesc *       sslSocket = NULL;
00523        SECStatus          secStatus = SECFailure;
00524        PRStatus           prStatus;
00525        PRSocketOptionData socketOption;
00526        DataBuffer         buffer;
00527        char *             fileName = NULL;
00528 
00529        /* Initialize the data buffer. */
00530        memset(buffer.data, 0, BUFFER_SIZE);
00531        buffer.remaining = BUFFER_SIZE;
00532        buffer.index = 0;
00533        buffer.dataStart = 0;
00534        buffer.dataEnd = 0;
00535 
00536        /* Make sure the socket is blocking. */
00537        socketOption.option             = PR_SockOpt_Nonblocking;
00538        socketOption.value.non_blocking = PR_FALSE;
00539        PR_SetSocketOption(tcpSocket, &socketOption);
00540 
00541        sslSocket = setupSSLSocket(tcpSocket, requestCert);
00542        if (sslSocket == NULL) {
00543               errWarn("setupSSLSocket");
00544               goto cleanup;
00545        }
00546 
00547        secStatus = SSL_ResetHandshake(sslSocket, /* asServer */ PR_TRUE);
00548        if (secStatus != SECSuccess) {
00549               errWarn("SSL_ResetHandshake");
00550               goto cleanup;
00551        }
00552 
00553        /* Read data from the socket, parse it for HTTP content.
00554         * If the user is requesting/requiring authentication, authenticate
00555         * the socket.  Then write the result back to the socket.  */
00556        fprintf(stdout, "\nReading data from socket...\n\n");
00557        secStatus = readDataFromSocket(sslSocket, &buffer, &fileName);
00558        if (secStatus != SECSuccess) {
00559               goto cleanup;
00560        }
00561        if (requestCert >= REQUEST_CERT_ALL) {
00562               fprintf(stdout, "\nAuthentication requested.\n\n");
00563               secStatus = authenticateSocket(sslSocket, 
00564                                              (requestCert == REQUIRE_CERT_ALL));
00565               if (secStatus != SECSuccess) {
00566                      goto cleanup;
00567               }
00568        }
00569 
00570        fprintf(stdout, "\nWriting data to socket...\n\n");
00571        secStatus = writeDataToSocket(sslSocket, &buffer, fileName);
00572 
00573 cleanup:
00574 
00575        /* Close down the socket. */
00576        prStatus = PR_Close(tcpSocket);
00577        if (prStatus != PR_SUCCESS) {
00578               errWarn("PR_Close");
00579        }
00580 
00581        return secStatus;
00582 }
00583 
00584 /* Function:  int accept_connection()
00585  *
00586  * Purpose:  Thread to accept a connection to the socket.
00587  *
00588  */
00589 SECStatus
00590 accept_connection(void *listener, int requestCert)
00591 {
00592        PRFileDesc *listenSocket = (PRFileDesc*)listener;
00593        PRNetAddr   addr;
00594        PRStatus    prStatus;
00595 
00596        /* XXX need an SSL socket here? */
00597        while (!stopping) {
00598               PRFileDesc *tcpSocket;
00599               SECStatus     result;
00600 
00601               fprintf(stderr, "\n\n\nAbout to call accept.\n");
00602 
00603               /* Accept a connection to the socket. */
00604               tcpSocket = PR_Accept(listenSocket, &addr, PR_INTERVAL_NO_TIMEOUT);
00605               if (tcpSocket == NULL) {
00606                      errWarn("PR_Accept");
00607                      break;
00608               }
00609 
00610               /* Accepted the connection, now handle it. */
00611               result = launch_thread(&threadMGR, handle_connection, 
00612                                      tcpSocket, requestCert);
00613 
00614               if (result != SECSuccess) {
00615                      prStatus = PR_Close(tcpSocket);
00616                      if (prStatus != PR_SUCCESS) {
00617                             exitErr("PR_Close");
00618                      }
00619                      break;
00620               }
00621        }
00622 
00623        fprintf(stderr, "Closing listen socket.\n");
00624 
00625        prStatus = PR_Close(listenSocket);
00626        if (prStatus != PR_SUCCESS) {
00627               exitErr("PR_Close");
00628        }
00629        return SECSuccess;
00630 }
00631 
00632 /* Function:  void server_main()
00633  *
00634  * Purpose:  This is the server's main function.  It configures a socket
00635  *                    and listens to it.
00636  *
00637  */
00638 void
00639 server_main(
00640        unsigned short      port, 
00641        int                 requestCert, 
00642        SECKEYPrivateKey *  privKey,
00643        CERTCertificate *   cert, 
00644        PRBool              disableSSL3)
00645 {
00646        SECStatus           secStatus;
00647        PRStatus            prStatus;
00648        PRFileDesc *        listenSocket;
00649        PRNetAddr           addr;
00650        PRSocketOptionData  socketOption;
00651 
00652        /* Create a new socket. */
00653        listenSocket = PR_NewTCPSocket();
00654        if (listenSocket == NULL) {
00655               exitErr("PR_NewTCPSocket");
00656        }
00657 
00658        /* Set socket to be blocking -
00659         * on some platforms the default is nonblocking.
00660         */
00661        socketOption.option = PR_SockOpt_Nonblocking;
00662        socketOption.value.non_blocking = PR_FALSE;
00663 
00664        prStatus = PR_SetSocketOption(listenSocket, &socketOption);
00665        if (prStatus != PR_SUCCESS) {
00666               exitErr("PR_SetSocketOption");
00667        }
00668 
00669        /* This cipher is not on by default. The Acceptance test
00670         * would like it to be. Turn this cipher on.
00671         */
00672        secStatus = SSL_CipherPrefSetDefault(SSL_RSA_WITH_NULL_MD5, PR_TRUE);
00673        if (secStatus != SECSuccess) {
00674               exitErr("SSL_CipherPrefSetDefault:SSL_RSA_WITH_NULL_MD5");
00675        }
00676 
00677        /* Configure the network connection. */
00678        addr.inet.family = PR_AF_INET;
00679        addr.inet.ip   = PR_INADDR_ANY;
00680        addr.inet.port        = PR_htons(port);
00681 
00682        /* Bind the address to the listener socket. */
00683        prStatus = PR_Bind(listenSocket, &addr);
00684        if (prStatus != PR_SUCCESS) {
00685               exitErr("PR_Bind");
00686        }
00687 
00688        /* Listen for connection on the socket.  The second argument is
00689         * the maximum size of the queue for pending connections.
00690         */
00691        prStatus = PR_Listen(listenSocket, 5);
00692        if (prStatus != PR_SUCCESS) {
00693               exitErr("PR_Listen");
00694        }
00695 
00696        /* Launch thread to handle connections to the socket. */
00697        secStatus = launch_thread(&threadMGR, accept_connection, 
00698                               listenSocket, requestCert);
00699        if (secStatus != SECSuccess) {
00700               PR_Close(listenSocket);
00701        } else {
00702               reap_threads(&threadMGR);
00703               destroy_thread_data(&threadMGR);
00704        }
00705 }
00706 
00707 /* Function: int main()
00708  *
00709  * Purpose:  Parses command arguments and configures SSL server.
00710  *
00711  */
00712 int
00713 main(int argc, char **argv)
00714 {
00715        char *              progName      = NULL;
00716        char *              nickName      = NULL;
00717        char *              cipherString  = NULL;
00718        char *              dir           = ".";
00719        int                 requestCert   = 0;
00720        unsigned short      port          = 0;
00721        SECStatus           secStatus;
00722        PRBool              disableSSL3   = PR_FALSE;
00723        PLOptState *        optstate;
00724        PLOptStatus         status;
00725 
00726        /* Zero out the thread manager. */
00727        PORT_Memset(&threadMGR, 0, sizeof(threadMGR));
00728 
00729        progName = PL_strdup(argv[0]);
00730 
00731        optstate = PL_CreateOptState(argc, argv, "3FRc:d:fp:n:rw:");
00732        while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
00733               switch(optstate->option) {
00734               case '3': disableSSL3 = PR_TRUE;                      break;
00735               case 'F': requestCert = REQUIRE_CERT_ALL;             break;
00736               case 'R': requestCert = REQUEST_CERT_ALL;             break;
00737               case 'c': cipherString = PL_strdup(optstate->value);  break;
00738               case 'd': dir = PL_strdup(optstate->value);           break;
00739               case 'f': requestCert = REQUIRE_CERT_ONCE;            break;
00740               case 'n': nickName = PL_strdup(optstate->value);      break;
00741               case 'p': port = PORT_Atoi(optstate->value);          break;
00742               case 'r': requestCert = REQUEST_CERT_ONCE;            break;
00743               case 'w': password = PL_strdup(optstate->value);      break;
00744               default:
00745               case '?': Usage(progName);
00746               }
00747        }
00748 
00749        if (nickName == NULL || port == 0)
00750               Usage(progName);
00751 
00752        /* Call the NSPR initialization routines. */
00753        PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
00754 
00755        /* Set the cert database password callback. */
00756        PK11_SetPasswordFunc(myPasswd);
00757 
00758        /* Initialize NSS. */
00759        secStatus = NSS_Init(dir);
00760        if (secStatus != SECSuccess) {
00761               exitErr("NSS_Init");
00762        }
00763 
00764        /* Set the policy for this server (REQUIRED - no default). */
00765        secStatus = NSS_SetDomesticPolicy();
00766        if (secStatus != SECSuccess) {
00767               exitErr("NSS_SetDomesticPolicy");
00768        }
00769 
00770        /* XXX keep this? */
00771        /* all the SSL2 and SSL3 cipher suites are enabled by default. */
00772        if (cipherString) {
00773            int ndx;
00774 
00775            /* disable all the ciphers, then enable the ones we want. */
00776            disableAllSSLCiphers();
00777 
00778            while (0 != (ndx = *cipherString++)) {
00779               int *cptr;
00780               int  cipher;
00781 
00782               if (! isalpha(ndx))
00783                      Usage(progName);
00784               cptr = islower(ndx) ? ssl3CipherSuites : ssl2CipherSuites;
00785               for (ndx &= 0x1f; (cipher = *cptr++) != 0 && --ndx > 0; ) 
00786                   /* do nothing */;
00787               if (cipher) {
00788                   SECStatus status;
00789                   status = SSL_CipherPrefSetDefault(cipher, PR_TRUE);
00790                   if (status != SECSuccess) 
00791                      errWarn("SSL_CipherPrefSetDefault()");
00792               }
00793            }
00794        }
00795 
00796        /* Get own certificate and private key. */
00797        cert = PK11_FindCertFromNickname(nickName, password);
00798        if (cert == NULL) {
00799               exitErr("PK11_FindCertFromNickname");
00800        }
00801 
00802        privKey = PK11_FindKeyByAnyCert(cert, password);
00803        if (privKey == NULL) {
00804               exitErr("PK11_FindKeyByAnyCert");
00805        }
00806 
00807        /* Configure the server's cache for a multi-process application
00808         * using default timeout values (24 hrs) and directory location (/tmp). 
00809         */
00810        SSL_ConfigMPServerSIDCache(256, 0, 0, NULL);
00811 
00812        /* Launch server. */
00813        server_main(port, requestCert, privKey, cert, disableSSL3);
00814 
00815        /* Shutdown NSS and exit NSPR gracefully. */
00816        if (NSS_Shutdown() != SECSuccess) {
00817             exit(1);
00818         }
00819        PR_Cleanup();
00820        return 0;
00821 }