SECComparison SGN_CompareDigestInfo(SGNDigestInfo *a, SGNDigestInfo *b) { SECComparison rv; /* Check signature algorithm's */ rv = SECOID_CompareAlgorithmID(&a->digestAlgorithm, &b->digestAlgorithm); if (rv) return rv; /* Compare signature block length's */ rv = SECITEM_CompareItem(&a->digest, &b->digest); return rv; }
/* * SecCmsAlgArrayGetIndexByAlgID - find a specific algorithm in an array of * algorithms. * * algorithmArray - array of algorithm IDs * algid - algorithmid of algorithm to pick * * Returns: * An integer containing the index of the algorithm in the array or -1 if * algorithm was not found. */ int SecCmsAlgArrayGetIndexByAlgID(SECAlgorithmID **algorithmArray, SECAlgorithmID *algid) { int i; if (algorithmArray == NULL || algorithmArray[0] == NULL) return -1; for (i = 0; algorithmArray[i] != NULL; i++) { if (SECOID_CompareAlgorithmID(algorithmArray[i], algid) == SECEqual) break; /* bingo */ } if (algorithmArray[i] == NULL) return -1; /* not found */ return i; }
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);
int main(int argc, char **argv) { int verbose=0, force=0; int ascii=0, issuerAscii=0; char *progName=0; PRFileDesc *inFile=0, *issuerCertFile=0; SECItem derCert, derIssuerCert; PLArenaPool *arena=0; CERTSignedData *signedData=0; CERTCertificate *cert=0, *issuerCert=0; SECKEYPublicKey *rsapubkey=0; SECAlgorithmID md5WithRSAEncryption, md2WithRSAEncryption; SECAlgorithmID sha1WithRSAEncryption, rsaEncryption; SECItem spk; int selfSigned=0; int invalid=0; char *inFileName = NULL, *issuerCertFileName = NULL; PLOptState *optstate; PLOptStatus status; SECStatus rv; PORT_Memset(&md5WithRSAEncryption, 0, sizeof(md5WithRSAEncryption)); PORT_Memset(&md2WithRSAEncryption, 0, sizeof(md2WithRSAEncryption)); PORT_Memset(&sha1WithRSAEncryption, 0, sizeof(sha1WithRSAEncryption)); PORT_Memset(&rsaEncryption, 0, sizeof(rsaEncryption)); progName = strrchr(argv[0], '/'); progName = progName ? progName+1 : argv[0]; optstate = PL_CreateOptState(argc, argv, "aAvf"); while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { switch (optstate->option) { case 'v': verbose = 1; break; case 'f': force = 1; break; case 'a': ascii = 1; break; case 'A': issuerAscii = 1; break; case '\0': if (!inFileName) inFileName = PL_strdup(optstate->value); else if (!issuerCertFileName) issuerCertFileName = PL_strdup(optstate->value); else Usage(progName); break; } } if (!inFileName || !issuerCertFileName || status == PL_OPT_BAD) { /* insufficient or excess args */ Usage(progName); } inFile = PR_Open(inFileName, PR_RDONLY, 0); if (!inFile) { fprintf(stderr, "%s: unable to open \"%s\" for reading\n", progName, inFileName); exit(1); } issuerCertFile = PR_Open(issuerCertFileName, PR_RDONLY, 0); if (!issuerCertFile) { fprintf(stderr, "%s: unable to open \"%s\" for reading\n", progName, issuerCertFileName); exit(1); } if (SECU_ReadDERFromFile(&derCert, inFile, ascii, PR_FALSE) != SECSuccess) { printf("Couldn't read input certificate as DER binary or base64\n"); exit(1); } arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (arena == 0) { fprintf(stderr,"%s: can't allocate scratch arena!", progName); exit(1); } if (issuerCertFile) { CERTSignedData *issuerCertSD=0; if (SECU_ReadDERFromFile(&derIssuerCert, issuerCertFile, issuerAscii, PR_FALSE) != SECSuccess) { printf("Couldn't read issuer certificate as DER binary or base64.\n"); exit(1); } issuerCertSD = PORT_ArenaZNew(arena, CERTSignedData); if (!issuerCertSD) { fprintf(stderr,"%s: can't allocate issuer signed data!", progName); exit(1); } rv = SEC_ASN1DecodeItem(arena, issuerCertSD, SEC_ASN1_GET(CERT_SignedDataTemplate), &derIssuerCert); if (rv) { fprintf(stderr, "%s: Issuer cert isn't X509 SIGNED Data?\n", progName); exit(1); } issuerCert = createEmptyCertificate(); if (!issuerCert) { printf("%s: can't allocate space for issuer cert.", progName); exit(1); } rv = SEC_ASN1DecodeItem(arena, issuerCert, SEC_ASN1_GET(CERT_CertificateTemplate), &issuerCertSD->data); if (rv) { printf("%s: Does not appear to be an X509 Certificate.\n", progName); exit(1); } } signedData = PORT_ArenaZNew(arena,CERTSignedData); if (!signedData) { fprintf(stderr,"%s: can't allocate signedData!", progName); exit(1); } rv = SEC_ASN1DecodeItem(arena, signedData, SEC_ASN1_GET(CERT_SignedDataTemplate), &derCert); if (rv) { fprintf(stderr, "%s: Does not appear to be X509 SIGNED Data.\n", progName); exit(1); } if (verbose) { printf("Decoded ok as X509 SIGNED data.\n"); } cert = createEmptyCertificate(); if (!cert) { fprintf(stderr, "%s: can't allocate cert", progName); exit(1); } rv = SEC_ASN1DecodeItem(arena, cert, SEC_ASN1_GET(CERT_CertificateTemplate), &signedData->data); if (rv) { fprintf(stderr, "%s: Does not appear to be an X509 Certificate.\n", progName); exit(1); } if (verbose) { printf("Decoded ok as an X509 certificate.\n"); } SECU_RegisterDynamicOids(); rv = SECU_PrintSignedData(stdout, &derCert, "Certificate", 0, (SECU_PPFunc)SECU_PrintCertificate); if (rv) { fprintf(stderr, "%s: Unable to pretty print cert. Error: %d\n", progName, PORT_GetError()); if (!force) { exit(1); } } /* Do various checks on the cert */ printf("\n"); /* Check algorithms */ rv = SECOID_SetAlgorithmID(arena, &md5WithRSAEncryption, SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION, NULL); if (rv) { fprintf(stderr, "%s: failed to set algorithm ID for SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION.\n", progName); exit(1); } rv = SECOID_SetAlgorithmID(arena, &md2WithRSAEncryption, SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION, NULL); if (rv) { fprintf(stderr, "%s: failed to set algorithm ID for SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION.\n", progName); exit(1); } rv = SECOID_SetAlgorithmID(arena, &sha1WithRSAEncryption, SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION, NULL); if (rv) { fprintf(stderr, "%s: failed to set algorithm ID for SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION.\n", progName); exit(1); } rv = SECOID_SetAlgorithmID(arena, &rsaEncryption, SEC_OID_PKCS1_RSA_ENCRYPTION, NULL); if (rv) { fprintf(stderr, "%s: failed to set algorithm ID for SEC_OID_PKCS1_RSA_ENCRYPTION.\n", progName); exit(1); } { int isMD5RSA = (SECOID_CompareAlgorithmID(&cert->signature, &md5WithRSAEncryption) == 0); int isMD2RSA = (SECOID_CompareAlgorithmID(&cert->signature, &md2WithRSAEncryption) == 0); int isSHA1RSA = (SECOID_CompareAlgorithmID(&cert->signature, &sha1WithRSAEncryption) == 0); if (verbose) { printf("\nDoing algorithm checks.\n"); } if (!(isMD5RSA || isMD2RSA || isSHA1RSA)) { printf("PROBLEM: Signature not PKCS1 MD5, MD2, or SHA1 + RSA.\n"); } else if (!isMD5RSA) { printf("WARNING: Signature not PKCS1 MD5 with RSA Encryption\n"); } if (SECOID_CompareAlgorithmID(&cert->signature, &signedData->signatureAlgorithm)) { printf("PROBLEM: Algorithm in sig and certInfo don't match.\n"); } } if (SECOID_CompareAlgorithmID(&cert->subjectPublicKeyInfo.algorithm, &rsaEncryption)) { printf("PROBLEM: Public key algorithm is not PKCS1 RSA Encryption.\n"); } /* Check further public key properties */ spk = cert->subjectPublicKeyInfo.subjectPublicKey; DER_ConvertBitString(&spk); if (verbose) { printf("\nsubjectPublicKey DER\n"); rv = DER_PrettyPrint(stdout, &spk, PR_FALSE); printf("\n"); } rsapubkey = (SECKEYPublicKey *) PORT_ArenaZAlloc(arena,sizeof(SECKEYPublicKey)); if (!rsapubkey) { fprintf(stderr, "%s: rsapubkey allocation failed.\n", progName); exit(1); } rv = SEC_ASN1DecodeItem(arena, rsapubkey, SEC_ASN1_GET(SECKEY_RSAPublicKeyTemplate), &spk); if (rv) { printf("PROBLEM: subjectPublicKey is not a DER PKCS1 RSAPublicKey.\n"); } else { int mlen; int pubexp; if (verbose) { printf("Decoded RSA Public Key ok. Doing key checks.\n"); } PORT_Assert(rsapubkey->keyType == rsaKey); /* XXX RSA */ mlen = checkInteger(&rsapubkey->u.rsa.modulus, "Modulus", verbose); printf("INFO: Public Key modulus length in bits: %d\n", mlen); if (mlen > MAX_MODULUS) { printf("PROBLEM: Modulus length exceeds %d bits.\n", MAX_MODULUS); } if (mlen < 512) { printf("WARNING: Short modulus.\n"); } if (mlen != (1 << (ffs(mlen)-1))) { printf("WARNING: Unusual modulus length (not a power of two).\n"); } checkInteger(&rsapubkey->u.rsa.publicExponent, "Public Exponent", verbose); pubexp = DER_GetInteger(&rsapubkey->u.rsa.publicExponent); if (pubexp != 17 && pubexp != 3 && pubexp != 65537) { printf("WARNING: Public exponent not any of: 3, 17, 65537\n"); } } /* Name checks */ checkName(&cert->issuer, "Issuer Name", verbose); checkName(&cert->subject, "Subject Name", verbose); if (issuerCert) { SECComparison c = CERT_CompareName(&cert->issuer, &issuerCert->subject); if (c) { printf("PROBLEM: Issuer Name and Subject in Issuing Cert differ\n"); } } /* Check if self-signed */ selfSigned = (CERT_CompareName(&cert->issuer, &cert->subject) == 0); if (selfSigned) { printf("INFO: Certificate is self signed.\n"); } else { printf("INFO: Certificate is NOT self-signed.\n"); } /* Validity time check */ if (CERT_CertTimesValid(cert) == SECSuccess) { printf("INFO: Inside validity period of certificate.\n"); } else { printf("PROBLEM: Not in validity period of certificate.\n"); invalid = 1; } /* Signature check if self-signed */ if (selfSigned && !invalid) { if (rsapubkey->u.rsa.modulus.len) { SECStatus ver; if (verbose) { printf("Checking self signature.\n"); } ver = OurVerifySignedData(signedData, cert); if (ver != SECSuccess) { printf("PROBLEM: Verification of self-signature failed!\n"); } else { printf("INFO: Self-signature verifies ok.\n"); } } else { printf("INFO: Not checking signature due to key problems.\n"); } } else if (!selfSigned && !invalid && issuerCert) { SECStatus ver; ver = OurVerifySignedData(signedData, issuerCert); if (ver != SECSuccess) { printf("PROBLEM: Verification of issuer's signature failed!\n"); } else { printf("INFO: Issuer's signature verifies ok.\n"); } } else { printf("INFO: Not checking signature.\n"); } return 0; }