📄 ssluse.c
字号:
/* Setup all the global SSL stuff */ SSLeay_add_ssl_algorithms();#else /* SSL disabled, do nothing */#endif}/* Global cleanup */void Curl_SSL_cleanup(void){#ifdef USE_SSLEAY if(init_ssl) { /* only cleanup if we did a previous init */ /* Free the SSL error strings */ ERR_free_strings(); /* EVP_cleanup() removes all ciphers and digests from the table. */ EVP_cleanup();#ifdef HAVE_ENGINE_cleanup ENGINE_cleanup();#endif#ifdef HAVE_CRYPTO_CLEANUP_ALL_EX_DATA /* this function was not present in 0.9.6b, but was added sometimes later */ CRYPTO_cleanup_all_ex_data();#endif init_ssl=0; /* not inited any more */ }#else /* SSL disabled, do nothing */#endif}#ifdef USE_SSLEAY/* * This function is called when an SSL connection is closed. */void Curl_SSL_Close(struct connectdata *conn){ if(conn->ssl.use) { /* ERR_remove_state() frees the error queue associated with thread pid. If pid == 0, the current thread will have its error queue removed. Since error queue data structures are allocated automatically for new threads, they must be freed when threads are terminated in oder to avoid memory leaks. */ ERR_remove_state(0); if(conn->ssl.handle) { (void)SSL_shutdown(conn->ssl.handle); SSL_set_connect_state(conn->ssl.handle); SSL_free (conn->ssl.handle); conn->ssl.handle = NULL; } if(conn->ssl.ctx) { SSL_CTX_free (conn->ssl.ctx); conn->ssl.ctx = NULL; } conn->ssl.use = FALSE; /* get back to ordinary socket usage */ }}/* * This sets up a session cache to the specified size. */CURLcode Curl_SSL_InitSessions(struct SessionHandle *data, long amount){ struct curl_ssl_session *session; if(data->state.session) /* this is just a precaution to prevent multiple inits */ return CURLE_OK; session = (struct curl_ssl_session *) malloc(amount * sizeof(struct curl_ssl_session)); if(!session) return CURLE_OUT_OF_MEMORY; /* "blank out" the newly allocated memory */ memset(session, 0, amount * sizeof(struct curl_ssl_session)); /* store the info in the SSL section */ data->set.ssl.numsessions = amount; data->state.session = session; data->state.sessionage = 1; /* this is brand new */ return CURLE_OK;}/* * Check if there's a session ID for the given connection in the cache, * and if there's one suitable, it is returned. */static int Get_SSL_Session(struct connectdata *conn, SSL_SESSION **ssl_sessionid){ struct curl_ssl_session *check; struct SessionHandle *data = conn->data; long i; for(i=0; i< data->set.ssl.numsessions; i++) { check = &data->state.session[i]; if(!check->sessionid) /* not session ID means blank entry */ continue; if(curl_strequal(conn->name, check->name) && (conn->remote_port == check->remote_port) && Curl_ssl_config_matches(&conn->ssl_config, &check->ssl_config)) { /* yes, we have a session ID! */ data->state.sessionage++; /* increase general age */ check->age = data->state.sessionage; /* set this as used in this age */ *ssl_sessionid = check->sessionid; return FALSE; } } *ssl_sessionid = (SSL_SESSION *)NULL; return TRUE;}/* * Kill a single session ID entry in the cache. */static int Kill_Single_Session(struct curl_ssl_session *session){ if(session->sessionid) { /* defensive check */ /* free the ID */ SSL_SESSION_free(session->sessionid); session->sessionid=NULL; session->age = 0; /* fresh */ Curl_free_ssl_config(&session->ssl_config); free(session->name); session->name = NULL; /* no name */ return 0; /* ok */ } else return 1;}/* * This function is called when the 'data' struct is going away. Close * down everything and free all resources! */int Curl_SSL_Close_All(struct SessionHandle *data){ int i; if(data->state.session) { for(i=0; i< data->set.ssl.numsessions; i++) /* the single-killer function handles empty table slots */ Kill_Single_Session(&data->state.session[i]); /* free the cache data */ free(data->state.session); }#ifdef HAVE_OPENSSL_ENGINE_H if(data->engine) { ENGINE_free(data->engine); data->engine = NULL; }#endif return 0;}/* * Extract the session id and store it in the session cache. */static int Store_SSL_Session(struct connectdata *conn){ SSL_SESSION *ssl_sessionid; int i; struct SessionHandle *data=conn->data; /* the mother of all structs */ struct curl_ssl_session *store = &data->state.session[0]; int oldest_age=data->state.session[0].age; /* zero if unused */ /* ask OpenSSL, say please */#ifdef HAVE_SSL_GET1_SESSION ssl_sessionid = SSL_get1_session(conn->ssl.handle); /* SSL_get1_session() will increment the reference count and the session will stay in memory until explicitly freed with SSL_SESSION_free(3), regardless of its state. This function was introduced in openssl 0.9.5a. */#else ssl_sessionid = SSL_get_session(conn->ssl.handle); /* if SSL_get1_session() is unavailable, use SSL_get_session(). This is an inferior option because the session can be flushed at any time by openssl. It is included only so curl compiles under versions of openssl < 0.9.5a. WARNING: How curl behaves if it's session is flushed is untested. */#endif /* Now we should add the session ID and the host name to the cache, (remove the oldest if necessary) */ /* find an empty slot for us, or find the oldest */ for(i=1; (i<data->set.ssl.numsessions) && data->state.session[i].sessionid; i++) { if(data->state.session[i].age < oldest_age) { oldest_age = data->state.session[i].age; store = &data->state.session[i]; } } if(i == data->set.ssl.numsessions) /* cache is full, we must "kill" the oldest entry! */ Kill_Single_Session(store); else store = &data->state.session[i]; /* use this slot */ /* now init the session struct wisely */ store->sessionid = ssl_sessionid; store->age = data->state.sessionage; /* set current age */ store->name = strdup(conn->name); /* clone host name */ store->remote_port = conn->remote_port; /* port number */ Curl_clone_ssl_config(&conn->ssl_config, &store->ssl_config); return 0;}static int Curl_ASN1_UTCTIME_output(struct connectdata *conn, const char *prefix, ASN1_UTCTIME *tm){ char *asn1_string; int gmt=FALSE; int i; int year=0,month=0,day=0,hour=0,minute=0,second=0; struct SessionHandle *data = conn->data; if(!data->set.verbose) return 0; i=tm->length; asn1_string=(char *)tm->data; if(i < 10) return 1; if(asn1_string[i-1] == 'Z') gmt=TRUE; for (i=0; i<10; i++) if((asn1_string[i] > '9') || (asn1_string[i] < '0')) return 2; year= (asn1_string[0]-'0')*10+(asn1_string[1]-'0'); if(year < 50) year+=100; month= (asn1_string[2]-'0')*10+(asn1_string[3]-'0'); if((month > 12) || (month < 1)) return 3; day= (asn1_string[4]-'0')*10+(asn1_string[5]-'0'); hour= (asn1_string[6]-'0')*10+(asn1_string[7]-'0'); minute= (asn1_string[8]-'0')*10+(asn1_string[9]-'0'); if((asn1_string[10] >= '0') && (asn1_string[10] <= '9') && (asn1_string[11] >= '0') && (asn1_string[11] <= '9')) second= (asn1_string[10]-'0')*10+(asn1_string[11]-'0'); infof(data, "%s%04d-%02d-%02d %02d:%02d:%02d %s\n", prefix, year+1900, month, day, hour, minute, second, (gmt?"GMT":"")); return 0;}#endif /* ====================================================== */#ifdef USE_SSLEAYstatic intcert_hostcheck(const char *certname, const char *hostname){ char *tmp; const char *certdomain; if(!certname || strlen(certname)<3 || !hostname || !strlen(hostname)) /* sanity check */ return 0; if(curl_strequal(certname, hostname)) /* trivial case */ return 1; certdomain = certname + 1; if((certname[0] != '*') || (certdomain[0] != '.')) return 0; /* not a wildcard certificate, check failed */ if(!strchr(certdomain+1, '.')) return 0; /* the certificate must have at least another dot in its name */ /* find 'certdomain' within 'hostname' */ tmp = strstr(hostname, certdomain); if(tmp) { /* ok the certname's domain matches the hostname, let's check that it's a tail-match */ if(curl_strequal(tmp, certdomain)) /* looks like a match. Just check we havent swallowed a '.' */ return tmp == strchr(hostname, '.'); else return 0; } return 0;}/* Quote from RFC2818 section 3.1 "Server Identity" If a subjectAltName extension of type dNSName is present, that MUST be used as the identity. Otherwise, the (most specific) Common Name field in the Subject field of the certificate MUST be used. Although the use of the Common Name is existing practice, it is deprecated and Certification Authorities are encouraged to use the dNSName instead. Matching is performed using the matching rules specified by [RFC2459]. If more than one identity of a given type is present in the certificate (e.g., more than one dNSName name, a match in any one of the set is considered acceptable.) Names may contain the wildcard character * which is considered to match any single domain name component or component fragment. E.g., *.a.com matches foo.a.com but not bar.foo.a.com. f*.com matches foo.com but not bar.com. In some cases, the URI is specified as an IP address rather than a hostname. In this case, the iPAddress subjectAltName must be present in the certificate and must exactly match the IP in the URI.*/static CURLcode verifyhost(struct connectdata *conn){ char peer_CN[257]; bool matched = FALSE; /* no alternative match yet */ int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */ int addrlen; struct SessionHandle *data = conn->data; STACK_OF(GENERAL_NAME) *altnames;#ifdef ENABLE_IPV6 struct in6_addr addr;#else struct in_addr addr;#endif #ifdef ENABLE_IPV6 if(conn->bits.ipv6_ip && Curl_inet_pton(AF_INET6, conn->hostname, &addr)) { target = GEN_IPADD; addrlen = sizeof(struct in6_addr); } else#endif if(Curl_inet_pton(AF_INET, conn->hostname, &addr)) { target = GEN_IPADD; addrlen = sizeof(struct in_addr); } /* get a "list" of alternative names */ altnames = X509_get_ext_d2i(conn->ssl.server_cert, NID_subject_alt_name, NULL, NULL); if(altnames) { int hostlen; int domainlen; char *domain; int numalts; int i; if(GEN_DNS == target) { hostlen = strlen(conn->hostname); domain = strchr(conn->hostname, '.'); if(domain) domainlen = strlen(domain); } /* get amount of alternatives, RFC2459 claims there MUST be at least one, but we don't depend on it... */ numalts = sk_GENERAL_NAME_num(altnames); /* loop through all alternatives while none has matched */ for (i=0; (i<numalts) && !matched; i++) { /* get a handle to alternative name number i */ const GENERAL_NAME *check = sk_GENERAL_NAME_value(altnames, i); /* only check alternatives of the same type the target is */ if(check->type == target) { /* get data and length */ const char *altptr = (char *)ASN1_STRING_data(check->d.ia5); const int altlen = ASN1_STRING_length(check->d.ia5); switch(target) { case GEN_DNS: /* name comparison */ /* Is this an exact match? */ if((hostlen == altlen) && curl_strnequal(conn->hostname, altptr, hostlen))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -