sslcon.c

来自「支持SSL v2/v3, TLS, PKCS #5, PKCS #7, PKCS」· C语言 代码 · 共 2,213 行 · 第 1/5 页

C
2,213
字号
*  We MUST NOT match on any of those.  *  Fortunately, this is easy to detect because SSLv3 ciphers have zero*  in the first byte, and none of the SSLv2 ciphers do.**  Called from ssl2_HandleClientHelloMessage().*/static intssl2_QualifyCypherSpecs(sslSocket *ss,                         PRUint8 *  cs, /* cipher specs in client hello msg. */		        int        csLen){    PRUint8 *    ms;    PRUint8 *    hs;    PRUint8 *    qs;    int          mc;    int          hc;    PRUint8      qualifiedSpecs[ssl2_NUM_SUITES_IMPLEMENTED * 3];    PORT_Assert( ssl_Have1stHandshakeLock(ss) );    PORT_Assert( ssl_HaveRecvBufLock(ss)   );    if (!ss->cipherSpecs) {	ssl2_ConstructCipherSpecs(ss);    }    PRINT_BUF(10, (ss, "specs from client:", cs, csLen));    qs = qualifiedSpecs;    ms = ss->cipherSpecs;    for (mc = ss->sizeCipherSpecs; mc > 0; mc -= 3, ms += 3) {	if (ms[0] == 0)	    continue;	for (hs = cs, hc = csLen; hc > 0; hs += 3, hc -= 3) {	    if ((hs[0] == ms[0]) &&		(hs[1] == ms[1]) &&		(hs[2] == ms[2])) {		/* Copy this cipher spec into the "keep" section */		qs[0] = hs[0];		qs[1] = hs[1];		qs[2] = hs[2];		qs   += 3;		break;	    }	}    }    hc = qs - qualifiedSpecs;    PRINT_BUF(10, (ss, "qualified specs from client:", qualifiedSpecs, hc));    PORT_Memcpy(cs, qualifiedSpecs, hc);    return hc;}/*** Pick the best cipher we can find, given the array of server cipher** specs.  Returns cipher number (e.g. SSL_CK_*), or -1 for no overlap.** If succesful, stores the master key size (bytes) in *pKeyLen.**** This is correct only for the client side, but presently** this function is only called from **	ssl2_ClientSetupSessionCypher() <- ssl2_HandleServerHelloMessage()**** Note that most servers only return a single cipher suite in their ** ServerHello messages.  So, the code below for finding the "best" cipher** suite usually has only one choice.  The client and server should send ** their cipher suite lists sorted in descending order by preference.*/static intssl2_ChooseSessionCypher(sslSocket *ss,                          int        hc,    /* number of cs's in hs. */		         PRUint8 *  hs,    /* server hello's cipher suites. */		         int *      pKeyLen) /* out: sym key size in bytes. */{    PRUint8 *       ms;    unsigned int    i;    int             bestKeySize;    int             bestRealKeySize;    int             bestCypher;    int             keySize;    int             realKeySize;    PRUint8 *       ohs               = hs;    PORT_Assert( ssl_Have1stHandshakeLock(ss) );    PORT_Assert( ssl_HaveRecvBufLock(ss)   );    if (!ss->cipherSpecs) {	ssl2_ConstructCipherSpecs(ss);    }    if (!ss->preferredCipher) {	const PRUint8 * preferred = implementedCipherSuites;    	unsigned int    allowed = ss->allowedByPolicy & ss->chosenPreference &	                       SSL_CB_IMPLEMENTED;	if (allowed) {	    for (i = ssl2_NUM_SUITES_IMPLEMENTED; i > 0; --i) {		if (0 != (allowed & (1U << preferred[0]))) {		    ss->preferredCipher = preferred;		    break;		}		preferred += 3;	    }	}    }    /*    ** Scan list of ciphers recieved from peer and look for a match in    ** our list.      *  Note: Our list may contain SSL v3 ciphers.      *  We MUST NOT match on any of those.      *  Fortunately, this is easy to detect because SSLv3 ciphers have zero    *  in the first byte, and none of the SSLv2 ciphers do.    */    bestKeySize = bestRealKeySize = 0;    bestCypher = -1;    while (--hc >= 0) {	for (i = 0, ms = ss->cipherSpecs; i < ss->sizeCipherSpecs; i += 3, ms += 3) {	    if ((hs[0] == ss->preferredCipher[0]) &&		(hs[1] == ss->preferredCipher[1]) &&		(hs[2] == ss->preferredCipher[2]) &&		 hs[0] != 0) {		/* Pick this cipher immediately! */		*pKeyLen = (((hs[1] << 8) | hs[2]) + 7) >> 3;		return hs[0];	    }	    if ((hs[0] == ms[0]) && (hs[1] == ms[1]) && (hs[2] == ms[2]) &&	         hs[0] != 0) {		/* Found a match */		/* Use secret keySize to determine which cipher is best */		realKeySize = (hs[1] << 8) | hs[2];		switch (hs[0]) {		  case SSL_CK_RC4_128_EXPORT40_WITH_MD5:		  case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5:		    keySize = 40;		    break;		  default:		    keySize = realKeySize;		    break;		}		if (keySize > bestKeySize) {		    bestCypher = hs[0];		    bestKeySize = keySize;		    bestRealKeySize = realKeySize;		}	    }	}	hs += 3;    }    if (bestCypher < 0) {	/*	** No overlap between server and client. Re-examine server list	** to see what kind of ciphers it does support so that we can set	** the error code appropriately.	*/	if ((ohs[0] == SSL_CK_RC4_128_WITH_MD5) ||	    (ohs[0] == SSL_CK_RC2_128_CBC_WITH_MD5)) {	    PORT_SetError(SSL_ERROR_US_ONLY_SERVER);	} else if ((ohs[0] == SSL_CK_RC4_128_EXPORT40_WITH_MD5) ||		   (ohs[0] == SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5)) {	    PORT_SetError(SSL_ERROR_EXPORT_ONLY_SERVER);	} else {	    PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);	}	SSL_DBG(("%d: SSL[%d]: no cipher overlap", SSL_GETPID(), ss->fd));	goto loser;    }    *pKeyLen = (bestRealKeySize + 7) >> 3;    return bestCypher;  loser:    return -1;}static SECStatusssl2_ClientHandleServerCert(sslSocket *ss, PRUint8 *certData, int certLen){    CERTCertificate *cert      = NULL;    SECItem          certItem;    PORT_Assert(ss->sec != 0);    certItem.data = certData;    certItem.len  = certLen;    /* decode the certificate */    cert = CERT_NewTempCertificate(ss->dbHandle, &certItem, NULL,				   PR_FALSE, PR_TRUE);        if (cert == NULL) {	SSL_DBG(("%d: SSL[%d]: decode of server certificate fails",		 SSL_GETPID(), ss->fd));	PORT_SetError(SSL_ERROR_BAD_CERTIFICATE);	return SECFailure;    }#ifdef TRACE    {	if (ssl_trace >= 1) {	    char *issuer;	    char *subject;	    issuer = CERT_NameToAscii(&cert->issuer);	    subject = CERT_NameToAscii(&cert->subject);	    SSL_TRC(1,("%d: server certificate issuer: '%s'",		       SSL_GETPID(), issuer ? issuer : "OOPS"));	    SSL_TRC(1,("%d: server name: '%s'",		       SSL_GETPID(), subject ? subject : "OOPS"));	    PORT_Free(issuer);	    PORT_Free(subject);	}    }#endif    ss->sec->peerCert = cert;    return SECSuccess;}/*** Given the server's public key and cipher specs, generate a session key** that is ready to use for encrypting/decrypting the byte stream. At** the same time, generate the SSL_MT_CLIENT_MASTER_KEY message and** send it to the server.**** Called from ssl2_HandleServerHelloMessage()*/static SECStatusssl2_ClientSetupSessionCypher(sslSocket *ss, PRUint8 *cs, int csLen){    sslSessionID *    sid;    PRUint8 *         ca;	/* points to iv data, or NULL if none. */    PRUint8 *         ekbuf 		= 0;    CERTCertificate * cert 		= 0;    SECKEYPublicKey * serverKey 	= 0;    unsigned          modulusLen 	= 0;    SECStatus         rv;    int               cipher;    int               keyLen;	/* cipher symkey size in bytes. */    int               ckLen;	/* publicly reveal this many bytes of key. */    int               caLen;	/* length of IV data at *ca.	*/    int               nc;    SECItem           eblock;	/* holds unencrypted PKCS#1 formatted key. */    SECItem           rek;	/* holds portion of symkey to be encrypted. */    PRUint8           keyData[SSL_MAX_MASTER_KEY_BYTES];    PRUint8           iv     [8];    PORT_Assert( ssl_Have1stHandshakeLock(ss) );    PORT_Assert((ss->sec != 0));    eblock.data = 0;    eblock.len  = 0;    sid = ss->sec->ci.sid;    PORT_Assert(sid != 0);    cert = ss->sec->peerCert;        serverKey = CERT_ExtractPublicKey(cert);    if (!serverKey) {	SSL_DBG(("%d: SSL[%d]: extract public key failed: error=%d",		 SSL_GETPID(), ss->fd, PORT_GetError()));	PORT_SetError(SSL_ERROR_BAD_CERTIFICATE);	rv = SECFailure;	goto loser2;    }    /* Choose a compatible cipher with the server */    nc = csLen / 3;    cipher = ssl2_ChooseSessionCypher(ss, nc, cs, &keyLen);    if (cipher < 0) {	/* ssl2_ChooseSessionCypher has set error code. */	ssl2_SendErrorMessage(ss, SSL_PE_NO_CYPHERS);	goto loser;    }    /* Generate the random keys */    PK11_GenerateRandom(keyData, sizeof(keyData));    /*    ** Next, carve up the keys into clear and encrypted portions. The    ** clear data is taken from the start of keyData and the encrypted    ** portion from the remainder. Note that each of these portions is    ** carved in half, one half for the read-key and one for the    ** write-key.    */    ca = 0;    /* We know that cipher is a legit value here, because      * ssl2_ChooseSessionCypher doesn't return bogus values.     */    ckLen = ssl_Specs[cipher].pubLen;	/* cleartext key length. */    caLen = ssl_Specs[cipher].ivLen;	/* IV length.		*/    if (caLen) {	PORT_Assert(sizeof iv >= caLen);    	PK11_GenerateRandom(iv, caLen);	ca = iv;    }    /* Fill in session-id */    rv = ssl2_FillInSID(sid, cipher, keyData, keyLen,		   ca, caLen, keyLen << 3, (keyLen - ckLen) << 3);    if (rv != SECSuccess) {	goto loser;    }    SSL_TRC(1, ("%d: SSL[%d]: client, using %s cipher, clear=%d total=%d",		SSL_GETPID(), ss->fd, ssl_cipherName[cipher],		ckLen<<3, keyLen<<3));    /* Now setup read and write ciphers */    rv = ssl2_CreateSessionCypher(ss, sid, PR_TRUE);    if (rv != SECSuccess) {	goto loser;    }    /*    ** Fill in the encryption buffer with some random bytes. Then     ** copy in the portion of the session key we are encrypting.    */    modulusLen = SECKEY_PublicKeyStrength(serverKey);    rek.data   = keyData + ckLen;    rek.len    = keyLen  - ckLen;    rv = RSA_FormatBlock(&eblock, modulusLen, RSA_BlockPublic, &rek);    if (rv)     	goto loser;    /* Set up the padding for version 2 rollback detection. */    /* XXX We should really use defines here */    if (ss->enableSSL3 || ss->enableTLS) {	PORT_Assert((modulusLen - rek.len) > 12);	PORT_Memset(eblock.data + modulusLen - rek.len - 8 - 1, 0x03, 8);    }    ekbuf = (PRUint8*) PORT_Alloc(modulusLen);    if (!ekbuf) 	goto loser;    PRINT_BUF(10, (ss, "master key encryption block:",		   eblock.data, eblock.len));    /* Encrypt ekitem */    rv = PK11_PubEncryptRaw(serverKey, ekbuf, eblock.data, modulusLen,						ss->pkcs11PinArg);    if (rv)     	goto loser;    if (eblock.len != modulusLen) {	/* Something strange just happened */	PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);	goto loser;    }    /*  Now we have everything ready to send */    rv = ssl2_SendSessionKeyMessage(ss, cipher, keyLen << 3, ca, caLen,			       keyData, ckLen, ekbuf, modulusLen);    if (rv != SECSuccess) {	goto loser;    }    rv = SECSuccess;    goto done;  loser:    rv = SECFailure;  loser2:  done:    PORT_Memset(keyData, 0, sizeof(keyData));    PORT_ZFree(ekbuf, modulusLen);    SECITEM_ZfreeItem(&eblock, PR_FALSE);    SECKEY_DestroyPublicKey(serverKey);    return rv;}/************************************************************************//*  * Called from ssl2_HandleMessage in response to SSL_MT_SERVER_FINISHED message. * Caller holds recvBufLock and handshakeLock */static voidssl2_ClientRegSessionID(sslSocket *ss, PRUint8 *s){    sslSecurityInfo *sec;    sslSessionID *sid;    PORT_Assert((ss->sec != 0));    sec = ss->sec;    sid = sec->ci.sid;    /* Record entry in nonce cache */    if (sid->peerCert == NULL) {	PORT_Memcpy(sid->u.ssl2.sessionID, s, sizeof(sid->u.ssl2.sessionID));	sid->peerCert = CERT_DupCertificate(sec->peerCert);    }    if (!ss->noCache)	(*sec->cache)(sid);}/* Called from ssl2_HandleMessage() */static SECStatusssl2_TriggerNextMessage(sslSocket *ss){    sslConnectInfo * ci;    SECStatus        rv;    PORT_Assert( ssl_Have1stHandshakeLock(ss) );    PORT_Assert((ss->sec != 0));    ci = &ss->sec->ci;    if ((ci->requiredElements & CIS_HAVE_CERTIFICATE) &&	!(ci->sentElements & CIS_HAVE_CERTIFICATE)) {	ci->sentElements |= CIS_HAVE_CERTIFICATE;	rv = ssl2_SendCertificateRequestMessage(ss);	return rv;    }    return SECSuccess;}/* See if it's time to send our finished message, or if the handshakes are** complete.  Send finished message if appropriate.** Returns SECSuccess unless anything goes wrong.**** Called from ssl2_HandleMessage,**             ssl2_HandleVerifyMessage **             ssl2_HandleServerHelloMessage**             ssl2_HandleClientSessionKeyMessage**             ssl2_RestartHandshakeAfterCertReq**             ssl2_RestartHandshakeAfterServerCert*/static SECStatusssl2_TryToFinish(sslSocket *ss){    sslSecurityInfo *sec;    sslConnectInfo * ci;    SECStatus        rv;    char             e, ef;    PORT_Assert( ssl_Have1stHandshakeLock(ss) );    PORT_Assert((ss->sec != 0));    sec = ss->sec;    ci  = &sec->ci;    e = ci->elements;    ef = e | CIS_HAVE_FINISHED;    if ((ef & ci->requiredElements

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?