/* NEED LOCKS IN HERE. */ SECStatus SSL_SecurityStatus(PRFileDesc *fd, int *op, char **cp, int *kp0, int *kp1, char **ip, char **sp) { sslSocket *ss; const char *cipherName; PRBool isDes = PR_FALSE; ss = ssl_FindSocket(fd); if (!ss) { SSL_DBG(("%d: SSL[%d]: bad socket in SecurityStatus", SSL_GETPID(), fd)); return SECFailure; } if (cp) *cp = 0; if (kp0) *kp0 = 0; if (kp1) *kp1 = 0; if (ip) *ip = 0; if (sp) *sp = 0; if (op) { *op = SSL_SECURITY_STATUS_OFF; } if (ss->opt.useSecurity && ss->enoughFirstHsDone) { if (ss->version < SSL_LIBRARY_VERSION_3_0) { cipherName = ssl_cipherName[ss->sec.cipherType]; } else { cipherName = ssl3_cipherName[ss->sec.cipherType]; } PORT_Assert(cipherName); if (cipherName) { if (PORT_Strstr(cipherName, "DES")) isDes = PR_TRUE; if (cp) { *cp = PORT_Strdup(cipherName); } } if (kp0) { *kp0 = ss->sec.keyBits; if (isDes) *kp0 = (*kp0 * 7) / 8; } if (kp1) { *kp1 = ss->sec.secretKeyBits; if (isDes) *kp1 = (*kp1 * 7) / 8; } if (op) { if (ss->sec.keyBits == 0) { *op = SSL_SECURITY_STATUS_OFF; } else if (ss->sec.secretKeyBits < 90) { *op = SSL_SECURITY_STATUS_ON_LOW; } else { *op = SSL_SECURITY_STATUS_ON_HIGH; } } if (ip || sp) { CERTCertificate *cert; cert = ss->sec.peerCert; if (cert) { if (ip) { *ip = CERT_NameToAscii(&cert->issuer); } if (sp) { *sp = CERT_NameToAscii(&cert->subject); } } else { if (ip) { *ip = PORT_Strdup("no certificate"); } if (sp) { *sp = PORT_Strdup("no certificate"); } } } } return SECSuccess; }
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);
SECStatus parseRevMethodsAndFlags() { int i; uint testType = 0; for(i = 0;i < REV_METHOD_INDEX_MAX;i++) { /* testType */ if (revMethodsData[i].testTypeStr) { char *typeStr = revMethodsData[i].testTypeStr; testType = 0; if (!PORT_Strcmp(typeStr, REVCONFIG_TEST_LEAF_STR)) { testType = REVCONFIG_TEST_LEAF; } else if (!PORT_Strcmp(typeStr, REVCONFIG_TEST_CHAIN_STR)) { testType = REVCONFIG_TEST_CHAIN; } } if (!testType) { return SECFailure; } revMethodsData[i].testType = testType; /* testFlags */ if (revMethodsData[i].testFlagsStr) { char *flagStr = revMethodsData[i].testFlagsStr; uint testFlags = 0; if (PORT_Strstr(flagStr, REVCONFIG_TEST_TESTLOCALINFOFIRST_STR)) { testFlags |= CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST; } if (PORT_Strstr(flagStr, REVCONFIG_TEST_REQUIREFRESHINFO_STR)) { testFlags |= CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE; } revMethodsData[i].testFlags = testFlags; } /* method type */ if (revMethodsData[i].methodTypeStr) { char *methodStr = revMethodsData[i].methodTypeStr; uint methodType = 0; if (!PORT_Strcmp(methodStr, REVCONFIG_METHOD_CRL_STR)) { methodType = REVCONFIG_METHOD_CRL; } else if (!PORT_Strcmp(methodStr, REVCONFIG_METHOD_OCSP_STR)) { methodType = REVCONFIG_METHOD_OCSP; } if (!methodType) { return SECFailure; } revMethodsData[i].methodType = methodType; } if (!revMethodsData[i].methodType) { revMethodsData[i].testType = REVCONFIG_TEST_UNDEFINED; continue; } /* method flags */ if (revMethodsData[i].methodFlagsStr) { char *flagStr = revMethodsData[i].methodFlagsStr; uint methodFlags = 0; if (!PORT_Strstr(flagStr, REVCONFIG_METHOD_DONOTUSEMETHOD_STR)) { methodFlags |= CERT_REV_M_TEST_USING_THIS_METHOD; } if (PORT_Strstr(flagStr, REVCONFIG_METHOD_FORBIDNETWORKFETCHIN_STR)) { methodFlags |= CERT_REV_M_FORBID_NETWORK_FETCHING; } if (PORT_Strstr(flagStr, REVCONFIG_METHOD_IGNOREDEFAULTSRC_STR)) { methodFlags |= CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE; } if (PORT_Strstr(flagStr, REVCONFIG_METHOD_REQUIREINFO_STR)) { methodFlags |= CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE; } if (PORT_Strstr(flagStr, REVCONFIG_METHOD_FAILIFNOINFO_STR)) { methodFlags |= CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO; } revMethodsData[i].methodFlags = methodFlags; } else { revMethodsData[i].methodFlags |= CERT_REV_M_TEST_USING_THIS_METHOD; } } return SECSuccess; }
int handle_connection( PRFileDesc *tcp_sock, PRFileDesc *model_sock, int requestCert ) { PRFileDesc * ssl_sock = NULL; PRFileDesc * local_file_fd = NULL; char * post; 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]; pBuf = buf; bufRem = sizeof buf; VLOG(("selfserv: handle_connection: starting")); opt.option = PR_SockOpt_Nonblocking; opt.value.non_blocking = PR_FALSE; PR_SetSocketOption(tcp_sock, &opt); VLOG(("selfserv: 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) { 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; /* It's a post, so look for the next and final CR/LF. */ /* We should parse content length here, but ... */ 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 && !strncmp(buf, getCmd, sizeof getCmd - 1)) { char * fnBegin = buf + 4; char * fnEnd; PRFileInfo info; /* try to open the file named. * If successful, then write it to the client. */ fnEnd = strpbrk(fnBegin, " \r\n"); if (fnEnd) { int fnLen = fnEnd - fnBegin; if (fnLen < sizeof fileName) { char *fnstart; strncpy(fileName, fnBegin, fnLen); fileName[fnLen] = 0; /* null terminate */ fnstart = fileName; /* strip initial / because our root is the current directory*/ while (*fnstart && *fnstart=='/') ++fnstart; 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 (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, "selfserv: 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++; } /* Don't add the EOF if we want to test bulk encryption */ if (!testBulk) { iovs[numIOVs].iov_base = (char *)EOFmsg; iovs[numIOVs].iov_len = sizeof EOFmsg - 1; numIOVs++; } rv = PR_Writev(ssl_sock, iovs, numIOVs, PR_INTERVAL_NO_TIMEOUT); if (rv < 0) { errWarn("PR_Writev"); break; } } while (0); cleanup: if (ssl_sock) { PR_Close(ssl_sock); } else if (tcp_sock) { PR_Close(tcp_sock); } if (local_file_fd) PR_Close(local_file_fd); VLOG(("selfserv: handle_connection: exiting\n")); /* do a nice shutdown if asked. */ if (!strncmp(buf, stopCmd, sizeof stopCmd - 1)) { VLOG(("selfserv: handle_connection: stop command")); stop_server(); } VLOG(("selfserv: handle_connection: exiting")); return SECSuccess; /* success */ }