Example #1
0
static SECItem *
encode(PLArenaPool *arena, CERTOCSPCertID *cid, CERTCertificate *ca)
{
    SECItem *response;
    PRTime now = PR_Now();
    PRTime nextUpdate;
    CERTOCSPSingleResponse **responses;
    CERTOCSPSingleResponse *sr;

    if (!arena)
        return NULL;

    nextUpdate = now + 10 * PR_USEC_PER_SEC; /* in the future */
    
    sr = CERT_CreateOCSPSingleResponseGood(arena, cid, now, &nextUpdate);

    /* meaning of value 2: one entry + one end marker */
    responses = PORT_ArenaNewArray(arena, CERTOCSPSingleResponse*, 2);
    if (responses == NULL)
        return NULL;
    
    responses[0] = sr;
    responses[1] = NULL;
    
    response = CERT_CreateEncodedOCSPSuccessResponse(
        arena, ca, ocspResponderID_byName, now, responses, &pwdata);

    return response;
}
Example #2
0
int
handle_connection(
    PRFileDesc *tcp_sock,
    PRFileDesc *model_sock,
    int requestCert)
{
    PRFileDesc *ssl_sock = NULL;
    PRFileDesc *local_file_fd = NULL;
    char *pBuf; /* unused space at end of buf */
    const char *errString;
    PRStatus status;
    int bufRem;    /* unused bytes at end of buf */
    int bufDat;    /* characters received in buf */
    int newln = 0; /* # of consecutive newlns */
    int firstTime = 1;
    int reqLen;
    int rv;
    int numIOVs;
    PRSocketOptionData opt;
    PRIOVec iovs[16];
    char msgBuf[160];
    char buf[10240];
    char fileName[513];
    char *getData = NULL; /* inplace conversion */
    SECItem postData;
    PRBool isOcspRequest = PR_FALSE;
    PRBool isPost;

    postData.data = NULL;
    postData.len = 0;

    pBuf = buf;
    bufRem = sizeof buf;

    VLOG(("httpserv: handle_connection: starting"));
    opt.option = PR_SockOpt_Nonblocking;
    opt.value.non_blocking = PR_FALSE;
    PR_SetSocketOption(tcp_sock, &opt);

    VLOG(("httpserv: handle_connection: starting\n"));
    ssl_sock = tcp_sock;

    if (noDelay) {
        opt.option = PR_SockOpt_NoDelay;
        opt.value.no_delay = PR_TRUE;
        status = PR_SetSocketOption(ssl_sock, &opt);
        if (status != PR_SUCCESS) {
            errWarn("PR_SetSocketOption(PR_SockOpt_NoDelay, PR_TRUE)");
            if (ssl_sock) {
                PR_Close(ssl_sock);
            }
            return SECFailure;
        }
    }

    while (1) {
        const char *post;
        const char *foundStr = NULL;
        const char *tmp = NULL;

        newln = 0;
        reqLen = 0;

        rv = PR_Read(ssl_sock, pBuf, bufRem - 1);
        if (rv == 0 ||
            (rv < 0 && PR_END_OF_FILE_ERROR == PR_GetError())) {
            if (verbose)
                errWarn("HDX PR_Read hit EOF");
            break;
        }
        if (rv < 0) {
            errWarn("HDX PR_Read");
            goto cleanup;
        }
        /* NULL termination */
        pBuf[rv] = 0;
        if (firstTime) {
            firstTime = 0;
        }

        pBuf += rv;
        bufRem -= rv;
        bufDat = pBuf - buf;
        /* 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 (reqLen < bufDat && newln < 2) {
            int octet = buf[reqLen++];
            if (octet == '\n') {
                newln++;
            } else if (octet != '\r') {
                newln = 0;
            }
        }

        /* came to the end of the buffer, or second newln
         * If we didn't get an empty line (CRLFCRLF) 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(buf, "POST ");
        if (!post || *post != 'P')
            break;

        postData.data = (void *)(buf + reqLen);

        tmp = "content-length: ";
        foundStr = PL_strcasestr(buf, tmp);
        if (foundStr) {
            int expectedPostLen;
            int havePostLen;

            expectedPostLen = atoi(foundStr + strlen(tmp));
            havePostLen = bufDat - reqLen;
            if (havePostLen >= expectedPostLen) {
                postData.len = expectedPostLen;
                break;
            }
        } else {
            /* use legacy hack */
            /* It's a post, so look for the next and final CR/LF. */
            while (reqLen < bufDat && newln < 3) {
                int octet = buf[reqLen++];
                if (octet == '\n') {
                    newln++;
                }
            }
            if (newln == 3)
                break;
        }
    } /* read loop */

    bufDat = pBuf - buf;
    if (bufDat)
        do { /* just close if no data */
            /* Have either (a) a complete get, (b) a complete post, (c) EOF */
            if (reqLen > 0) {
                PRBool isGetOrPost = PR_FALSE;
                unsigned skipChars = 0;
                isPost = PR_FALSE;

                if (!strncmp(buf, getCmd, sizeof getCmd - 1)) {
                    isGetOrPost = PR_TRUE;
                    skipChars = 4;
                } else if (!strncmp(buf, "POST ", 5)) {
                    isGetOrPost = PR_TRUE;
                    isPost = PR_TRUE;
                    skipChars = 5;
                }

                if (isGetOrPost) {
                    char *fnBegin = buf;
                    char *fnEnd;
                    char *fnstart = NULL;
                    PRFileInfo info;

                    fnBegin += skipChars;

                    fnEnd = strpbrk(fnBegin, " \r\n");
                    if (fnEnd) {
                        int fnLen = fnEnd - fnBegin;
                        if (fnLen < sizeof fileName) {
                            strncpy(fileName, fnBegin, fnLen);
                            fileName[fnLen] = 0; /* null terminate */
                            fnstart = fileName;
                            /* strip initial / because our root is the current directory*/
                            while (*fnstart && *fnstart == '/')
                                ++fnstart;
                        }
                    }
                    if (fnstart) {
                        if (!strncmp(fnstart, "ocsp", 4)) {
                            if (isPost) {
                                if (postData.data) {
                                    isOcspRequest = PR_TRUE;
                                }
                            } else {
                                if (!strncmp(fnstart, "ocsp/", 5)) {
                                    isOcspRequest = PR_TRUE;
                                    getData = fnstart + 5;
                                }
                            }
                        } else {
                            /* try to open the file named.
                             * If successful, then write it to the client.
                             */
                            status = PR_GetFileInfo(fnstart, &info);
                            if (status == PR_SUCCESS &&
                                info.type == PR_FILE_FILE &&
                                info.size >= 0) {
                                local_file_fd = PR_Open(fnstart, PR_RDONLY, 0);
                            }
                        }
                    }
                }
            }

            numIOVs = 0;

            iovs[numIOVs].iov_base = (char *)outHeader;
            iovs[numIOVs].iov_len = (sizeof(outHeader)) - 1;
            numIOVs++;

            if (isOcspRequest && caRevoInfos) {
                CERTOCSPRequest *request = NULL;
                PRBool failThisRequest = PR_FALSE;
                PLArenaPool *arena = NULL;

                if (ocspMethodsAllowed == ocspGetOnly && postData.len) {
                    failThisRequest = PR_TRUE;
                } else if (ocspMethodsAllowed == ocspPostOnly && getData) {
                    failThisRequest = PR_TRUE;
                } else if (ocspMethodsAllowed == ocspRandomGetFailure && getData) {
                    if (!(rand() % 2)) {
                        failThisRequest = PR_TRUE;
                    }
                }

                if (failThisRequest) {
                    PR_Write(ssl_sock, outBadRequestHeader, strlen(outBadRequestHeader));
                    break;
                }
                /* get is base64, post is binary.
                 * If we have base64, convert into the (empty) postData array.
                 */
                if (getData) {
                    if (urldecode_base64chars_inplace(getData) == SECSuccess) {
                        /* The code below can handle a NULL arena */
                        arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
                        NSSBase64_DecodeBuffer(arena, &postData, getData, strlen(getData));
                    }
                }
                if (postData.len) {
                    request = CERT_DecodeOCSPRequest(&postData);
                }
                if (arena) {
                    PORT_FreeArena(arena, PR_FALSE);
                }
                if (!request || !request->tbsRequest ||
                    !request->tbsRequest->requestList ||
                    !request->tbsRequest->requestList[0]) {
                    PORT_Sprintf(msgBuf, "Cannot decode OCSP request.\r\n");

                    iovs[numIOVs].iov_base = msgBuf;
                    iovs[numIOVs].iov_len = PORT_Strlen(msgBuf);
                    numIOVs++;
                } else {
                    /* TODO: support more than one request entry */
                    CERTOCSPCertID *reqid = request->tbsRequest->requestList[0]->reqCert;
                    const caRevoInfo *revoInfo = NULL;
                    PRBool unknown = PR_FALSE;
                    PRBool revoked = PR_FALSE;
                    PRTime nextUpdate = 0;
                    PRTime revoDate = 0;
                    PRCList *caRevoIter;

                    caRevoIter = &caRevoInfos->link;
                    do {
                        CERTOCSPCertID *caid;

                        revoInfo = (caRevoInfo *)caRevoIter;
                        caid = revoInfo->id;

                        if (SECOID_CompareAlgorithmID(&reqid->hashAlgorithm,
                                                      &caid->hashAlgorithm) == SECEqual &&
                            SECITEM_CompareItem(&reqid->issuerNameHash,
                                                &caid->issuerNameHash) == SECEqual &&
                            SECITEM_CompareItem(&reqid->issuerKeyHash,
                                                &caid->issuerKeyHash) == SECEqual) {
                            break;
                        }
                        revoInfo = NULL;
                        caRevoIter = PR_NEXT_LINK(caRevoIter);
                    } while (caRevoIter != &caRevoInfos->link);

                    if (!revoInfo) {
                        unknown = PR_TRUE;
                        revoInfo = caRevoInfos;
                    } else {
                        CERTCrl *crl = &revoInfo->crl->crl;
                        CERTCrlEntry *entry = NULL;
                        DER_DecodeTimeChoice(&nextUpdate, &crl->nextUpdate);
                        if (crl->entries) {
                            int iv = 0;
                            /* assign, not compare */
                            while ((entry = crl->entries[iv++])) {
                                if (SECITEM_CompareItem(&reqid->serialNumber,
                                                        &entry->serialNumber) == SECEqual) {
                                    break;
                                }
                            }
                        }
                        if (entry) {
                            /* revoked status response */
                            revoked = PR_TRUE;
                            DER_DecodeTimeChoice(&revoDate, &entry->revocationDate);
                        } else {
                            /* else good status response */
                            if (!isPost && ocspMethodsAllowed == ocspGetUnknown) {
                                unknown = PR_TRUE;
                                nextUpdate = PR_Now() + (PRTime)60 * 60 * 24 * PR_USEC_PER_SEC; /*tomorrow*/
                                revoDate = PR_Now() - (PRTime)60 * 60 * 24 * PR_USEC_PER_SEC;   /*yesterday*/
                            }
                        }
                    }

                    {
                        PRTime now = PR_Now();
                        PLArenaPool *arena = NULL;
                        CERTOCSPSingleResponse *sr;
                        CERTOCSPSingleResponse **singleResponses;
                        SECItem *ocspResponse;

                        arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);

                        if (unknown) {
                            sr = CERT_CreateOCSPSingleResponseUnknown(arena, reqid, now,
                                                                      &nextUpdate);
                        } else if (revoked) {
                            sr = CERT_CreateOCSPSingleResponseRevoked(arena, reqid, now,
                                                                      &nextUpdate, revoDate, NULL);
                        } else {
                            sr = CERT_CreateOCSPSingleResponseGood(arena, reqid, now,
                                                                   &nextUpdate);
                        }

                        /* meaning of value 2: one entry + one end marker */
                        singleResponses = PORT_ArenaNewArray(arena, CERTOCSPSingleResponse *, 2);
                        singleResponses[0] = sr;
                        singleResponses[1] = NULL;
                        ocspResponse = CERT_CreateEncodedOCSPSuccessResponse(arena,
                                                                             revoInfo->cert, ocspResponderID_byName, now,
                                                                             singleResponses, &pwdata);

                        if (!ocspResponse) {
                            PORT_Sprintf(msgBuf, "Failed to encode response\r\n");
                            iovs[numIOVs].iov_base = msgBuf;
                            iovs[numIOVs].iov_len = PORT_Strlen(msgBuf);
                            numIOVs++;
                        } else {
                            PR_Write(ssl_sock, outOcspHeader, strlen(outOcspHeader));
                            PR_Write(ssl_sock, ocspResponse->data, ocspResponse->len);
                            PORT_FreeArena(arena, PR_FALSE);
                        }
                    }
                    CERT_DestroyOCSPRequest(request);
                    break;
                }
            } else if (local_file_fd) {
                PRInt32 bytes;
                int errLen;
                bytes = PR_TransmitFile(ssl_sock, local_file_fd, outHeader,
                                        sizeof outHeader - 1,
                                        PR_TRANSMITFILE_KEEP_OPEN,
                                        PR_INTERVAL_NO_TIMEOUT);
                if (bytes >= 0) {
                    bytes -= sizeof outHeader - 1;
                    FPRINTF(stderr,
                            "httpserv: PR_TransmitFile wrote %d bytes from %s\n",
                            bytes, fileName);
                    break;
                }
                errString = errWarn("PR_TransmitFile");
                errLen = PORT_Strlen(errString);
                errLen = PR_MIN(errLen, sizeof msgBuf - 1);
                PORT_Memcpy(msgBuf, errString, errLen);
                msgBuf[errLen] = 0;

                iovs[numIOVs].iov_base = msgBuf;
                iovs[numIOVs].iov_len = PORT_Strlen(msgBuf);
                numIOVs++;
            } else if (reqLen <= 0) { /* hit eof */
                PORT_Sprintf(msgBuf, "Get or Post incomplete after %d bytes.\r\n",
                             bufDat);

                iovs[numIOVs].iov_base = msgBuf;
                iovs[numIOVs].iov_len = PORT_Strlen(msgBuf);
                numIOVs++;
            } else if (reqLen < bufDat) {
                PORT_Sprintf(msgBuf, "Discarded %d characters.\r\n",
                             bufDat - reqLen);

                iovs[numIOVs].iov_base = msgBuf;
                iovs[numIOVs].iov_len = PORT_Strlen(msgBuf);
                numIOVs++;
            }

            if (reqLen > 0) {
                if (verbose > 1)
                    fwrite(buf, 1, reqLen, stdout); /* display it */

                iovs[numIOVs].iov_base = buf;
                iovs[numIOVs].iov_len = reqLen;
                numIOVs++;
            }

            rv = PR_Writev(ssl_sock, iovs, numIOVs, PR_INTERVAL_NO_TIMEOUT);
            if (rv < 0) {
                errWarn("PR_Writev");
                break;
            }

        } while (0);