📄 sslconn.c
字号:
* Function: SECStatus SSM_SSLBadCertHandler() * Purpose: this callback function is used to handle a situation in which * server certificate verification has failed. * * Arguments and return values * - arg: the SSL data connection * - socket: SSL socket * - returns: SECSuccess if the case is handled and it is authorized to * continue connection; otherwise SECFailure */SECStatus SSM_SSLBadCertHandler(void* arg, PRFileDesc* socket){ int error; SSMSSLDataConnection* conn; CERTCertificate* cert = NULL; SECStatus rv = SECFailure; if (socket == NULL || arg == NULL) { return SECFailure; } conn = (SSMSSLDataConnection*)arg; if (SSMCONTROLCONNECTION(conn)->m_doesUI == PR_FALSE) { /* UI-less application. We choose to reject the server cert. */ goto loser; } cert = SSL_PeerCertificate(conn->socketSSL); if (cert == NULL) { goto loser; } while (rv != SECSuccess) { error = PR_GetError(); /* first of all, get error code */ SSM_DEBUG("Got a bad server cert: error %d (%s).\n", error, SSL_Strerror(error)); /* Save error for socket status */ conn->m_sslServerError = error; if (SSM_SSLErrorNeedsDialog(error) == PR_FALSE) { SSM_DEBUG("Exiting abnormally...\n"); break; } if (SSM_SSLMakeBadServerCertDialog(error, cert, conn) != SECSuccess) { break; } /* check the server cert again for more errors */ rv = SSM_DoubleCheckServerCert(cert, conn); }loser: if (rv != SECSuccess) { (void)SSMSSLDataConnection_UpdateErrorCode(conn); } if (cert != NULL) { CERT_DestroyCertificate(cert); } return rv;}/* * Function: SECStatus SSM_SSLGetClientAuthData() * Purpose: this callback function is used to pull client certificate * information upon server request * * Arguments and return values * - arg: SSL data connection * - socket: SSL socket we're dealing with * - caNames: list of CA names * - pRetCert: returns a pointer to a pointer to a valid certificate if * successful; otherwise NULL * - pRetKey: returns a pointer to a pointer to the corresponding key if * successful; otherwise NULL * - returns: SECSuccess if successful; error code otherwise */SECStatus SSM_SSLGetClientAuthData(void* arg, PRFileDesc* socket, CERTDistNames* caNames, CERTCertificate** pRetCert, SECKEYPrivateKey** pRetKey){ void* wincx = NULL; SECStatus rv = SECFailure; SSMSSLDataConnection* conn; SSMControlConnection* ctrlconn; PRArenaPool* arena = NULL; char** caNameStrings = NULL; CERTCertificate* cert = NULL; SECKEYPrivateKey* privKey = NULL; CERTCertList* certList = NULL; CERTCertListNode* node; CERTCertNicknames* nicknames = NULL; char* extracted = NULL; PRIntn keyError = 0; /* used for private key retrieval error */ SSM_DEBUG("Client authentication callback function called.\n"); /* do some argument checking */ if (socket == NULL || caNames == NULL || pRetCert == NULL || pRetKey == NULL) { rv = (SECStatus) SEC_ERROR_INVALID_ARGS; return rv; } /* get PKCS11 pin argument */ wincx = SSL_RevealPinArg(socket); if (wincx == NULL) { return SECFailure; } /* get the right connections */ conn = (SSMSSLDataConnection*)wincx; ctrlconn = (SSMControlConnection*)(SSMCONNECTION(conn)->m_parent); PR_ASSERT(ctrlconn); /* create caNameStrings */ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (arena == NULL) { goto loser; } caNameStrings = (char**)PORT_ArenaAlloc(arena, sizeof(char*)*(caNames->nnames)); if (caNameStrings == NULL) { goto loser; } rv = SSM_ConvertCANamesToStrings(arena, caNameStrings, caNames); if (rv != SECSuccess) { goto loser; } /* get the preference */ if (SSM_SetUserCertChoice(conn) != SSM_SUCCESS) { goto loser; } /* find valid user cert and key pair */ if (certChoice == AUTO) { /* automatically find the right cert */ /* find all user certs that are valid and for SSL */ certList = CERT_FindUserCertsByUsage(ctrlconn->m_certdb, certUsageSSLClient, PR_TRUE, PR_TRUE, wincx); if (certList == NULL) { goto noCert; } /* filter the list to those issued by CAs supported by the server */ rv = CERT_FilterCertListByCANames(certList, caNames->nnames, caNameStrings, certUsageSSLClient); if (rv != SECSuccess) { goto noCert; } /* make sure the list is not empty */ node = CERT_LIST_HEAD(certList); if (CERT_LIST_END(node, certList)) { goto noCert; } /* loop through the list until we find a cert with a key */ while (!CERT_LIST_END(node, certList)) { privKey = PK11_FindKeyByAnyCert(node->cert, wincx); if (privKey != NULL) { /* this is a good cert to present */ cert = CERT_DupCertificate(node->cert); break; } keyError = PR_GetError(); if (keyError == SEC_ERROR_BAD_PASSWORD) { /* problem with password: bail */ goto loser; } node = CERT_LIST_NEXT(node); } if (cert == NULL) { rv = (SECStatus) SEC_ERROR_NO_KEY; goto noCert; } } else { /* user selects a cert to present */ int i; /* find all user certs that are valid and for SSL */ /* note that we are allowing expired certs in this list */ certList = CERT_FindUserCertsByUsage(ctrlconn->m_certdb, certUsageSSLClient, PR_TRUE, PR_FALSE, wincx); if (certList == NULL) { goto noCert; } if (caNames->nnames != 0) { /* filter the list to those issued by CAs supported by the * server */ rv = CERT_FilterCertListByCANames(certList, caNames->nnames, caNameStrings, certUsageSSLClient); if (rv != SECSuccess) { goto loser; } } if (CERT_LIST_END(CERT_LIST_HEAD(certList), certList)) { /* list is empty - no matching certs */ goto noCert; } nicknames = CERT_NicknameStringsFromCertList(certList, NICKNAME_EXPIRED_STRING, NICKNAME_NOT_YET_VALID_STRING); if (nicknames == NULL) { goto loser; } SSM_DEBUG("%d valid user certs found.\n", nicknames->numnicknames); conn->m_UIInfo.numFilteredCerts = nicknames->numnicknames; if (ssm_client_auth_prepare_nicknames(conn, nicknames) != SSM_SUCCESS) { goto loser; } /* create a cert selection dialog and get back the chosen nickname */ if (SSM_SSLMakeClientAuthDialog(conn) != SSM_SUCCESS) { goto loser; } PR_ASSERT(conn->m_UIInfo.chosen >= 0); i = conn->m_UIInfo.chosen; SSM_DEBUG("Cert %s was selected.\n", conn->m_UIInfo.certNicknames[i]); /* first we need to extract the real nickname in case the cert * is an expired or not a valid one yet */ extracted = CERT_ExtractNicknameString(conn->m_UIInfo.certNicknames[i], NICKNAME_EXPIRED_STRING, NICKNAME_NOT_YET_VALID_STRING); if (extracted == NULL) { goto loser; } /* find the cert under that nickname */ node = CERT_LIST_HEAD(certList); while (!CERT_LIST_END(node, certList)) { if (PL_strcmp(node->cert->nickname, extracted) == 0) { cert = CERT_DupCertificate(node->cert); break; } node = CERT_LIST_NEXT(node); } if (cert == NULL) { goto loser; } /* go get the private key */ privKey = PK11_FindKeyByAnyCert(cert, wincx); if (privKey == NULL) { keyError = PR_GetError(); if (keyError == SEC_ERROR_BAD_PASSWORD) { /* problem with password: bail */ goto loser; } else { goto noCert; } } } /* rv == SECSuccess */ SSM_DEBUG("Client auth complete.\n"); goto done;noCert: /* we display the no cert dialog only if the app is UI-capable */ if (SSMCONTROLCONNECTION(conn)->m_doesUI == PR_TRUE) { if (SSM_SSLMakeBadClientAuthDialog(conn) != SSM_SUCCESS) { SSM_DEBUG("client auth failure UI display failed.\n"); } }loser: if (rv == SECSuccess) { rv = SECFailure; } if (cert != NULL) { CERT_DestroyCertificate(cert); cert = NULL; }done: if (extracted != NULL) { PR_Free(extracted); } if (nicknames != NULL) { CERT_FreeNicknames(nicknames); } if (certList != NULL) { CERT_DestroyCertList(certList); } if (arena != NULL) { PORT_FreeArena(arena, PR_FALSE); } *pRetCert = cert; *pRetKey = privKey; return rv;}/* * Function: SSMStatus SSM_SetUserCertChoice() * Purpose: sets certChoice by reading the preference * * Arguments and return values * - conn: SSMSSLDataConnection * - returns: SSM_SUCCESS if successful; SSM_FAILURE otherwise * * Note: If done properly, this function will read the identifier strings * for ASK and AUTO modes, read the selected strings from the * preference, compare the strings, and determine in which mode it is * in. * We currently use ASK mode for UI apps and AUTO mode for UI-less * apps without really asking for preferences. */SSMStatus SSM_SetUserCertChoice(SSMSSLDataConnection* conn){ SSMStatus rv; SSMControlConnection* parent; parent = SSMCONTROLCONNECTION(conn); if (parent->m_doesUI == PR_TRUE) { char* mode = NULL; rv = PREF_GetStringPref(parent->m_prefs, "security.default_personal_cert", &mode); if (rv != PR_SUCCESS) { return SSM_FAILURE; } if (PL_strcmp(mode, "Select Automatically") == 0) { certChoice = AUTO; } else if (PL_strcmp(mode, "Ask Every Time") == 0) { certChoice = ASK; } else { return SSM_FAILURE; } } else { SSM_DEBUG("UI-less app: use auto cert selection.\n"); certChoice = AUTO; } return SSM_SUCCESS;}/* * Function: SECStatus SSM_ConvertCANamesToStrings() * Purpose: creates CA names strings from (CERTDistNames* caNames) * * Arguments and return values * - arena: arena to allocate strings on * - caNameStrings: filled with CA names strings on return * - caNames: CERTDistNames to extract strings from * - return: SECSuccess if successful; error code otherwise */SECStatus SSM_ConvertCANamesToStrings(PRArenaPool* arena, char** caNameStrings, CERTDistNames* caNames){ SECItem* dername; SECStatus rv; int headerlen; uint32 contentlen; SECItem newitem; int n; char* namestring; for (n = 0; n < caNames->nnames; n++) { newitem.data = NULL; dername = &caNames->names[n]; rv = DER_Lengths(dername, &headerlen, &contentlen); if (rv != SECSuccess) { goto loser; } if (headerlen + contentlen != dername->len) { if (dername->len <= 127) { newitem.data = (unsigned char *) PR_Malloc(dername->len + 2); if (newitem.data == NULL) { goto loser; } newitem.data[0] = (unsigned char)0x30; newitem.data[1] = (unsigned char)dername->len; (void)memcpy(&newitem.data[2], dername->data, dername->len); } else if (dername->len <= 255) { newitem.data = (unsigned char *) PR_Malloc(dername->len + 3); if (newitem.data == NULL) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -