CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, const char *pinnedpubkey, const unsigned char *pubkey, size_t pubkeylen) { FILE *fp; unsigned char *buf = NULL, *pem_ptr = NULL; long filesize; size_t size, pem_len; CURLcode pem_read; CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; #ifdef curlssl_sha256sum CURLcode encode; size_t encodedlen, pinkeylen; char *encoded, *pinkeycopy, *begin_pos, *end_pos; unsigned char *sha256sumdigest = NULL; #endif /* if a path wasn't specified, don't pin */ if(!pinnedpubkey) return CURLE_OK; if(!pubkey || !pubkeylen) return result; /* only do this if pinnedpubkey starts with "sha256//", length 8 */ if(strncmp(pinnedpubkey, "sha256//", 8) == 0) { #ifdef curlssl_sha256sum /* compute sha256sum of public key */ sha256sumdigest = malloc(SHA256_DIGEST_LENGTH); if(!sha256sumdigest) return CURLE_OUT_OF_MEMORY; curlssl_sha256sum(pubkey, pubkeylen, sha256sumdigest, SHA256_DIGEST_LENGTH); encode = Curl_base64_encode(data, (char *)sha256sumdigest, SHA256_DIGEST_LENGTH, &encoded, &encodedlen); Curl_safefree(sha256sumdigest); if(encode) return encode; infof(data, "\t public key hash: sha256//%s\n", encoded); /* it starts with sha256//, copy so we can modify it */ pinkeylen = strlen(pinnedpubkey) + 1; pinkeycopy = malloc(pinkeylen); if(!pinkeycopy) { Curl_safefree(encoded); return CURLE_OUT_OF_MEMORY; } memcpy(pinkeycopy, pinnedpubkey, pinkeylen); /* point begin_pos to the copy, and start extracting keys */ begin_pos = pinkeycopy; do { end_pos = strstr(begin_pos, ";sha256//"); /* * if there is an end_pos, null terminate, * otherwise it'll go to the end of the original string */ if(end_pos) end_pos[0] = '\0'; /* compare base64 sha256 digests, 8 is the length of "sha256//" */ if(encodedlen == strlen(begin_pos + 8) && !memcmp(encoded, begin_pos + 8, encodedlen)) { result = CURLE_OK; break; } /* * change back the null-terminator we changed earlier, * and look for next begin */ if(end_pos) { end_pos[0] = ';'; begin_pos = strstr(end_pos, "sha256//"); } } while(end_pos && begin_pos); Curl_safefree(encoded); Curl_safefree(pinkeycopy); #else /* without sha256 support, this cannot match */ (void)data; #endif return result; } fp = fopen(pinnedpubkey, "rb"); if(!fp) return result; do { /* Determine the file's size */ if(fseek(fp, 0, SEEK_END)) break; filesize = ftell(fp); if(fseek(fp, 0, SEEK_SET)) break; if(filesize < 0 || filesize > MAX_PINNED_PUBKEY_SIZE) break; /* * if the size of our certificate is bigger than the file * size then it can't match */ size = curlx_sotouz((curl_off_t) filesize); if(pubkeylen > size) break; /* * Allocate buffer for the pinned key * With 1 additional byte for null terminator in case of PEM key */ buf = malloc(size + 1); if(!buf) break; /* Returns number of elements read, which should be 1 */ if((int) fread(buf, size, 1, fp) != 1) break; /* If the sizes are the same, it can't be base64 encoded, must be der */ if(pubkeylen == size) { if(!memcmp(pubkey, buf, pubkeylen)) result = CURLE_OK; break; } /* * Otherwise we will assume it's PEM and try to decode it * after placing null terminator */ buf[size] = '\0'; pem_read = pubkey_pem_to_der((const char *)buf, &pem_ptr, &pem_len); /* if it wasn't read successfully, exit */ if(pem_read) break; /* * if the size of our certificate doesn't match the size of * the decoded file, they can't be the same, otherwise compare */ if(pubkeylen == pem_len && !memcmp(pubkey, pem_ptr, pubkeylen)) result = CURLE_OK; } while(0); Curl_safefree(buf); Curl_safefree(pem_ptr); fclose(fp); return result; }
static ssize_t ldap_recv(struct connectdata *conn, int sockindex, char *buf, size_t len, CURLcode *err) { ldapconninfo *li = conn->proto.generic; struct SessionHandle *data=conn->data; ldapreqinfo *lr = data->state.proto.generic; int rc, ret; LDAPMessage *result = NULL; LDAPMessage *ent; BerElement *ber = NULL; struct timeval tv = {0,1}; (void)len; (void)buf; (void)sockindex; rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_RECEIVED, &tv, &result); if(rc < 0) { failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc)); *err = CURLE_RECV_ERROR; return -1; } *err = CURLE_AGAIN; ret = -1; /* timed out */ if(result == NULL) return ret; for(ent = ldap_first_message(li->ld, result); ent; ent = ldap_next_message(li->ld, ent)) { struct berval bv, *bvals, **bvp = &bvals; int binary = 0, msgtype; msgtype = ldap_msgtype(ent); if(msgtype == LDAP_RES_SEARCH_RESULT) { int code; char *info = NULL; rc = ldap_parse_result(li->ld, ent, &code, NULL, &info, NULL, NULL, 0); if(rc) { failf(data, "LDAP local: search ldap_parse_result %s", ldap_err2string(rc)); *err = CURLE_LDAP_SEARCH_FAILED; } else if(code && code != LDAP_SIZELIMIT_EXCEEDED) { failf(data, "LDAP remote: search failed %s %s", ldap_err2string(rc), info ? info : ""); *err = CURLE_LDAP_SEARCH_FAILED; } else { /* successful */ if(code == LDAP_SIZELIMIT_EXCEEDED) infof(data, "There are more than %d entries\n", lr->nument); data->req.size = data->req.bytecount; *err = CURLE_OK; ret = 0; } lr->msgid = 0; ldap_memfree(info); break; } else if(msgtype != LDAP_RES_SEARCH_ENTRY) continue; lr->nument++; rc = ldap_get_dn_ber(li->ld, ent, &ber, &bv); if(rc < 0) { /* TODO: verify that this is really how this return code should be handled */ *err = CURLE_RECV_ERROR; return -1; } Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"DN: ", 4); Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val, bv.bv_len); Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1); data->req.bytecount += bv.bv_len + 5; for(rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, bvp); rc == LDAP_SUCCESS; rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, bvp)) { int i; if(bv.bv_val == NULL) break; if(bv.bv_len > 7 && !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7)) binary = 1; else binary = 0; for(i=0; bvals[i].bv_val != NULL; i++) { int binval = 0; Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1); Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val, bv.bv_len); Curl_client_write(conn, CLIENTWRITE_BODY, (char *)":", 1); data->req.bytecount += bv.bv_len + 2; if(!binary) { /* check for leading or trailing whitespace */ if(ISSPACE(bvals[i].bv_val[0]) || ISSPACE(bvals[i].bv_val[bvals[i].bv_len-1])) binval = 1; else { /* check for unprintable characters */ unsigned int j; for(j=0; j<bvals[i].bv_len; j++) if(!ISPRINT(bvals[i].bv_val[j])) { binval = 1; break; } } } if(binary || binval) { char *val_b64 = NULL; size_t val_b64_sz = 0; /* Binary value, encode to base64. */ CURLcode error = Curl_base64_encode(data, bvals[i].bv_val, bvals[i].bv_len, &val_b64, &val_b64_sz); if(error) { ber_memfree(bvals); ber_free(ber, 0); ldap_msgfree(result); *err = error; return -1; } Curl_client_write(conn, CLIENTWRITE_BODY, (char *)": ", 2); data->req.bytecount += 2; if(val_b64_sz > 0) { Curl_client_write(conn, CLIENTWRITE_BODY, val_b64, val_b64_sz); free(val_b64); data->req.bytecount += val_b64_sz; } } else { Curl_client_write(conn, CLIENTWRITE_BODY, (char *)" ", 1); Curl_client_write(conn, CLIENTWRITE_BODY, bvals[i].bv_val, bvals[i].bv_len); data->req.bytecount += bvals[i].bv_len + 1; } Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0); data->req.bytecount++; } ber_memfree(bvals); Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0); data->req.bytecount++; } Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0); data->req.bytecount++; ber_free(ber, 0); } ldap_msgfree(result); return ret; }
static CURLcode cyassl_connect_step2(struct connectdata *conn, int sockindex) { int ret = -1; struct Curl_easy *data = conn->data; struct ssl_connect_data* connssl = &conn->ssl[sockindex]; const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; const char * const dispname = SSL_IS_PROXY() ? conn->http_proxy.host.dispname : conn->host.dispname; const char * const pinnedpubkey = SSL_IS_PROXY() ? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]; conn->recv[sockindex] = cyassl_recv; conn->send[sockindex] = cyassl_send; /* Enable RFC2818 checks */ if(SSL_CONN_CONFIG(verifyhost)) { ret = CyaSSL_check_domain_name(BACKEND->handle, hostname); if(ret == SSL_FAILURE) return CURLE_OUT_OF_MEMORY; } ret = SSL_connect(BACKEND->handle); if(ret != 1) { char error_buffer[CYASSL_MAX_ERROR_SZ]; int detail = SSL_get_error(BACKEND->handle, ret); if(SSL_ERROR_WANT_READ == detail) { connssl->connecting_state = ssl_connect_2_reading; return CURLE_OK; } else if(SSL_ERROR_WANT_WRITE == detail) { connssl->connecting_state = ssl_connect_2_writing; return CURLE_OK; } /* There is no easy way to override only the CN matching. * This will enable the override of both mismatching SubjectAltNames * as also mismatching CN fields */ else if(DOMAIN_NAME_MISMATCH == detail) { #if 1 failf(data, "\tsubject alt name(s) or common name do not match \"%s\"\n", dispname); return CURLE_PEER_FAILED_VERIFICATION; #else /* When the CyaSSL_check_domain_name() is used and you desire to continue * on a DOMAIN_NAME_MISMATCH, i.e. 'conn->ssl_config.verifyhost == 0', * CyaSSL version 2.4.0 will fail with an INCOMPLETE_DATA error. The only * way to do this is currently to switch the CyaSSL_check_domain_name() * in and out based on the 'conn->ssl_config.verifyhost' value. */ if(SSL_CONN_CONFIG(verifyhost)) { failf(data, "\tsubject alt name(s) or common name do not match \"%s\"\n", dispname); return CURLE_PEER_FAILED_VERIFICATION; } else { infof(data, "\tsubject alt name(s) and/or common name do not match \"%s\"\n", dispname); return CURLE_OK; } #endif } #if LIBCYASSL_VERSION_HEX >= 0x02007000 /* 2.7.0 */ else if(ASN_NO_SIGNER_E == detail) { if(SSL_CONN_CONFIG(verifypeer)) { failf(data, "\tCA signer not available for verification\n"); return CURLE_SSL_CACERT_BADFILE; } else { /* Just continue with a warning if no strict certificate verification is required. */ infof(data, "CA signer not available for verification, " "continuing anyway\n"); } } #endif else { failf(data, "SSL_connect failed with error %d: %s", detail, ERR_error_string(detail, error_buffer)); return CURLE_SSL_CONNECT_ERROR; } } if(pinnedpubkey) { #ifdef KEEP_PEER_CERT X509 *x509; const char *x509_der; int x509_der_len; curl_X509certificate x509_parsed; curl_asn1Element *pubkey; CURLcode result; x509 = SSL_get_peer_certificate(BACKEND->handle); if(!x509) { failf(data, "SSL: failed retrieving server certificate"); return CURLE_SSL_PINNEDPUBKEYNOTMATCH; } x509_der = (const char *)CyaSSL_X509_get_der(x509, &x509_der_len); if(!x509_der) { failf(data, "SSL: failed retrieving ASN.1 server certificate"); return CURLE_SSL_PINNEDPUBKEYNOTMATCH; } memset(&x509_parsed, 0, sizeof(x509_parsed)); if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len)) return CURLE_SSL_PINNEDPUBKEYNOTMATCH; pubkey = &x509_parsed.subjectPublicKeyInfo; if(!pubkey->header || pubkey->end <= pubkey->header) { failf(data, "SSL: failed retrieving public key from server certificate"); return CURLE_SSL_PINNEDPUBKEYNOTMATCH; } result = Curl_pin_peer_pubkey(data, pinnedpubkey, (const unsigned char *)pubkey->header, (size_t)(pubkey->end - pubkey->header)); if(result) { failf(data, "SSL: public key does not match pinned public key!"); return result; } #else failf(data, "Library lacks pinning support built-in"); return CURLE_NOT_BUILT_IN; #endif } #ifdef HAVE_ALPN if(conn->bits.tls_enable_alpn) { int rc; char *protocol = NULL; unsigned short protocol_len = 0; rc = wolfSSL_ALPN_GetProtocol(BACKEND->handle, &protocol, &protocol_len); if(rc == SSL_SUCCESS) { infof(data, "ALPN, server accepted to use %.*s\n", protocol_len, protocol); if(protocol_len == ALPN_HTTP_1_1_LENGTH && !memcmp(protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH)) conn->negnpn = CURL_HTTP_VERSION_1_1; #ifdef USE_NGHTTP2 else if(data->set.httpversion >= CURL_HTTP_VERSION_2 && protocol_len == NGHTTP2_PROTO_VERSION_ID_LEN && !memcmp(protocol, NGHTTP2_PROTO_VERSION_ID, NGHTTP2_PROTO_VERSION_ID_LEN)) conn->negnpn = CURL_HTTP_VERSION_2; #endif else infof(data, "ALPN, unrecognized protocol %.*s\n", protocol_len, protocol); } else if(rc == SSL_ALPN_NOT_FOUND) infof(data, "ALPN, server did not agree to a protocol\n"); else { failf(data, "ALPN, failure getting protocol, error %d", rc); return CURLE_SSL_CONNECT_ERROR; } } #endif /* HAVE_ALPN */ connssl->connecting_state = ssl_connect_3; #if (LIBCYASSL_VERSION_HEX >= 0x03009010) infof(data, "SSL connection using %s / %s\n", wolfSSL_get_version(BACKEND->handle), wolfSSL_get_cipher_name(BACKEND->handle)); #else infof(data, "SSL connected\n"); #endif return CURLE_OK; }
static int random_the_seed(struct SessionHandle *data) { char *buf = data->state.buffer; /* point to the big buffer */ int nread=0; /* Q: should we add support for a random file name as a libcurl option? A: Yes, it is here */ #ifndef RANDOM_FILE /* if RANDOM_FILE isn't defined, we only perform this if an option tells us to! */ if(data->set.ssl.random_file) #define RANDOM_FILE "" /* doesn't matter won't be used */ #endif { /* let the option override the define */ nread += RAND_load_file((data->set.ssl.random_file? data->set.ssl.random_file:RANDOM_FILE), 16384); if(seed_enough(nread)) return nread; } #if defined(HAVE_RAND_EGD) /* only available in OpenSSL 0.9.5 and later */ /* EGD_SOCKET is set at configure time or not at all */ #ifndef EGD_SOCKET /* If we don't have the define set, we only do this if the egd-option is set */ if(data->set.ssl.egdsocket) #define EGD_SOCKET "" /* doesn't matter won't be used */ #endif { /* If there's an option and a define, the option overrides the define */ int ret = RAND_egd(data->set.ssl.egdsocket?data->set.ssl.egdsocket:EGD_SOCKET); if(-1 != ret) { nread += ret; if(seed_enough(nread)) return nread; } } #endif /* If we get here, it means we need to seed the PRNG using a "silly" approach! */ #ifdef HAVE_RAND_SCREEN /* This one gets a random value by reading the currently shown screen */ RAND_screen(); nread = 100; /* just a value */ #else { int len; char *area = Curl_FormBoundary(); if(!area) return 3; /* out of memory */ len = strlen(area); RAND_seed(area, len); free(area); /* now remove the random junk */ } #endif /* generates a default path for the random seed file */ buf[0]=0; /* blank it first */ RAND_file_name(buf, BUFSIZE); if ( buf[0] ) { /* we got a file name to try */ nread += RAND_load_file(buf, 16384); if(seed_enough(nread)) return nread; } infof(data, "libcurl is now using a weak random seed!\n"); return nread; }
CURLcode Curl_is_connected(struct connectdata *conn, int sockindex, bool *connected) { int rc; struct SessionHandle *data = conn->data; CURLcode code = CURLE_OK; curl_socket_t sockfd = conn->sock[sockindex]; long allow = DEFAULT_CONNECT_TIMEOUT; int error = 0; struct timeval now; DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET); *connected = FALSE; /* a very negative world view is best */ if(conn->bits.tcpconnect) { /* we are connected already! */ *connected = TRUE; return CURLE_OK; } now = Curl_tvnow(); /* figure out how long time we have left to connect */ allow = Curl_timeleft(data, &now, TRUE); if(allow < 0) { /* time-out, bail out, go home */ failf(data, "Connection time-out"); return CURLE_OPERATION_TIMEDOUT; } /* check for connect without timeout as we want to return immediately */ rc = waitconnect(conn, sockfd, 0); if(WAITCONN_TIMEOUT == rc) { if(curlx_tvdiff(now, conn->connecttime) >= conn->timeoutms_per_addr) { infof(data, "After %ldms connect time, move on!\n", conn->timeoutms_per_addr); goto next; } /* not an error, but also no connection yet */ return code; } if(WAITCONN_CONNECTED == rc) { if(verifyconnect(sockfd, &error)) { /* we are connected with TCP, awesome! */ /* see if we need to do any proxy magic first once we connected */ code = Curl_connected_proxy(conn); if(code) return code; conn->bits.tcpconnect = TRUE; *connected = TRUE; Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */ Curl_verboseconnect(conn); Curl_updateconninfo(conn, sockfd); return CURLE_OK; } /* nope, not connected for real */ } else { /* nope, not connected */ if(WAITCONN_FDSET_ERROR == rc) { (void)verifyconnect(sockfd, &error); infof(data, "%s\n",Curl_strerror(conn, error)); } else infof(data, "Connection failed\n"); } /* * The connection failed here, we should attempt to connect to the "next * address" for the given host. But first remember the latest error. */ if(error) { data->state.os_errno = error; SET_SOCKERRNO(error); } next: code = trynextip(conn, sockindex, connected); if(code) { error = SOCKERRNO; data->state.os_errno = error; failf(data, "Failed connect to %s:%ld; %s", conn->host.name, conn->port, Curl_strerror(conn, error)); } return code; }
/* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, void *userp) { struct HTTP *stream; struct SessionHandle *data_s; int32_t stream_id = frame->hd.stream_id; struct connectdata *conn = (struct connectdata *)userp; (void)flags; DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ /* get the stream from the hash based on Stream ID */ data_s = nghttp2_session_get_stream_user_data(session, stream_id); if(!data_s) /* Receiving a Stream ID not in the hash should not happen, this is an internal error more than anything else! */ return NGHTTP2_ERR_CALLBACK_FAILURE; stream = data_s->req.protop; if(!stream) { failf(data_s, "Internal NULL stream! 5\n"); return NGHTTP2_ERR_CALLBACK_FAILURE; } if(stream->bodystarted) /* Ignore trailer or HEADERS not mapped to HTTP semantics. The consequence is handled in on_frame_recv(). */ return 0; /* Store received PUSH_PROMISE headers to be used when the subsequent PUSH_PROMISE callback comes */ if(frame->hd.type == NGHTTP2_PUSH_PROMISE) { char *h; if(!stream->push_headers) { stream->push_headers_alloc = 10; stream->push_headers = malloc(stream->push_headers_alloc * sizeof(char *)); stream->push_headers_used = 0; } else if(stream->push_headers_used == stream->push_headers_alloc) { char **headp; stream->push_headers_alloc *= 2; headp = realloc(stream->push_headers, stream->push_headers_alloc * sizeof(char *)); if(!headp) { free(stream->push_headers); stream->push_headers = NULL; return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } stream->push_headers = headp; } h = aprintf("%s:%s", name, value); if(h) stream->push_headers[stream->push_headers_used++] = h; return 0; } if(namelen == sizeof(":status") - 1 && memcmp(":status", name, namelen) == 0) { /* nghttp2 guarantees :status is received first and only once, and value is 3 digits status code, and decode_status_code always succeeds. */ stream->status_code = decode_status_code(value, valuelen); DEBUGASSERT(stream->status_code != -1); Curl_add_buffer(stream->header_recvbuf, "HTTP/2.0 ", 9); Curl_add_buffer(stream->header_recvbuf, value, valuelen); Curl_add_buffer(stream->header_recvbuf, "\r\n", 2); data_s->state.drain++; /* if we receive data for another handle, wake that up */ if(conn->data != data_s) Curl_expire(data_s, 1); DEBUGF(infof(data_s, "h2 status: HTTP/2 %03d\n", stream->status_code)); return 0; } /* nghttp2 guarantees that namelen > 0, and :status was already received, and this is not pseudo-header field . */ /* convert to a HTTP1-style header */ Curl_add_buffer(stream->header_recvbuf, name, namelen); Curl_add_buffer(stream->header_recvbuf, ":", 1); Curl_add_buffer(stream->header_recvbuf, value, valuelen); Curl_add_buffer(stream->header_recvbuf, "\r\n", 2); data_s->state.drain++; /* if we receive data for another handle, wake that up */ if(conn->data != data_s) Curl_expire(data_s, 1); DEBUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen, value)); return 0; /* 0 is successful */ }
/********************************************************** * * tftp_set_timeouts - * * Set timeouts based on state machine state. * Use user provided connect timeouts until DATA or ACK * packet is received, then use user-provided transfer timeouts * * **********************************************************/ static CURLcode tftp_set_timeouts(tftp_state_data_t *state) { time_t maxtime, timeout; long timeout_ms; bool start = (state->state == TFTP_STATE_START) ? TRUE : FALSE; time(&state->start_time); /* Compute drop-dead time */ timeout_ms = Curl_timeleft(state->conn->data, NULL, start); if(timeout_ms < 0) { /* time-out, bail out, go home */ failf(state->conn->data, "Connection time-out"); return CURLE_OPERATION_TIMEDOUT; } if(start) { maxtime = (time_t)(timeout_ms + 500) / 1000; state->max_time = state->start_time+maxtime; /* Set per-block timeout to total */ timeout = maxtime; /* Average restart after 5 seconds */ state->retry_max = (int)timeout/5; if(state->retry_max < 1) /* avoid division by zero below */ state->retry_max = 1; /* Compute the re-start interval to suit the timeout */ state->retry_time = (int)timeout/state->retry_max; if(state->retry_time<1) state->retry_time=1; } else { if(timeout_ms > 0) maxtime = (time_t)(timeout_ms + 500) / 1000; else maxtime = 3600; state->max_time = state->start_time+maxtime; /* Set per-block timeout to total */ timeout = maxtime; /* Average reposting an ACK after 5 seconds */ state->retry_max = (int)timeout/5; } /* But bound the total number */ if(state->retry_max<3) state->retry_max=3; if(state->retry_max>50) state->retry_max=50; /* Compute the re-ACK interval to suit the timeout */ state->retry_time = (int)(timeout/state->retry_max); if(state->retry_time<1) state->retry_time=1; infof(state->conn->data, "set timeouts for state %d; Total %ld, retry %d maxtry %d\n", (int)state->state, (long)(state->max_time-state->start_time), state->retry_time, state->retry_max); /* init RX time */ time(&state->rx_time); return CURLE_OK; }
/* * Curl_pp_readresp() * * Reads a piece of a server response. */ CURLcode Curl_pp_readresp(curl_socket_t sockfd, struct pingpong *pp, int *code, /* return the server code if done */ size_t *size) /* size of the response */ { ssize_t perline; /* count bytes per line */ bool keepon=TRUE; ssize_t gotbytes; char *ptr; struct connectdata *conn = pp->conn; struct SessionHandle *data = conn->data; char * const buf = data->state.buffer; CURLcode result = CURLE_OK; *code = 0; /* 0 for errors or not done */ *size = 0; ptr=buf + pp->nread_resp; /* number of bytes in the current line, so far */ perline = (ssize_t)(ptr-pp->linestart_resp); keepon=TRUE; while((pp->nread_resp<BUFSIZE) && (keepon && !result)) { if(pp->cache) { /* we had data in the "cache", copy that instead of doing an actual * read * * pp->cache_size is cast to ssize_t here. This should be safe, because * it would have been populated with something of size int to begin * with, even though its datatype may be larger than an int. */ DEBUGASSERT((ptr+pp->cache_size) <= (buf+BUFSIZE+1)); memcpy(ptr, pp->cache, pp->cache_size); gotbytes = (ssize_t)pp->cache_size; free(pp->cache); /* free the cache */ pp->cache = NULL; /* clear the pointer */ pp->cache_size = 0; /* zero the size just in case */ } else { int res; #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) enum protection_level prot = conn->data_prot; conn->data_prot = PROT_CLEAR; #endif DEBUGASSERT((ptr+BUFSIZE-pp->nread_resp) <= (buf+BUFSIZE+1)); res = Curl_read(conn, sockfd, ptr, BUFSIZE-pp->nread_resp, &gotbytes); #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) DEBUGASSERT(prot > PROT_NONE && prot < PROT_LAST); conn->data_prot = prot; #endif if(res == CURLE_AGAIN) return CURLE_OK; /* return */ if((res == CURLE_OK) && (gotbytes > 0)) /* convert from the network encoding */ res = Curl_convert_from_network(data, ptr, gotbytes); /* Curl_convert_from_network calls failf if unsuccessful */ if(CURLE_OK != res) { result = (CURLcode)res; /* Set outer result variable to this error. */ keepon = FALSE; } } if(!keepon) ; else if(gotbytes <= 0) { keepon = FALSE; result = CURLE_RECV_ERROR; failf(data, "response reading failed"); } else { /* we got a whole chunk of data, which can be anything from one * byte to a set of lines and possible just a piece of the last * line */ ssize_t i; ssize_t clipamount = 0; bool restart = FALSE; data->req.headerbytecount += (long)gotbytes; pp->nread_resp += gotbytes; for(i = 0; i < gotbytes; ptr++, i++) { perline++; if(*ptr=='\n') { /* a newline is CRLF in pp-talk, so the CR is ignored as the line isn't really terminated until the LF comes */ /* output debug output if that is requested */ #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) if(!conn->sec_complete) #endif if(data->set.verbose) Curl_debug(data, CURLINFO_HEADER_IN, pp->linestart_resp, (size_t)perline, conn); /* * We pass all response-lines to the callback function registered * for "headers". The response lines can be seen as a kind of * headers. */ result = Curl_client_write(conn, CLIENTWRITE_HEADER, pp->linestart_resp, perline); if(result) return result; if(pp->endofresp(pp, code)) { /* This is the end of the last line, copy the last line to the start of the buffer and zero terminate, for old times sake (and krb4)! */ char *meow; int n; for(meow=pp->linestart_resp, n=0; meow<ptr; meow++, n++) buf[n] = *meow; *meow=0; /* zero terminate */ keepon=FALSE; pp->linestart_resp = ptr+1; /* advance pointer */ i++; /* skip this before getting out */ *size = pp->nread_resp; /* size of the response */ pp->nread_resp = 0; /* restart */ break; } perline=0; /* line starts over here */ pp->linestart_resp = ptr+1; } } if(!keepon && (i != gotbytes)) { /* We found the end of the response lines, but we didn't parse the full chunk of data we have read from the server. We therefore need to store the rest of the data to be checked on the next invoke as it may actually contain another end of response already! */ clipamount = gotbytes - i; restart = TRUE; DEBUGF(infof(data, "Curl_pp_readresp_ %d bytes of trailing " "server response left\n", (int)clipamount)); } else if(keepon) { if((perline == gotbytes) && (gotbytes > BUFSIZE/2)) { /* We got an excessive line without newlines and we need to deal with it. We keep the first bytes of the line then we throw away the rest. */ infof(data, "Excessive server response line length received, " "%zd bytes. Stripping\n", gotbytes); restart = TRUE; /* we keep 40 bytes since all our pingpong protocols are only interested in the first piece */ clipamount = 40; } else if(pp->nread_resp > BUFSIZE/2) { /* We got a large chunk of data and there's potentially still trailing data to take care of, so we put any such part in the "cache", clear the buffer to make space and restart. */ clipamount = perline; restart = TRUE; } } else if(i == gotbytes) restart = TRUE; if(clipamount) { pp->cache_size = clipamount; pp->cache = malloc(pp->cache_size); if(pp->cache) memcpy(pp->cache, pp->linestart_resp, pp->cache_size); else return CURLE_OUT_OF_MEMORY; } if(restart) { /* now reset a few variables to start over nicely from the start of the big buffer */ pp->nread_resp = 0; /* start over from scratch in the buffer */ ptr = pp->linestart_resp = buf; perline = 0; } } /* there was data */ } /* while there's buffer left and loop is requested */ pp->pending_resp = FALSE; return result; }
/* returning zero (0) means success, everything else is treated as "failure" with no care exactly what the failure was */ int Curl_input_negotiate(struct connectdata *conn, bool proxy, const char *header) { struct SessionHandle *data = conn->data; struct negotiatedata *neg_ctx = proxy?&data->state.proxyneg: &data->state.negotiate; OM_uint32 major_status, minor_status, minor_status2; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; int ret; size_t len; size_t rawlen = 0; bool gss; const char* protocol; CURLcode error; while(*header && ISSPACE(*header)) header++; if(checkprefix("GSS-Negotiate", header)) { protocol = "GSS-Negotiate"; gss = TRUE; } else if(checkprefix("Negotiate", header)) { protocol = "Negotiate"; gss = FALSE; } else return -1; if(neg_ctx->context) { if(neg_ctx->gss != gss) { return -1; } } else { neg_ctx->protocol = protocol; neg_ctx->gss = gss; } if(neg_ctx->context && neg_ctx->status == GSS_S_COMPLETE) { /* We finished successfully our part of authentication, but server * rejected it (since we're again here). Exit with an error since we * can't invent anything better */ Curl_cleanup_negotiate(data); return -1; } if(neg_ctx->server_name == NULL && (ret = get_gss_name(conn, proxy, &neg_ctx->server_name))) return ret; header += strlen(neg_ctx->protocol); while(*header && ISSPACE(*header)) header++; len = strlen(header); if(len > 0) { error = Curl_base64_decode(header, (unsigned char **)&input_token.value, &rawlen); if(error || rawlen == 0) return -1; input_token.length = rawlen; #ifdef HAVE_SPNEGO /* Handle SPNEGO */ if(checkprefix("Negotiate", header)) { ASN1_OBJECT * object = NULL; unsigned char * spnegoToken = NULL; size_t spnegoTokenLength = 0; unsigned char * mechToken = NULL; size_t mechTokenLength = 0; if(input_token.value == NULL) return CURLE_OUT_OF_MEMORY; spnegoToken = malloc(input_token.length); if(spnegoToken == NULL) return CURLE_OUT_OF_MEMORY; spnegoTokenLength = input_token.length; object = OBJ_txt2obj ("1.2.840.113554.1.2.2", 1); if(!parseSpnegoTargetToken(spnegoToken, spnegoTokenLength, NULL, NULL, &mechToken, &mechTokenLength, NULL, NULL)) { free(spnegoToken); spnegoToken = NULL; infof(data, "Parse SPNEGO Target Token failed\n"); } else { free(input_token.value); input_token.value = malloc(mechTokenLength); if(input_token.value == NULL) return CURLE_OUT_OF_MEMORY; memcpy(input_token.value, mechToken,mechTokenLength); input_token.length = mechTokenLength; free(mechToken); mechToken = NULL; infof(data, "Parse SPNEGO Target Token succeeded\n"); } } #endif } major_status = Curl_gss_init_sec_context(data, &minor_status, &neg_ctx->context, neg_ctx->server_name, GSS_C_NO_CHANNEL_BINDINGS, &input_token, &output_token, NULL); if(input_token.length > 0) gss_release_buffer(&minor_status2, &input_token); neg_ctx->status = major_status; if(GSS_ERROR(major_status)) { /* Curl_cleanup_negotiate(data) ??? */ log_gss_error(conn, minor_status, "gss_init_sec_context() failed: "); return -1; } if(output_token.length == 0) { return -1; } neg_ctx->output_token = output_token; /* conn->bits.close = FALSE; */ return 0; }
static CURLcode dict_do(struct connectdata *conn, bool *done) { char *word; char *eword; char *ppath; char *database = NULL; char *strategy = NULL; char *nthdef = NULL; /* This is not part of the protocol, but required by RFC 2229 */ CURLcode result=CURLE_OK; struct SessionHandle *data=conn->data; curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; char *path = data->state.path; curl_off_t *bytecount = &data->req.bytecount; *done = TRUE; /* unconditionally */ if(conn->bits.user_passwd) { /* AUTH is missing */ } if(Curl_raw_nequal(path, DICT_MATCH, sizeof(DICT_MATCH)-1) || Curl_raw_nequal(path, DICT_MATCH2, sizeof(DICT_MATCH2)-1) || Curl_raw_nequal(path, DICT_MATCH3, sizeof(DICT_MATCH3)-1)) { word = strchr(path, ':'); if(word) { word++; database = strchr(word, ':'); if(database) { *database++ = (char)0; strategy = strchr(database, ':'); if(strategy) { *strategy++ = (char)0; nthdef = strchr(strategy, ':'); if(nthdef) { *nthdef = (char)0; } } } } if((word == NULL) || (*word == (char)0)) { infof(data, "lookup word is missing"); word=(char *)"default"; } if((database == NULL) || (*database == (char)0)) { database = (char *)"!"; } if((strategy == NULL) || (*strategy == (char)0)) { strategy = (char *)"."; } eword = unescape_word(data, word); if(!eword) return CURLE_OUT_OF_MEMORY; result = Curl_sendf(sockfd, conn, "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n" "MATCH " "%s " /* database */ "%s " /* strategy */ "%s\r\n" /* word */ "QUIT\r\n", database, strategy, eword ); free(eword); if(result) { failf(data, "Failed sending DICT request"); return result; } Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount, -1, NULL); /* no upload */ } else if(Curl_raw_nequal(path, DICT_DEFINE, sizeof(DICT_DEFINE)-1) || Curl_raw_nequal(path, DICT_DEFINE2, sizeof(DICT_DEFINE2)-1) || Curl_raw_nequal(path, DICT_DEFINE3, sizeof(DICT_DEFINE3)-1)) { word = strchr(path, ':'); if(word) { word++; database = strchr(word, ':'); if(database) { *database++ = (char)0; nthdef = strchr(database, ':'); if(nthdef) { *nthdef = (char)0; } } } if((word == NULL) || (*word == (char)0)) { infof(data, "lookup word is missing"); word=(char *)"default"; } if((database == NULL) || (*database == (char)0)) { database = (char *)"!"; } eword = unescape_word(data, word); if(!eword) return CURLE_OUT_OF_MEMORY; result = Curl_sendf(sockfd, conn, "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n" "DEFINE " "%s " /* database */ "%s\r\n" /* word */ "QUIT\r\n", database, eword); free(eword); if(result) { failf(data, "Failed sending DICT request"); return result; } Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount, -1, NULL); /* no upload */ } else { ppath = strchr(path, '/'); if(ppath) { int i; ppath++; for (i = 0; ppath[i]; i++) { if(ppath[i] == ':') ppath[i] = ' '; } result = Curl_sendf(sockfd, conn, "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n" "%s\r\n" "QUIT\r\n", ppath); if(result) { failf(data, "Failed sending DICT request"); return result; } Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount, -1, NULL); } } return CURLE_OK; }
/* * This function logs in to a SOCKS4 proxy and sends the specifics to the final * destination server. * * Reference : * http://socks.permeo.com/protocol/socks4.protocol * * Note : * Nonsupport "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)" * Nonsupport "Identification Protocol (RFC1413)" */ CURLcode Curl_SOCKS4(const char *proxy_name, struct connectdata *conn) { unsigned char socksreq[262]; /* room for SOCKS4 request incl. user id */ int result; CURLcode code; curl_socket_t sock = conn->sock[FIRSTSOCKET]; long timeout; struct SessionHandle *data = conn->data; /* get timeout */ if(data->set.timeout && data->set.connecttimeout) { if (data->set.timeout < data->set.connecttimeout) timeout = data->set.timeout*1000; else timeout = data->set.connecttimeout*1000; } else if(data->set.timeout) timeout = data->set.timeout*1000; else if(data->set.connecttimeout) timeout = data->set.connecttimeout*1000; else timeout = DEFAULT_CONNECT_TIMEOUT; Curl_nonblock(sock, FALSE); /* * Compose socks4 request * * Request format * * +----+----+----+----+----+----+----+----+----+----+....+----+ * | VN | CD | DSTPORT | DSTIP | USERID |NULL| * +----+----+----+----+----+----+----+----+----+----+....+----+ * # of bytes: 1 1 2 4 variable 1 */ socksreq[0] = 4; /* version (SOCKS4) */ socksreq[1] = 1; /* connect */ *((unsigned short*)&socksreq[2]) = htons(conn->remote_port); /* DNS resolve */ { struct Curl_dns_entry *dns; Curl_addrinfo *hp=NULL; int rc; rc = Curl_resolv(conn, conn->host.name, (int)conn->remote_port, &dns); if(rc == CURLRESOLV_ERROR) return CURLE_COULDNT_RESOLVE_PROXY; if(rc == CURLRESOLV_PENDING) /* this requires that we're in "wait for resolve" state */ rc = Curl_wait_for_resolv(conn, &dns); /* * We cannot use 'hostent' as a struct that Curl_resolv() returns. It * returns a Curl_addrinfo pointer that may not always look the same. */ if(dns) hp=dns->addr; if (hp) { char buf[64]; unsigned short ip[4]; Curl_printable_address(hp, buf, sizeof(buf)); if(4 == sscanf( buf, "%hu.%hu.%hu.%hu", &ip[0], &ip[1], &ip[2], &ip[3])) { /* Set DSTIP */ socksreq[4] = (unsigned char)ip[0]; socksreq[5] = (unsigned char)ip[1]; socksreq[6] = (unsigned char)ip[2]; socksreq[7] = (unsigned char)ip[3]; } else hp = NULL; /* fail! */ Curl_resolv_unlock(data, dns); /* not used anymore from now on */ } if(!hp) { failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", conn->host.name); return CURLE_COULDNT_RESOLVE_HOST; } } /* * This is currently not supporting "Identification Protocol (RFC1413)". */ socksreq[8] = 0; /* ensure empty userid is NUL-terminated */ if (proxy_name) strlcat((char*)socksreq + 8, proxy_name, sizeof(socksreq) - 8); /* * Make connection */ { ssize_t actualread; ssize_t written; int packetsize = 9 + (int)strlen((char*)socksreq + 8); /* size including NUL */ /* Send request */ code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written); if ((code != CURLE_OK) || (written != packetsize)) { failf(data, "Failed to send SOCKS4 connect request."); return CURLE_COULDNT_CONNECT; } packetsize = 8; /* receive data size */ /* Receive response */ result = blockread_all(conn, sock, (char *)socksreq, packetsize, &actualread, timeout); if ((result != CURLE_OK) || (actualread != packetsize)) { failf(data, "Failed to receive SOCKS4 connect request ack."); return CURLE_COULDNT_CONNECT; } /* * Response format * * +----+----+----+----+----+----+----+----+ * | VN | CD | DSTPORT | DSTIP | * +----+----+----+----+----+----+----+----+ * # of bytes: 1 1 2 4 * * VN is the version of the reply code and should be 0. CD is the result * code with one of the following values: * * 90: request granted * 91: request rejected or failed * 92: request rejected because SOCKS server cannot connect to * identd on the client * 93: request rejected because the client program and identd * report different user-ids */ /* wrong version ? */ if (socksreq[0] != 0) { failf(data, "SOCKS4 reply has wrong version, version should be 4."); return CURLE_COULDNT_CONNECT; } /* Result */ switch(socksreq[1]) { case 90: infof(data, "SOCKS4 request granted.\n"); break; case 91: failf(data, "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" ", request rejected or failed.", (unsigned char)socksreq[4], (unsigned char)socksreq[5], (unsigned char)socksreq[6], (unsigned char)socksreq[7], (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])), socksreq[1]); return CURLE_COULDNT_CONNECT; case 92: failf(data, "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" ", request rejected because SOCKS server cannot connect to " "identd on the client.", (unsigned char)socksreq[4], (unsigned char)socksreq[5], (unsigned char)socksreq[6], (unsigned char)socksreq[7], (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])), socksreq[1]); return CURLE_COULDNT_CONNECT; case 93: failf(data, "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" ", request rejected because the client program and identd " "report different user-ids.", (unsigned char)socksreq[4], (unsigned char)socksreq[5], (unsigned char)socksreq[6], (unsigned char)socksreq[7], (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])), socksreq[1]); return CURLE_COULDNT_CONNECT; default: failf(data, "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" ", Unknown.", (unsigned char)socksreq[4], (unsigned char)socksreq[5], (unsigned char)socksreq[6], (unsigned char)socksreq[7], (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])), socksreq[1]); return CURLE_COULDNT_CONNECT; } } Curl_nonblock(sock, TRUE); return CURLE_OK; /* Proxy was successful! */ }
static void error_callback(int error, const char *description) { infof("glfw", "error: %s", description); }
bool ctx_init(struct context *ctx, int w, int h, bool debug) { assert(! ctx->win); glfwSetErrorCallback(error_callback); if (! glfwInit()) { return false; } GLFWmonitor *monitor = glfwGetPrimaryMonitor(); const GLFWvidmode *mode = glfwGetVideoMode(monitor); int mw, mh; glfwGetMonitorPhysicalSize(monitor, &mw, &mh); ctx->dpi = mode->width / (mw / 25.4); ctx->hidpi = ctx->dpi > 180.0; ctx->vidmode = mode; if (ctx->hidpi) { w *= 2; h *= 2; } glfwWindowHint(GLFW_RESIZABLE, true); glfwWindowHint(GLFW_SRGB_CAPABLE, true); // Require OpenGL 3.3 or later glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Only support new core functionality glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, true); glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, true); glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); glfwWindowHint(GLFW_SAMPLES, 16); if ((ctx->win = glfwCreateWindow(w, h, "px", NULL, NULL)) == NULL) { return false; } glfwMakeContextCurrent(ctx->win); glfwSetWindowUserPointer(ctx->win, ctx); glfwSetInputMode(ctx->win, GLFW_CURSOR, GLFW_CURSOR_NORMAL); glfwSetInputMode(ctx->win, GLFW_STICKY_KEYS, true); glfwSetKeyCallback(ctx->win, key_callback); glfwSetMouseButtonCallback(ctx->win, mouse_button_callback); glfwSetCursorPosCallback(ctx->win, cursor_pos_callback); glfwSetCursorEnterCallback(ctx->win, focus_callback); glfwSetFramebufferSizeCallback(ctx->win, framebuffer_size_callback); glfwSetWindowPosCallback(ctx->win, window_pos_callback); glfwSetCharCallback(ctx->win, char_callback); glfwSetWindowAspectRatio(ctx->win, w, h); glfwGetFramebufferSize(ctx->win, &ctx->winw, &ctx->winh); ctx_win_center(ctx); gl_init(ctx->winw, ctx->winh, debug); int vw = ctx->winw, /* Virtual screen */ vh = ctx->winh; if (ctx->hidpi) { vw /= 2; vh /= 2; /* We can't create odd-sized framebuffer textures, so we make * the screen framebuffer even in case the virtual screen isn't. */ if (vw % 2 != 0) vw ++; if (vh % 2 != 0) vh ++; } infof("ctx", "real screen size: %dx%d", ctx->winw, ctx->winh); infof("ctx", "virtual screen size: %dx%d", vw, vh); ctx->lastframe = 0; ctx->frametime = 0; ctx->transforms = NULL; ctx->ortho = mat4ortho(ctx->winw, ctx->winh); ctx->font = malloc(sizeof(*ctx->font)); ctx->screen = framebuffer_screen(vw, vh, NULL); ctx->width = ctx->hidpi ? ctx->winw / 2 : ctx->winw; ctx->height = ctx->hidpi ? ctx->winh / 2 : ctx->winh; ctx_update_cursor_pos(ctx); ctx_blend_alpha(ctx); ctx_save(ctx); glfwSetTime(0); infof("ctx", "dpi = %f", ctx->dpi); return true; }
/* * Curl_auth_create_digest_md5_message() * * This is used to generate an already encoded DIGEST-MD5 response message * ready for sending to the recipient. * * Parameters: * * data [in] - The session handle. * chlg64 [in] - The base64 encoded challenge message. * userp [in] - The user name in the format User or Domain\User. * passdwp [in] - The user's password. * service [in] - The service type such as http, smtp, pop or imap. * outptr [in/out] - The address where a pointer to newly allocated memory * holding the result will be stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, const char *chlg64, const char *userp, const char *passwdp, const char *service, char **outptr, size_t *outlen) { CURLcode result = CURLE_OK; TCHAR *spn = NULL; size_t chlglen = 0; size_t token_max = 0; unsigned char *input_token = NULL; unsigned char *output_token = NULL; CredHandle credentials; CtxtHandle context; PSecPkgInfo SecurityPackage; SEC_WINNT_AUTH_IDENTITY identity; SEC_WINNT_AUTH_IDENTITY *p_identity; SecBuffer chlg_buf; SecBuffer resp_buf; SecBufferDesc chlg_desc; SecBufferDesc resp_desc; SECURITY_STATUS status; unsigned long attrs; TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ /* Decode the base-64 encoded challenge message */ if(strlen(chlg64) && *chlg64 != '=') { result = Curl_base64_decode(chlg64, &input_token, &chlglen); if(result) return result; } /* Ensure we have a valid challenge message */ if(!input_token) { infof(data, "DIGEST-MD5 handshake failure (empty challenge message)\n"); return CURLE_BAD_CONTENT_ENCODING; } /* Query the security package for DigestSSP */ status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST), &SecurityPackage); if(status != SEC_E_OK) { free(input_token); return CURLE_NOT_BUILT_IN; } token_max = SecurityPackage->cbMaxToken; /* Release the package buffer as it is not required anymore */ s_pSecFn->FreeContextBuffer(SecurityPackage); /* Allocate our response buffer */ output_token = malloc(token_max); if(!output_token) { free(input_token); return CURLE_OUT_OF_MEMORY; } /* Generate our SPN */ spn = Curl_auth_build_spn(service, data->easy_conn->host.name, NULL); if(!spn) { free(output_token); free(input_token); return CURLE_OUT_OF_MEMORY; } if(userp && *userp) { /* Populate our identity structure */ result = Curl_create_sspi_identity(userp, passwdp, &identity); if(result) { free(spn); free(output_token); free(input_token); return result; } /* Allow proper cleanup of the identity structure */ p_identity = &identity; } else /* Use the current Windows user */ p_identity = NULL; /* Acquire our credentials handle */ status = s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *) TEXT(SP_NAME_DIGEST), SECPKG_CRED_OUTBOUND, NULL, p_identity, NULL, NULL, &credentials, &expiry); if(status != SEC_E_OK) { Curl_sspi_free_identity(p_identity); free(spn); free(output_token); free(input_token); return CURLE_LOGIN_DENIED; } /* Setup the challenge "input" security buffer */ chlg_desc.ulVersion = SECBUFFER_VERSION; chlg_desc.cBuffers = 1; chlg_desc.pBuffers = &chlg_buf; chlg_buf.BufferType = SECBUFFER_TOKEN; chlg_buf.pvBuffer = input_token; chlg_buf.cbBuffer = curlx_uztoul(chlglen); /* Setup the response "output" security buffer */ resp_desc.ulVersion = SECBUFFER_VERSION; resp_desc.cBuffers = 1; resp_desc.pBuffers = &resp_buf; resp_buf.BufferType = SECBUFFER_TOKEN; resp_buf.pvBuffer = output_token; resp_buf.cbBuffer = curlx_uztoul(token_max); /* Generate our response message */ status = s_pSecFn->InitializeSecurityContext(&credentials, NULL, spn, 0, 0, 0, &chlg_desc, 0, &context, &resp_desc, &attrs, &expiry); if(status == SEC_I_COMPLETE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) s_pSecFn->CompleteAuthToken(&credentials, &resp_desc); else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) { s_pSecFn->FreeCredentialsHandle(&credentials); Curl_sspi_free_identity(p_identity); free(spn); free(output_token); free(input_token); return CURLE_RECV_ERROR; } /* Base64 encode the response */ result = Curl_base64_encode(data, (char *) output_token, resp_buf.cbBuffer, outptr, outlen); /* Free our handles */ s_pSecFn->DeleteSecurityContext(&context); s_pSecFn->FreeCredentialsHandle(&credentials); /* Free the identity structure */ Curl_sspi_free_identity(p_identity); /* Free the SPN */ free(spn); /* Free the response buffer */ free(output_token); /* Free the decoded challenge message */ free(input_token); return result; }
static int push_promise(struct SessionHandle *data, struct connectdata *conn, const nghttp2_push_promise *frame) { int rv; DEBUGF(infof(data, "PUSH_PROMISE received, stream %u!\n", frame->promised_stream_id)); if(data->multi->push_cb) { struct HTTP *stream; struct curl_pushheaders heads; CURLMcode rc; struct http_conn *httpc; size_t i; /* clone the parent */ CURL *newhandle = duphandle(data); if(!newhandle) { infof(data, "failed to duplicate handle\n"); rv = 1; /* FAIL HARD */ goto fail; } heads.data = data; heads.frame = frame; /* ask the application */ DEBUGF(infof(data, "Got PUSH_PROMISE, ask application!\n")); stream = data->req.protop; if(!stream) { failf(data, "Internal NULL stream!\n"); rv = 1; goto fail; } rv = data->multi->push_cb(data, newhandle, stream->push_headers_used, &heads, data->multi->push_userp); /* free the headers again */ for(i=0; i<stream->push_headers_used; i++) free(stream->push_headers[i]); free(stream->push_headers); stream->push_headers = NULL; if(rv) { /* denied, kill off the new handle again */ (void)Curl_close(newhandle); goto fail; } /* approved, add to the multi handle and immediately switch to PERFORM state with the given connection !*/ rc = Curl_multi_add_perform(data->multi, newhandle, conn); if(rc) { infof(data, "failed to add handle to multi\n"); Curl_close(newhandle); rv = 1; goto fail; } httpc = &conn->proto.httpc; nghttp2_session_set_stream_user_data(httpc->h2, frame->promised_stream_id, newhandle); } else { DEBUGF(infof(data, "Got PUSH_PROMISE, ignore it!\n")); rv = 1; } fail: return rv; }
CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy) { struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg: &conn->data->state.negotiate; char *encoded = NULL; size_t len = 0; char *userp; CURLcode error; #ifdef HAVE_SPNEGO /* Handle SPNEGO */ if(checkprefix("Negotiate", neg_ctx->protocol)) { ASN1_OBJECT * object = NULL; unsigned char * spnegoToken = NULL; size_t spnegoTokenLength = 0; unsigned char * responseToken = NULL; size_t responseTokenLength = 0; responseToken = malloc(neg_ctx->output_token.length); if(responseToken == NULL) return CURLE_OUT_OF_MEMORY; memcpy(responseToken, neg_ctx->output_token.value, neg_ctx->output_token.length); responseTokenLength = neg_ctx->output_token.length; object=OBJ_txt2obj ("1.2.840.113554.1.2.2", 1); if(!makeSpnegoInitialToken (object, responseToken, responseTokenLength, &spnegoToken, &spnegoTokenLength)) { free(responseToken); responseToken = NULL; infof(conn->data, "Make SPNEGO Initial Token failed\n"); } else { free(responseToken); responseToken = NULL; free(neg_ctx->output_token.value); neg_ctx->output_token.value = malloc(spnegoTokenLength); if(neg_ctx->output_token.value == NULL) { free(spnegoToken); spnegoToken = NULL; return CURLE_OUT_OF_MEMORY; } memcpy(neg_ctx->output_token.value, spnegoToken,spnegoTokenLength); neg_ctx->output_token.length = spnegoTokenLength; free(spnegoToken); spnegoToken = NULL; infof(conn->data, "Make SPNEGO Initial Token succeeded\n"); } } #endif error = Curl_base64_encode(conn->data, neg_ctx->output_token.value, neg_ctx->output_token.length, &encoded, &len); if(error) { Curl_safefree(neg_ctx->output_token.value); neg_ctx->output_token.value = NULL; return error; } if(len == 0) { Curl_safefree(neg_ctx->output_token.value); neg_ctx->output_token.value = NULL; return CURLE_REMOTE_ACCESS_DENIED; } userp = aprintf("%sAuthorization: %s %s\r\n", proxy ? "Proxy-" : "", neg_ctx->protocol, encoded); if(proxy) conn->allocptr.proxyuserpwd = userp; else conn->allocptr.userpwd = userp; free(encoded); Curl_cleanup_negotiate (conn->data); return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK; }
static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, void *userp) { struct connectdata *conn = (struct connectdata *)userp; struct http_conn *httpc = NULL; struct SessionHandle *data_s = NULL; struct HTTP *stream = NULL; static int lastStream = -1; int rv; size_t left, ncopy; int32_t stream_id = frame->hd.stream_id; if(!stream_id) { /* stream ID zero is for connection-oriented stuff */ return 0; } data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); if(lastStream != frame->hd.stream_id) { lastStream = frame->hd.stream_id; } if(!data_s) { DEBUGF(infof(conn->data, "No SessionHandle associated with stream: %x\n", stream_id)); return 0; } stream = data_s->req.protop; if(!stream) return NGHTTP2_ERR_CALLBACK_FAILURE; DEBUGF(infof(data_s, "on_frame_recv() header %x stream %x\n", frame->hd.type, stream_id)); conn = data_s->easy_conn; assert(conn); assert(conn->data == data_s); httpc = &conn->proto.httpc; switch(frame->hd.type) { case NGHTTP2_DATA: /* If body started on this stream, then receiving DATA is illegal. */ if(!stream->bodystarted) { rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id, NGHTTP2_PROTOCOL_ERROR); if(nghttp2_is_fatal(rv)) { return NGHTTP2_ERR_CALLBACK_FAILURE; } } break; case NGHTTP2_HEADERS: if(frame->headers.cat == NGHTTP2_HCAT_REQUEST) break; if(stream->bodystarted) { /* Only valid HEADERS after body started is trailer HEADERS. We ignores trailer HEADERS for now. nghttp2 guarantees that it has END_STREAM flag set. */ break; } /* nghttp2 guarantees that :status is received, and we store it to stream->status_code */ DEBUGASSERT(stream->status_code != -1); /* Only final status code signals the end of header */ if(stream->status_code / 100 != 1) { stream->bodystarted = TRUE; stream->status_code = -1; } Curl_add_buffer(stream->header_recvbuf, "\r\n", 2); left = stream->header_recvbuf->size_used - stream->nread_header_recvbuf; ncopy = MIN(stream->len, left); memcpy(&stream->mem[stream->memlen], stream->header_recvbuf->buffer + stream->nread_header_recvbuf, ncopy); stream->nread_header_recvbuf += ncopy; DEBUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p\n", ncopy, stream_id, stream->mem)); stream->len -= ncopy; stream->memlen += ncopy; data_s->state.drain++; { /* get the pointer from userp again since it was re-assigned above */ struct connectdata *conn_s = (struct connectdata *)userp; /* if we receive data for another handle, wake that up */ if(conn_s->data != data_s) Curl_expire(data_s, 1); } break; case NGHTTP2_PUSH_PROMISE: rv = push_promise(data_s, conn, &frame->push_promise); if(rv) { /* deny! */ rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->push_promise.promised_stream_id, NGHTTP2_CANCEL); if(nghttp2_is_fatal(rv)) { return rv; } } break; case NGHTTP2_SETTINGS: { uint32_t max_conn = httpc->settings.max_concurrent_streams; DEBUGF(infof(conn->data, "Got SETTINGS for stream %u!\n", stream_id)); httpc->settings.max_concurrent_streams = nghttp2_session_get_remote_settings( session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); httpc->settings.enable_push = nghttp2_session_get_remote_settings( session, NGHTTP2_SETTINGS_ENABLE_PUSH); DEBUGF(infof(conn->data, "MAX_CONCURRENT_STREAMS == %d\n", httpc->settings.max_concurrent_streams)); DEBUGF(infof(conn->data, "ENABLE_PUSH == %s\n", httpc->settings.enable_push?"TRUE":"false")); if(max_conn != httpc->settings.max_concurrent_streams) { /* only signal change if the value actually changed */ infof(conn->data, "Connection state changed (MAX_CONCURRENT_STREAMS updated)!\n"); Curl_multi_connchanged(conn->data->multi); } } break; default: DEBUGF(infof(conn->data, "Got frame type %x for stream %u!\n", frame->hd.type, stream_id)); break; } return 0; }
CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) { PRInt32 err; PRFileDesc *model = NULL; PRBool ssl2, ssl3, tlsv1; struct SessionHandle *data = conn->data; curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; SECStatus rv; #ifdef HAVE_PK11_CREATEGENERICOBJECT char *configstring = NULL; #endif char *certDir = NULL; int curlerr; curlerr = CURLE_SSL_CONNECT_ERROR; /* FIXME. NSS doesn't support multiple databases open at the same time. */ if(!initialized) { initialized = 1; certDir = getenv("SSL_DIR"); /* Look in $SSL_DIR */ if (!certDir) { struct stat st; if (stat(SSL_DIR, &st) == 0) if (S_ISDIR(st.st_mode)) { certDir = (char *)SSL_DIR; } } if(!certDir) { rv = NSS_NoDB_Init(NULL); } else { rv = NSS_Initialize(certDir, NULL, NULL, "secmod.db", NSS_INIT_READONLY); } if(rv != SECSuccess) { infof(conn->data, "Unable to initialize NSS database\n"); curlerr = CURLE_SSL_CACERT_BADFILE; goto error; } NSS_SetDomesticPolicy(); #ifdef HAVE_PK11_CREATEGENERICOBJECT configstring = (char *)malloc(PATH_MAX); PR_snprintf(configstring, PATH_MAX, "library=%s name=PEM", pem_library); mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE); free(configstring); if (!mod || !mod->loaded) { if (mod) { SECMOD_DestroyModule(mod); mod = NULL; } infof(data, "WARNING: failed to load NSS PEM library %s. Using OpenSSL " "PEM certificates will not work.\n", pem_library); } #endif } model = PR_NewTCPSocket(); if(!model) goto error; model = SSL_ImportFD(NULL, model); if(SSL_OptionSet(model, SSL_SECURITY, PR_TRUE) != SECSuccess) goto error; if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) != SECSuccess) goto error; if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) != SECSuccess) goto error; ssl2 = ssl3 = tlsv1 = PR_FALSE; switch (data->set.ssl.version) { default: case CURL_SSLVERSION_DEFAULT: ssl2 = ssl3 = tlsv1 = PR_TRUE; break; case CURL_SSLVERSION_TLSv1: tlsv1 = PR_TRUE; break; case CURL_SSLVERSION_SSLv2: ssl2 = PR_TRUE; break; case CURL_SSLVERSION_SSLv3: ssl3 = PR_TRUE; break; } if(SSL_OptionSet(model, SSL_ENABLE_SSL2, ssl2) != SECSuccess) goto error; if(SSL_OptionSet(model, SSL_ENABLE_SSL3, ssl3) != SECSuccess) goto error; if(SSL_OptionSet(model, SSL_ENABLE_TLS, tlsv1) != SECSuccess) goto error; if(data->set.ssl.cipher_list) { if(set_ciphers(data, model, data->set.ssl.cipher_list) != SECSuccess) { curlerr = CURLE_SSL_CIPHER; goto error; } } data->set.ssl.certverifyresult=0; /* not checked yet */ if(SSL_BadCertHook(model, (SSLBadCertHandler) BadCertHandler, conn) != SECSuccess) { goto error; } if(SSL_HandshakeCallback(model, (SSLHandshakeCallback) HandshakeCallback, NULL) != SECSuccess) goto error; if(!data->set.ssl.verifypeer) /* skip the verifying of the peer */ ; else if (data->set.ssl.CAfile) { int rc = nss_load_cert(data->set.ssl.CAfile, PR_TRUE); if (!rc) { curlerr = CURLE_SSL_CACERT_BADFILE; goto error; } } else if (data->set.ssl.CApath) { struct stat st; PRDir *dir; PRDirEntry *entry; if (stat(data->set.ssl.CApath, &st) == -1) { curlerr = CURLE_SSL_CACERT_BADFILE; goto error; } if (S_ISDIR(st.st_mode)) { int rc; dir = PR_OpenDir(data->set.ssl.CApath); do { entry = PR_ReadDir(dir, PR_SKIP_BOTH | PR_SKIP_HIDDEN); if (entry) { char fullpath[PATH_MAX]; snprintf(fullpath, sizeof(fullpath), "%s/%s", data->set.ssl.CApath, entry->name); rc = nss_load_cert(fullpath, PR_TRUE); /* FIXME: check this return value! */ } /* This is purposefully tolerant of errors so non-PEM files * can be in the same directory */ } while (entry != NULL); PR_CloseDir(dir); } } infof(data, " CAfile: %s\n" " CApath: %s\n", data->set.ssl.CAfile ? data->set.ssl.CAfile : "none", data->set.ssl.CApath ? data->set.ssl.CApath : "none"); if(data->set.str[STRING_CERT]) { char *n; char *nickname; nickname = (char *)malloc(PATH_MAX); if(is_file(data->set.str[STRING_CERT])) { n = strrchr(data->set.str[STRING_CERT], '/'); if (n) { n++; /* skip last slash */ snprintf(nickname, PATH_MAX, "PEM Token #%ld:%s", 1, n); } } else { strncpy(nickname, data->set.str[STRING_CERT], PATH_MAX); } if(nss_Init_Tokens(conn) != SECSuccess) { free(nickname); goto error; } if (!cert_stuff(conn, data->set.str[STRING_CERT], data->set.str[STRING_KEY])) { /* failf() is already done in cert_stuff() */ free(nickname); return CURLE_SSL_CERTPROBLEM; } connssl->client_nickname = strdup(nickname); if(SSL_GetClientAuthDataHook(model, (SSLGetClientAuthData) SelectClientCert, (void *)connssl->client_nickname) != SECSuccess) { curlerr = CURLE_SSL_CERTPROBLEM; goto error; } free(nickname); PK11_SetPasswordFunc(nss_no_password); } else connssl->client_nickname = NULL; /* Import our model socket onto the existing file descriptor */ connssl->handle = PR_ImportTCPSocket(sockfd); connssl->handle = SSL_ImportFD(model, connssl->handle); if(!connssl->handle) goto error; PR_Close(model); /* We don't need this any more */ /* Force handshake on next I/O */ SSL_ResetHandshake(connssl->handle, /* asServer */ PR_FALSE); SSL_SetURL(connssl->handle, conn->host.name); /* Force the handshake now */ if (SSL_ForceHandshakeWithTimeout(connssl->handle, PR_SecondsToInterval(HANDSHAKE_TIMEOUT)) != SECSuccess) { if (conn->data->set.ssl.certverifyresult!=0) curlerr = CURLE_SSL_CACERT; goto error; } display_conn_info(conn, connssl->handle); return CURLE_OK; error: err = PR_GetError(); infof(data, "NSS error %d\n", err); if(model) PR_Close(model); return curlerr; }
/* * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return * a regular CURLcode value. */ static ssize_t http2_recv(struct connectdata *conn, int sockindex, char *mem, size_t len, CURLcode *err) { CURLcode result = CURLE_OK; ssize_t rv; ssize_t nread; struct http_conn *httpc = &conn->proto.httpc; struct SessionHandle *data = conn->data; struct HTTP *stream = data->req.protop; (void)sockindex; /* we always do HTTP2 on sockindex 0 */ /* If stream is closed, return 0 to signal the http routine to close the connection. We need to handle stream closure here, otherwise, we may be going to read from underlying connection, and gets EAGAIN, and we will get stuck there. */ if(stream->memlen == 0 && stream->closed) { return http2_handle_stream_close(httpc, data, stream, err); } /* Nullify here because we call nghttp2_session_send() and they might refer to the old buffer. */ stream->upload_mem = NULL; stream->upload_len = 0; /* * At this point 'stream' is just in the SessionHandle the connection * identifies as its owner at this time. */ if(stream->bodystarted && stream->nread_header_recvbuf < stream->header_recvbuf->size_used) { /* If there is body data pending for this stream to return, do that */ size_t left = stream->header_recvbuf->size_used - stream->nread_header_recvbuf; size_t ncopy = MIN(len, left); memcpy(mem, stream->header_recvbuf->buffer + stream->nread_header_recvbuf, ncopy); stream->nread_header_recvbuf += ncopy; infof(data, "http2_recv: Got %d bytes from header_recvbuf\n", (int)ncopy); return ncopy; } infof(data, "http2_recv: %d bytes buffer at %p (stream %u)\n", len, mem, stream->stream_id); if((data->state.drain) && stream->memlen) { DEBUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u!! (%p => %p)\n", stream->memlen, stream->stream_id, stream->mem, mem)); if(mem != stream->mem) { /* if we didn't get the same buffer this time, we must move the data to the beginning */ memmove(mem, stream->mem, stream->memlen); stream->len = len - stream->memlen; stream->mem = mem; } } else if(stream->pausedata) { nread = MIN(len, stream->pauselen); memcpy(mem, stream->pausedata, nread); stream->pausedata += nread; stream->pauselen -= nread; infof(data, "%zu data bytes written\n", nread); if(stream->pauselen == 0) { DEBUGF(infof(data, "Unpaused by stream %u\n", stream->stream_id)); assert(httpc->pause_stream_id == stream->stream_id); httpc->pause_stream_id = 0; stream->pausedata = NULL; stream->pauselen = 0; } infof(data, "http2_recv: returns unpaused %zd bytes on stream %u\n", nread, stream->stream_id); return nread; } else if(httpc->pause_stream_id) { /* If a stream paused nghttp2_session_mem_recv previously, and has not processed all data, it still refers to the buffer in nghttp2_session. If we call nghttp2_session_mem_recv(), we may overwrite that buffer. To avoid that situation, just return here with CURLE_AGAIN. This could be busy loop since data in socket is not read. But it seems that usually streams are notified with its drain property, and socket is read again quickly. */ *err = CURLE_AGAIN; return -1; } else { char *inbuf; /* remember where to store incoming data for this stream and how big the buffer is */ stream->mem = mem; stream->len = len; stream->memlen = 0; if(httpc->inbuflen == 0) { nread = ((Curl_recv *)httpc->recv_underlying)( conn, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result); if(result == CURLE_AGAIN) { *err = result; return -1; } if(nread == -1) { failf(data, "Failed receiving HTTP2 data"); *err = result; return 0; } if(nread == 0) { failf(data, "Unexpected EOF"); *err = CURLE_RECV_ERROR; return -1; } DEBUGF(infof(data, "nread=%zd\n", nread)); httpc->inbuflen = nread; inbuf = httpc->inbuf; } else { nread = httpc->inbuflen - httpc->nread_inbuf; inbuf = httpc->inbuf + httpc->nread_inbuf; DEBUGF(infof(data, "Use data left in connection buffer, nread=%zd\n", nread)); } rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread); if(nghttp2_is_fatal((int)rv)) { failf(data, "nghttp2_session_mem_recv() returned %d:%s\n", rv, nghttp2_strerror((int)rv)); *err = CURLE_RECV_ERROR; return 0; } DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", rv)); if(nread == rv) { DEBUGF(infof(data, "All data in connection buffer processed\n")); httpc->inbuflen = 0; httpc->nread_inbuf = 0; } else { httpc->nread_inbuf += rv; DEBUGF(infof(data, "%zu bytes left in connection buffer\n", httpc->inbuflen - httpc->nread_inbuf)); } /* Always send pending frames in nghttp2 session, because nghttp2_session_mem_recv() may queue new frame */ rv = nghttp2_session_send(httpc->h2); if(rv != 0) { *err = CURLE_SEND_ERROR; return 0; } } if(stream->memlen) { ssize_t retlen = stream->memlen; infof(data, "http2_recv: returns %zd for stream %u\n", retlen, stream->stream_id); stream->memlen = 0; if(httpc->pause_stream_id == stream->stream_id) { /* data for this stream is returned now, but this stream caused a pause already so we need it called again asap */ DEBUGF(infof(data, "Data returned for PAUSED stream %u\n", stream->stream_id)); } else data->state.drain = 0; /* this stream is hereby drained */ return retlen; } /* If stream is closed, return 0 to signal the http routine to close the connection */ if(stream->closed) { return http2_handle_stream_close(httpc, data, stream, err); } *err = CURLE_AGAIN; DEBUGF(infof(data, "http2_recv returns AGAIN for stream %u\n", stream->stream_id)); return -1; }
int Curl_resolv(struct connectdata *conn, const char *hostname, int port, struct Curl_dns_entry **entry) { struct Curl_dns_entry *dns = NULL; struct SessionHandle *data = conn->data; CURLcode result; int rc = CURLRESOLV_ERROR; /* default to failure */ *entry = NULL; if(data->share) Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); dns = Curl_fetch_addr(conn, hostname, port); if(dns) { infof(data, "Hostname %s was found in DNS cache\n", hostname); dns->inuse++; /* we use it! */ rc = CURLRESOLV_RESOLVED; } if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); if(!dns) { /* The entry was not in the cache. Resolve it to IP address */ Curl_addrinfo *addr; int respwait; /* Check what IP specifics the app has requested and if we can provide it. * If not, bail out. */ if(!Curl_ipvalid(conn)) return CURLRESOLV_ERROR; /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a non-zero value indicating that we need to wait for the response to the resolve call */ addr = Curl_getaddrinfo(conn, #ifdef DEBUGBUILD (data->set.str[STRING_DEVICE] && !strcmp(data->set.str[STRING_DEVICE], "LocalHost"))?"localhost": #endif hostname, port, &respwait); if(!addr) { if(respwait) { /* the response to our resolve call will come asynchronously at a later time, good or bad */ /* First, check that we haven't received the info by now */ result = Curl_resolver_is_resolved(conn, &dns); if(result) /* error detected */ return CURLRESOLV_ERROR; if(dns) rc = CURLRESOLV_RESOLVED; /* pointer provided */ else rc = CURLRESOLV_PENDING; /* no info yet */ } } else { if(data->share) Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); /* we got a response, store it in the cache */ dns = Curl_cache_addr(data, addr, hostname, port); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); if(!dns) /* returned failure, bail out nicely */ Curl_freeaddrinfo(addr); else rc = CURLRESOLV_RESOLVED; } } *entry = dns; return rc; }
/* ====================================================== */ CURLcode Curl_SSLConnect(struct connectdata *conn) { CURLcode retcode = CURLE_OK; #ifdef USE_SSLEAY struct SessionHandle *data = conn->data; int err; char * str; SSL_METHOD *req_method; SSL_SESSION *ssl_sessionid=NULL; ASN1_TIME *certdate; /* mark this is being ssl enabled from here on out. */ conn->ssl.use = TRUE; if(!ssl_seeded || data->set.ssl.random_file || data->set.ssl.egdsocket) { /* Make funny stuff to get random input */ random_the_seed(data); ssl_seeded = TRUE; } /* check to see if we've been told to use an explicit SSL/TLS version */ switch(data->set.ssl.version) { default: case CURL_SSLVERSION_DEFAULT: /* we try to figure out version */ req_method = SSLv23_client_method(); break; case CURL_SSLVERSION_TLSv1: req_method = TLSv1_client_method(); break; case CURL_SSLVERSION_SSLv2: req_method = SSLv2_client_method(); break; case CURL_SSLVERSION_SSLv3: req_method = SSLv3_client_method(); break; } conn->ssl.ctx = SSL_CTX_new(req_method); if(!conn->ssl.ctx) { failf(data, "SSL: couldn't create a context!"); return CURLE_OUT_OF_MEMORY; } if(data->set.cert) { if (!cert_stuff(conn, data->set.cert, data->set.cert_type, data->set.key, data->set.key_type)) { /* failf() is already done in cert_stuff() */ return CURLE_SSL_CONNECT_ERROR; } } if(data->set.ssl.cipher_list) { if (!SSL_CTX_set_cipher_list(conn->ssl.ctx, data->set.ssl.cipher_list)) { failf(data, "failed setting cipher list"); return CURLE_SSL_CONNECT_ERROR; } } if(data->set.ssl.verifypeer){ SSL_CTX_set_verify(conn->ssl.ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT| SSL_VERIFY_CLIENT_ONCE, cert_verify_callback); if (!SSL_CTX_load_verify_locations(conn->ssl.ctx, data->set.ssl.CAfile, data->set.ssl.CApath)) { failf(data,"error setting cerficate verify locations"); return CURLE_SSL_CONNECT_ERROR; } } else SSL_CTX_set_verify(conn->ssl.ctx, SSL_VERIFY_NONE, cert_verify_callback); /* Lets make an SSL structure */ conn->ssl.handle = SSL_new (conn->ssl.ctx); SSL_set_connect_state (conn->ssl.handle); conn->ssl.server_cert = 0x0; if(!conn->bits.reuse) { /* We're not re-using a connection, check if there's a cached ID we can/should use here! */ if(!Get_SSL_Session(conn, &ssl_sessionid)) { /* we got a session id, use it! */ SSL_set_session(conn->ssl.handle, ssl_sessionid); /* Informational message */ infof (data, "SSL re-using session ID\n"); } } /* pass the raw socket into the SSL layers */ SSL_set_fd(conn->ssl.handle, conn->firstsocket); do { int what; fd_set writefd; fd_set readfd; struct timeval interval; long timeout_ms; err = SSL_connect(conn->ssl.handle); what = SSL_get_error(conn->ssl.handle, err); FD_ZERO(&writefd); FD_ZERO(&readfd); if(SSL_ERROR_WANT_READ == what) FD_SET(conn->firstsocket, &readfd); else if(SSL_ERROR_WANT_WRITE == what) FD_SET(conn->firstsocket, &writefd); else break; /* untreated error */ /* Find out if any timeout is set. If not, use 300 seconds. Otherwise, figure out the most strict timeout of the two possible one and then how much time that has elapsed to know how much time we allow for the connect call */ if(data->set.timeout || data->set.connecttimeout) { double has_passed; /* Evaluate in milliseconds how much time that has passed */ has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start); #ifndef min #define min(a, b) ((a) < (b) ? (a) : (b)) #endif /* get the most strict timeout of the ones converted to milliseconds */ if(data->set.timeout && (data->set.timeout>data->set.connecttimeout)) timeout_ms = data->set.timeout*1000; else timeout_ms = data->set.connecttimeout*1000; /* subtract the passed time */ timeout_ms -= (long)has_passed; if(timeout_ms < 0) { /* a precaution, no need to continue if time already is up */ failf(data, "SSL connection timeout"); return CURLE_OPERATION_TIMEOUTED; } } else /* no particular time-out has been set */ timeout_ms=300000; /* milliseconds, default to five minutes */ interval.tv_sec = timeout_ms/1000; timeout_ms -= interval.tv_sec*1000; interval.tv_usec = timeout_ms*1000; what = select(conn->firstsocket+1, &readfd, &writefd, NULL, &interval); if(what > 0) /* reabable or writable, go loop yourself */ continue; else if(0 == what) { /* timeout */ failf(data, "SSL connection timeout"); return CURLE_OPERATION_TIMEOUTED; } else break; /* get out of loop */ } while(1); /* 1 is fine 0 is "not successful but was shut down controlled" <0 is "handshake was not successful, because a fatal error occurred" */ if (err <= 0) { err = ERR_get_error(); failf(data, "SSL: %s", ERR_error_string(err, NULL)); return CURLE_SSL_CONNECT_ERROR; } /* Informational message */ infof (data, "SSL connection using %s\n", SSL_get_cipher(conn->ssl.handle)); if(!ssl_sessionid) { /* Since this is not a cached session ID, then we want to stach this one in the cache! */ Store_SSL_Session(conn); } /* Get server's certificate (note: beware of dynamic allocation) - opt */ /* major serious hack alert -- we should check certificates * to authenticate the server; otherwise we risk man-in-the-middle * attack */ conn->ssl.server_cert = SSL_get_peer_certificate (conn->ssl.handle); if(!conn->ssl.server_cert) { failf(data, "SSL: couldn't get peer certificate!"); return CURLE_SSL_PEER_CERTIFICATE; } infof (data, "Server certificate:\n"); str = X509_NAME_oneline (X509_get_subject_name (conn->ssl.server_cert), NULL, 0); if(!str) { failf(data, "SSL: couldn't get X509-subject!"); X509_free(conn->ssl.server_cert); return CURLE_SSL_CONNECT_ERROR; } infof(data, "\t subject: %s\n", str); CRYPTO_free(str); certdate = X509_get_notBefore(conn->ssl.server_cert); Curl_ASN1_UTCTIME_output(conn, "\t start date: ", certdate); certdate = X509_get_notAfter(conn->ssl.server_cert); Curl_ASN1_UTCTIME_output(conn, "\t expire date: ", certdate); if (data->set.ssl.verifyhost) { char peer_CN[257]; if (X509_NAME_get_text_by_NID(X509_get_subject_name(conn->ssl.server_cert), NID_commonName, peer_CN, sizeof(peer_CN)) < 0) { failf(data, "SSL: unable to obtain common name from peer certificate"); X509_free(conn->ssl.server_cert); return CURLE_SSL_PEER_CERTIFICATE; } if (!strequal(peer_CN, conn->hostname)) { if (data->set.ssl.verifyhost > 1) { failf(data, "SSL: certificate subject name '%s' does not match " "target host name '%s'", peer_CN, conn->hostname); X509_free(conn->ssl.server_cert); return CURLE_SSL_PEER_CERTIFICATE; } else infof(data, "\t common name: %s (does not match '%s')\n", peer_CN, conn->hostname); } else infof(data, "\t common name: %s (matched)\n", peer_CN); } str = X509_NAME_oneline (X509_get_issuer_name (conn->ssl.server_cert), NULL, 0); if(!str) { failf(data, "SSL: couldn't get X509-issuer name!"); X509_free(conn->ssl.server_cert); return CURLE_SSL_CONNECT_ERROR; } infof(data, "\t issuer: %s\n", str); CRYPTO_free(str); /* We could do all sorts of certificate verification stuff here before deallocating the certificate. */ if(data->set.ssl.verifypeer) { data->set.ssl.certverifyresult=SSL_get_verify_result(conn->ssl.handle); if (data->set.ssl.certverifyresult != X509_V_OK) { failf(data, "SSL certificate verify result: %d", data->set.ssl.certverifyresult); retcode = CURLE_SSL_PEER_CERTIFICATE; } } else data->set.ssl.certverifyresult=0; X509_free(conn->ssl.server_cert); #else /* USE_SSLEAY */ /* this is for "-ansi -Wall -pedantic" to stop complaining! (rabe) */ (void) conn; #endif return retcode; }
int Curl_resolv_timeout(struct connectdata *conn, const char *hostname, int port, struct Curl_dns_entry **entry, long timeoutms) { #ifdef USE_ALARM_TIMEOUT #ifdef HAVE_SIGACTION struct sigaction keep_sigact; /* store the old struct here */ volatile bool keep_copysig = FALSE; /* wether old sigact has been saved */ struct sigaction sigact; #else #ifdef HAVE_SIGNAL void (*keep_sigact)(int); /* store the old handler here */ #endif /* HAVE_SIGNAL */ #endif /* HAVE_SIGACTION */ volatile long timeout; volatile unsigned int prev_alarm = 0; struct SessionHandle *data = conn->data; #endif /* USE_ALARM_TIMEOUT */ int rc; *entry = NULL; if(timeoutms < 0) /* got an already expired timeout */ return CURLRESOLV_TIMEDOUT; #ifdef USE_ALARM_TIMEOUT if(data->set.no_signal) /* Ignore the timeout when signals are disabled */ timeout = 0; else timeout = timeoutms; if(!timeout) /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */ return Curl_resolv(conn, hostname, port, entry); if(timeout < 1000) /* The alarm() function only provides integer second resolution, so if we want to wait less than one second we must bail out already now. */ return CURLRESOLV_TIMEDOUT; /* This allows us to time-out from the name resolver, as the timeout will generate a signal and we will siglongjmp() from that here. This technique has problems (see alarmfunc). This should be the last thing we do before calling Curl_resolv(), as otherwise we'd have to worry about variables that get modified before we invoke Curl_resolv() (and thus use "volatile"). */ if(sigsetjmp(curl_jmpenv, 1)) { /* this is coming from a siglongjmp() after an alarm signal */ failf(data, "name lookup timed out"); rc = CURLRESOLV_ERROR; goto clean_up; } else { /************************************************************* * Set signal handler to catch SIGALRM * Store the old value to be able to set it back later! *************************************************************/ #ifdef HAVE_SIGACTION sigaction(SIGALRM, NULL, &sigact); keep_sigact = sigact; keep_copysig = TRUE; /* yes, we have a copy */ sigact.sa_handler = alarmfunc; #ifdef SA_RESTART /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */ sigact.sa_flags &= ~SA_RESTART; #endif /* now set the new struct */ sigaction(SIGALRM, &sigact, NULL); #else /* HAVE_SIGACTION */ /* no sigaction(), revert to the much lamer signal() */ #ifdef HAVE_SIGNAL keep_sigact = signal(SIGALRM, alarmfunc); #endif #endif /* HAVE_SIGACTION */ /* alarm() makes a signal get sent when the timeout fires off, and that will abort system calls */ prev_alarm = alarm(curlx_sltoui(timeout/1000L)); } #else #ifndef CURLRES_ASYNCH if(timeoutms) infof(conn->data, "timeout on name lookup is not supported\n"); #else (void)timeoutms; /* timeoutms not used with an async resolver */ #endif #endif /* USE_ALARM_TIMEOUT */ /* Perform the actual name resolution. This might be interrupted by an * alarm if it takes too long. */ rc = Curl_resolv(conn, hostname, port, entry); #ifdef USE_ALARM_TIMEOUT clean_up: if(!prev_alarm) /* deactivate a possibly active alarm before uninstalling the handler */ alarm(0); #ifdef HAVE_SIGACTION if(keep_copysig) { /* we got a struct as it looked before, now put that one back nice and clean */ sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */ } #else #ifdef HAVE_SIGNAL /* restore the previous SIGALRM handler */ signal(SIGALRM, keep_sigact); #endif #endif /* HAVE_SIGACTION */ /* switch back the alarm() to either zero or to what it was before minus the time we spent until now! */ if(prev_alarm) { /* there was an alarm() set before us, now put it back */ unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created); /* the alarm period is counted in even number of seconds */ unsigned long alarm_set = prev_alarm - elapsed_ms/1000; if(!alarm_set || ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) { /* if the alarm time-left reached zero or turned "negative" (counted with unsigned values), we should fire off a SIGALRM here, but we won't, and zero would be to switch it off so we never set it to less than 1! */ alarm(1); rc = CURLRESOLV_TIMEDOUT; failf(data, "Previous alarm fired off!"); } else alarm((unsigned int)alarm_set); } #endif /* USE_ALARM_TIMEOUT */ return rc; }
static CURLcode bindlocal(struct connectdata *conn, curl_socket_t sockfd, int af) { struct SessionHandle *data = conn->data; struct Curl_sockaddr_storage sa; struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */ curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */ struct sockaddr_in *si4 = (struct sockaddr_in *)&sa; #ifdef ENABLE_IPV6 struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa; #endif struct Curl_dns_entry *h=NULL; unsigned short port = data->set.localport; /* use this port number, 0 for "random" */ /* how many port numbers to try to bind to, increasing one at a time */ int portnum = data->set.localportrange; const char *dev = data->set.str[STRING_DEVICE]; int error; char myhost[256] = ""; int done = 0; /* -1 for error, 1 for address found */ /************************************************************* * Select device to bind socket to *************************************************************/ if(!dev && !port) /* no local kind of binding was requested */ return CURLE_OK; memset(&sa, 0, sizeof(struct Curl_sockaddr_storage)); if(dev && (strlen(dev)<255) ) { /* interface */ if(Curl_if2ip(af, dev, myhost, sizeof(myhost))) { /* * We now have the numerical IP address in the 'myhost' buffer */ infof(data, "Local Interface %s is ip %s using address family %i\n", dev, myhost, af); done = 1; #ifdef SO_BINDTODEVICE /* I am not sure any other OSs than Linux that provide this feature, and * at the least I cannot test. --Ben * * This feature allows one to tightly bind the local socket to a * particular interface. This will force even requests to other local * interfaces to go out the external interface. * * * Only bind to the interface when specified as interface, not just as a * hostname or ip address. */ if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, dev, (curl_socklen_t)strlen(dev)+1) != 0) { error = SOCKERRNO; infof(data, "SO_BINDTODEVICE %s failed with errno %d: %s;" " will do regular bind\n", dev, error, Curl_strerror(conn, error)); /* This is typically "errno 1, error: Operation not permitted" if you're not running as root or another suitable privileged user */ } #endif } else { /* * This was not an interface, resolve the name as a host name * or IP number * * Temporarily force name resolution to use only the address type * of the connection. The resolve functions should really be changed * to take a type parameter instead. */ long ipver = conn->ip_version; int rc; if(af == AF_INET) conn->ip_version = CURL_IPRESOLVE_V4; #ifdef ENABLE_IPV6 else if(af == AF_INET6) conn->ip_version = CURL_IPRESOLVE_V6; #endif rc = Curl_resolv(conn, dev, 0, &h); if(rc == CURLRESOLV_PENDING) (void)Curl_resolver_wait_resolv(conn, &h); conn->ip_version = ipver; if(h) { /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */ Curl_printable_address(h->addr, myhost, sizeof(myhost)); infof(data, "Name '%s' family %i resolved to '%s' family %i\n", dev, af, myhost, h->addr->ai_family); Curl_resolv_unlock(data, h); done = 1; } else { /* * provided dev was no interface (or interfaces are not supported * e.g. solaris) no ip address and no domain we fail here */ done = -1; } } if(done > 0) { #ifdef ENABLE_IPV6 /* ipv6 address */ if((af == AF_INET6) && (Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0)) { si6->sin6_family = AF_INET6; si6->sin6_port = htons(port); sizeof_sa = sizeof(struct sockaddr_in6); } else #endif /* ipv4 address */ if((af == AF_INET) && (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) { si4->sin_family = AF_INET; si4->sin_port = htons(port); sizeof_sa = sizeof(struct sockaddr_in); } } if(done < 1) { failf(data, "Couldn't bind to '%s'", dev); return CURLE_INTERFACE_FAILED; } } else { /* no device was given, prepare sa to match af's needs */ #ifdef ENABLE_IPV6 if(af == AF_INET6) { si6->sin6_family = AF_INET6; si6->sin6_port = htons(port); sizeof_sa = sizeof(struct sockaddr_in6); } else #endif if(af == AF_INET) { si4->sin_family = AF_INET; si4->sin_port = htons(port); sizeof_sa = sizeof(struct sockaddr_in); } } for(;;) { if(bind(sockfd, sock, sizeof_sa) >= 0) { /* we succeeded to bind */ struct Curl_sockaddr_storage add; curl_socklen_t size = sizeof(add); memset(&add, 0, sizeof(struct Curl_sockaddr_storage)); if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) { data->state.os_errno = error = SOCKERRNO; failf(data, "getsockname() failed with errno %d: %s", error, Curl_strerror(conn, error)); return CURLE_INTERFACE_FAILED; } infof(data, "Local port: %hu\n", port); conn->bits.bound = TRUE; return CURLE_OK; } if(--portnum > 0) { infof(data, "Bind to local port %hu failed, trying next\n", port); port++; /* try next port */ /* We re-use/clobber the port variable here below */ if(sock->sa_family == AF_INET) si4->sin_port = ntohs(port); #ifdef ENABLE_IPV6 else si6->sin6_port = ntohs(port); #endif } else break; } data->state.os_errno = error = SOCKERRNO; failf(data, "bind failed with errno %d: %s", error, Curl_strerror(conn, error)); return CURLE_INTERFACE_FAILED; }
CURLcode Curl_loadhostpairs(struct SessionHandle *data) { struct curl_slist *hostp; char hostname[256]; char address[256]; int port; for(hostp = data->change.resolve; hostp; hostp = hostp->next ) { if(!hostp->data) continue; if(hostp->data[0] == '-') { /* TODO: mark an entry for removal */ } else if(3 == sscanf(hostp->data, "%255[^:]:%d:%255s", hostname, &port, address)) { struct Curl_dns_entry *dns; Curl_addrinfo *addr; char *entry_id; size_t entry_len; addr = Curl_str2addr(address, port); if(!addr) { infof(data, "Resolve %s found illegal!\n", hostp->data); continue; } /* Create an entry id, based upon the hostname and port */ entry_id = create_hostcache_id(hostname, port); /* If we can't create the entry id, fail */ if(!entry_id) { Curl_freeaddrinfo(addr); return CURLE_OUT_OF_MEMORY; } entry_len = strlen(entry_id); if(data->share) Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); /* See if its already in our dns cache */ dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1); /* free the allocated entry_id again */ free(entry_id); if(!dns) /* if not in the cache already, put this host in the cache */ dns = Curl_cache_addr(data, addr, hostname, port); else /* this is a duplicate, free it again */ Curl_freeaddrinfo(addr); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); if(!dns) { Curl_freeaddrinfo(addr); return CURLE_OUT_OF_MEMORY; } infof(data, "Added %s:%d:%s to DNS cache\n", hostname, port, address); } } data->change.resolve = NULL; /* dealt with now */ return CURLE_OK; }
/* * singleipconnect() * * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to * CURL_SOCKET_BAD. Other errors will however return proper errors. * * singleipconnect() connects to the given IP only, and it may return without * having connected if used from the multi interface. */ static CURLcode singleipconnect(struct connectdata *conn, const Curl_addrinfo *ai, long timeout_ms, curl_socket_t *sockp, bool *connected) { struct Curl_sockaddr_ex addr; int rc; int error; bool isconnected = FALSE; struct SessionHandle *data = conn->data; curl_socket_t sockfd; CURLcode res = CURLE_OK; #if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) struct sockaddr_in6 * const sa6 = (void *)&addr.sa_addr; #endif *sockp = CURL_SOCKET_BAD; /* * The Curl_sockaddr_ex structure is basically libcurl's external API * curl_sockaddr structure with enough space available to directly hold * any protocol-specific address structures. The variable declared here * will be used to pass / receive data to/from the fopensocket callback * if this has been set, before that, it is initialized from parameters. */ addr.family = ai->ai_family; addr.socktype = conn->socktype; addr.protocol = conn->socktype==SOCK_DGRAM?IPPROTO_UDP:ai->ai_protocol; addr.addrlen = ai->ai_addrlen; if(addr.addrlen > sizeof(struct Curl_sockaddr_storage)) addr.addrlen = sizeof(struct Curl_sockaddr_storage); memcpy(&addr.sa_addr, ai->ai_addr, addr.addrlen); *connected = FALSE; /* default is not connected */ if(data->set.fopensocket) /* * If the opensocket callback is set, all the destination address * information is passed to the callback. Depending on this information the * callback may opt to abort the connection, this is indicated returning * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When * the callback returns a valid socket the destination address information * might have been changed and this 'new' address will actually be used * here to connect. */ sockfd = data->set.fopensocket(data->set.opensocket_client, CURLSOCKTYPE_IPCXN, (struct curl_sockaddr *)&addr); else /* opensocket callback not set, so simply create the socket now */ sockfd = socket(addr.family, addr.socktype, addr.protocol); if(sockfd == CURL_SOCKET_BAD) /* no socket, no connection */ return CURLE_OK; #if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) if(conn->scope && (addr.family == AF_INET6)) sa6->sin6_scope_id = conn->scope; #endif /* store remote address and port used in this connection attempt */ if(!getaddressinfo((struct sockaddr*)&addr.sa_addr, conn->primary_ip, &conn->primary_port)) { /* malformed address or bug in inet_ntop, try next address */ error = ERRNO; failf(data, "sa_addr inet_ntop() failed with errno %d: %s", error, Curl_strerror(conn, error)); Curl_closesocket(conn, sockfd); return CURLE_OK; } memcpy(conn->ip_addr_str, conn->primary_ip, MAX_IPADR_LEN); infof(data, " Trying %s... ", conn->ip_addr_str); Curl_persistconninfo(conn); #ifdef ENABLE_IPV6 if(addr.family == AF_INET6) conn->bits.ipv6 = TRUE; #endif if(data->set.tcp_nodelay) tcpnodelay(conn, sockfd); nosigpipe(conn, sockfd); Curl_sndbufset(sockfd); if(data->set.fsockopt) { /* activate callback for setting socket options */ error = data->set.fsockopt(data->set.sockopt_client, sockfd, CURLSOCKTYPE_IPCXN); if(error == CURL_SOCKOPT_ALREADY_CONNECTED) isconnected = TRUE; else if(error) { Curl_closesocket(conn, sockfd); /* close the socket and bail out */ return CURLE_ABORTED_BY_CALLBACK; } } /* possibly bind the local end to an IP, interface or port */ res = bindlocal(conn, sockfd, addr.family); if(res) { Curl_closesocket(conn, sockfd); /* close socket and bail out */ return res; } /* set socket non-blocking */ curlx_nonblock(sockfd, TRUE); /* Connect TCP sockets, bind UDP */ if(!isconnected && (conn->socktype == SOCK_STREAM)) { rc = connect(sockfd, &addr.sa_addr, addr.addrlen); conn->connecttime = Curl_tvnow(); if(conn->num_addr > 1) Curl_expire(data, conn->timeoutms_per_addr); } else rc = 0; if(-1 == rc) { error = SOCKERRNO; switch (error) { case EINPROGRESS: case EWOULDBLOCK: #if defined(EAGAIN) #if (EAGAIN) != (EWOULDBLOCK) /* On some platforms EAGAIN and EWOULDBLOCK are the * same value, and on others they are different, hence * the odd #if */ case EAGAIN: #endif #endif rc = waitconnect(conn, sockfd, timeout_ms); if(WAITCONN_ABORTED == rc) { Curl_closesocket(conn, sockfd); return CURLE_ABORTED_BY_CALLBACK; } break; default: /* unknown error, fallthrough and try another address! */ failf(data, "Failed to connect to %s: %s", conn->ip_addr_str, Curl_strerror(conn,error)); data->state.os_errno = error; break; } } /* The 'WAITCONN_TIMEOUT == rc' comes from the waitconnect(), and not from connect(). We can be sure of this since connect() cannot return 1. */ if((WAITCONN_TIMEOUT == rc) && (data->state.used_interface == Curl_if_multi)) { /* Timeout when running the multi interface */ *sockp = sockfd; return CURLE_OK; } if(!isconnected) isconnected = verifyconnect(sockfd, &error); if(!rc && isconnected) { /* we are connected, awesome! */ *connected = TRUE; /* this is a true connect */ infof(data, "connected\n"); Curl_updateconninfo(conn, sockfd); *sockp = sockfd; return CURLE_OK; } else if(WAITCONN_TIMEOUT == rc) infof(data, "Timeout\n"); else { data->state.os_errno = error; infof(data, "%s\n", Curl_strerror(conn, error)); } /* connect failed or timed out */ Curl_closesocket(conn, sockfd); return CURLE_OK; }
/* return number of received (decrypted) bytes */ static ssize_t http2_send(struct connectdata *conn, int sockindex, const void *mem, size_t len, CURLcode *err) { /* * BIG TODO: Currently, we send request in this function, but this * function is also used to send request body. It would be nice to * add dedicated function for request. */ int rv; struct http_conn *httpc = &conn->proto.httpc; struct HTTP *stream = conn->data->req.protop; nghttp2_nv *nva; size_t nheader; size_t i; size_t authority_idx; char *hdbuf = (char*)mem; char *end; nghttp2_data_provider data_prd; int32_t stream_id; nghttp2_session *h2 = httpc->h2; (void)sockindex; DEBUGF(infof(conn->data, "http2_send len=%zu\n", len)); if(stream->stream_id != -1) { /* If stream_id != -1, we have dispatched request HEADERS, and now are going to send or sending request body in DATA frame */ stream->upload_mem = mem; stream->upload_len = len; nghttp2_session_resume_data(h2, stream->stream_id); rv = nghttp2_session_send(h2); if(nghttp2_is_fatal(rv)) { *err = CURLE_SEND_ERROR; return -1; } len -= stream->upload_len; /* Nullify here because we call nghttp2_session_send() and they might refer to the old buffer. */ stream->upload_mem = NULL; stream->upload_len = 0; if(stream->upload_left) { /* we are sure that we have more data to send here. Calling the following API will make nghttp2_session_want_write() return nonzero if remote window allows it, which then libcurl checks socket is writable or not. See http2_perform_getsock(). */ nghttp2_session_resume_data(h2, stream->stream_id); } DEBUGF(infof(conn->data, "http2_send returns %zu for stream %u\n", len, stream->stream_id)); return len; } /* Calculate number of headers contained in [mem, mem + len) */ /* Here, we assume the curl http code generate *correct* HTTP header field block */ nheader = 0; for(i = 0; i < len; ++i) { if(hdbuf[i] == 0x0a) { ++nheader; } } /* We counted additional 2 \n in the first and last line. We need 3 new headers: :method, :path and :scheme. Therefore we need one more space. */ nheader += 1; nva = malloc(sizeof(nghttp2_nv) * nheader); if(nva == NULL) { *err = CURLE_OUT_OF_MEMORY; return -1; } /* Extract :method, :path from request line */ end = strchr(hdbuf, ' '); if(!end) goto fail; nva[0].name = (unsigned char *)":method"; nva[0].namelen = (uint16_t)strlen((char *)nva[0].name); nva[0].value = (unsigned char *)hdbuf; nva[0].valuelen = (uint16_t)(end - hdbuf); nva[0].flags = NGHTTP2_NV_FLAG_NONE; hdbuf = end + 1; end = strchr(hdbuf, ' '); if(!end) goto fail; nva[1].name = (unsigned char *)":path"; nva[1].namelen = (uint16_t)strlen((char *)nva[1].name); nva[1].value = (unsigned char *)hdbuf; nva[1].valuelen = (uint16_t)(end - hdbuf); nva[1].flags = NGHTTP2_NV_FLAG_NONE; nva[2].name = (unsigned char *)":scheme"; nva[2].namelen = (uint16_t)strlen((char *)nva[2].name); if(conn->handler->flags & PROTOPT_SSL) nva[2].value = (unsigned char *)"https"; else nva[2].value = (unsigned char *)"http"; nva[2].valuelen = (uint16_t)strlen((char *)nva[2].value); nva[2].flags = NGHTTP2_NV_FLAG_NONE; hdbuf = strchr(hdbuf, 0x0a); if(!hdbuf) goto fail; ++hdbuf; authority_idx = 0; for(i = 3; i < nheader; ++i) { size_t hlen; end = strchr(hdbuf, ':'); if(!end) goto fail; hlen = end - hdbuf; if(hlen == 10 && Curl_raw_nequal("connection", hdbuf, 10)) ; /* skip Connection: headers! */ else if(hlen == 4 && Curl_raw_nequal("host", hdbuf, 4)) { authority_idx = i; nva[i].name = (unsigned char *)":authority"; nva[i].namelen = (uint16_t)strlen((char *)nva[i].name); } else { nva[i].name = (unsigned char *)hdbuf; nva[i].namelen = (uint16_t)(end - hdbuf); } hdbuf = end + 1; for(; *hdbuf == ' '; ++hdbuf); end = strchr(hdbuf, 0x0d); if(!end) goto fail; nva[i].value = (unsigned char *)hdbuf; nva[i].valuelen = (uint16_t)(end - hdbuf); nva[i].flags = NGHTTP2_NV_FLAG_NONE; hdbuf = end + 2; /* Inspect Content-Length header field and retrieve the request entity length so that we can set END_STREAM to the last DATA frame. */ if(nva[i].namelen == 14 && Curl_raw_nequal("content-length", (char*)nva[i].name, 14)) { size_t j; stream->upload_left = 0; for(j = 0; j < nva[i].valuelen; ++j) { stream->upload_left *= 10; stream->upload_left += nva[i].value[j] - '0'; } DEBUGF(infof(conn->data, "request content-length=%" CURL_FORMAT_CURL_OFF_T "\n", stream->upload_left)); } } /* :authority must come before non-pseudo header fields */ if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) { nghttp2_nv authority = nva[authority_idx]; for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) { nva[i] = nva[i - 1]; } nva[i] = authority; } switch(conn->data->set.httpreq) { case HTTPREQ_POST: case HTTPREQ_POST_FORM: case HTTPREQ_PUT: data_prd.read_callback = data_source_read_callback; data_prd.source.ptr = NULL; stream_id = nghttp2_submit_request(h2, NULL, nva, nheader, &data_prd, conn->data); break; default: stream_id = nghttp2_submit_request(h2, NULL, nva, nheader, NULL, conn->data); } Curl_safefree(nva); if(stream_id < 0) { DEBUGF(infof(conn->data, "http2_send() send error\n")); *err = CURLE_SEND_ERROR; return -1; } infof(conn->data, "Using Stream ID: %x (easy handle %p)\n", stream_id, conn->data); stream->stream_id = stream_id; rv = nghttp2_session_send(h2); if(rv != 0) { *err = CURLE_SEND_ERROR; return -1; } if(stream->stream_id != -1) { /* If whole HEADERS frame was sent off to the underlying socket, the nghttp2 library calls data_source_read_callback. But only it found that no data available, so it deferred the DATA transmission. Which means that nghttp2_session_want_write() returns 0 on http2_perform_getsock(), which results that no writable socket check is performed. To workaround this, we issue nghttp2_session_resume_data() here to bring back DATA transmission from deferred state. */ nghttp2_session_resume_data(h2, stream->stream_id); } return len; fail: free(nva); *err = CURLE_SEND_ERROR; return -1; }
/* * This function loads all the client/CA certificates and CRLs. Setup the TLS * layer and do all necessary magic. */ static CURLcode cyassl_connect_step1(struct connectdata *conn, int sockindex) { char error_buffer[CYASSL_MAX_ERROR_SZ]; char *ciphers; struct Curl_easy *data = conn->data; struct ssl_connect_data* connssl = &conn->ssl[sockindex]; SSL_METHOD* req_method = NULL; curl_socket_t sockfd = conn->sock[sockindex]; #ifdef HAVE_SNI bool sni = FALSE; #define use_sni(x) sni = (x) #else #define use_sni(x) Curl_nop_stmt #endif if(connssl->state == ssl_connection_complete) return CURLE_OK; if(SSL_CONN_CONFIG(version_max) != CURL_SSLVERSION_MAX_NONE) { failf(data, "CyaSSL does not support to set maximum SSL/TLS version"); return CURLE_SSL_CONNECT_ERROR; } /* check to see if we've been told to use an explicit SSL/TLS version */ switch(SSL_CONN_CONFIG(version)) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: #if LIBCYASSL_VERSION_HEX >= 0x03003000 /* >= 3.3.0 */ /* minimum protocol version is set later after the CTX object is created */ req_method = SSLv23_client_method(); #else infof(data, "CyaSSL <3.3.0 cannot be configured to use TLS 1.0-1.2, " "TLS 1.0 is used exclusively\n"); req_method = TLSv1_client_method(); #endif use_sni(TRUE); break; case CURL_SSLVERSION_TLSv1_0: #ifdef WOLFSSL_ALLOW_TLSV10 req_method = TLSv1_client_method(); use_sni(TRUE); #else failf(data, "CyaSSL does not support TLS 1.0"); return CURLE_NOT_BUILT_IN; #endif break; case CURL_SSLVERSION_TLSv1_1: req_method = TLSv1_1_client_method(); use_sni(TRUE); break; case CURL_SSLVERSION_TLSv1_2: req_method = TLSv1_2_client_method(); use_sni(TRUE); break; case CURL_SSLVERSION_TLSv1_3: #ifdef WOLFSSL_TLS13 req_method = wolfTLSv1_3_client_method(); use_sni(TRUE); break; #else failf(data, "CyaSSL: TLS 1.3 is not yet supported"); return CURLE_SSL_CONNECT_ERROR; #endif case CURL_SSLVERSION_SSLv3: #ifdef WOLFSSL_ALLOW_SSLV3 req_method = SSLv3_client_method(); use_sni(FALSE); #else failf(data, "CyaSSL does not support SSLv3"); return CURLE_NOT_BUILT_IN; #endif break; case CURL_SSLVERSION_SSLv2: failf(data, "CyaSSL does not support SSLv2"); return CURLE_SSL_CONNECT_ERROR; default: failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); return CURLE_SSL_CONNECT_ERROR; } if(!req_method) { failf(data, "SSL: couldn't create a method!"); return CURLE_OUT_OF_MEMORY; } if(BACKEND->ctx) SSL_CTX_free(BACKEND->ctx); BACKEND->ctx = SSL_CTX_new(req_method); if(!BACKEND->ctx) { failf(data, "SSL: couldn't create a context!"); return CURLE_OUT_OF_MEMORY; } switch(SSL_CONN_CONFIG(version)) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: #if LIBCYASSL_VERSION_HEX > 0x03004006 /* > 3.4.6 */ /* Versions 3.3.0 to 3.4.6 we know the minimum protocol version is whatever minimum version of TLS was built in and at least TLS 1.0. For later library versions that could change (eg TLS 1.0 built in but defaults to TLS 1.1) so we have this short circuit evaluation to find the minimum supported TLS version. We use wolfSSL_CTX_SetMinVersion and not CyaSSL_SetMinVersion because only the former will work before the user's CTX callback is called. */ if((wolfSSL_CTX_SetMinVersion(BACKEND->ctx, WOLFSSL_TLSV1) != 1) && (wolfSSL_CTX_SetMinVersion(BACKEND->ctx, WOLFSSL_TLSV1_1) != 1) && (wolfSSL_CTX_SetMinVersion(BACKEND->ctx, WOLFSSL_TLSV1_2) != 1) #ifdef WOLFSSL_TLS13 && (wolfSSL_CTX_SetMinVersion(BACKEND->ctx, WOLFSSL_TLSV1_3) != 1) #endif ) { failf(data, "SSL: couldn't set the minimum protocol version"); return CURLE_SSL_CONNECT_ERROR; } #endif break; } ciphers = SSL_CONN_CONFIG(cipher_list); if(ciphers) { if(!SSL_CTX_set_cipher_list(BACKEND->ctx, ciphers)) { failf(data, "failed setting cipher list: %s", ciphers); return CURLE_SSL_CIPHER; } infof(data, "Cipher selection: %s\n", ciphers); } #ifndef NO_FILESYSTEM /* load trusted cacert */ if(SSL_CONN_CONFIG(CAfile)) { if(1 != SSL_CTX_load_verify_locations(BACKEND->ctx, SSL_CONN_CONFIG(CAfile), SSL_CONN_CONFIG(CApath))) { if(SSL_CONN_CONFIG(verifypeer)) { /* Fail if we insist on successfully verifying the server. */ failf(data, "error setting certificate verify locations:\n" " CAfile: %s\n CApath: %s", SSL_CONN_CONFIG(CAfile)? SSL_CONN_CONFIG(CAfile): "none", SSL_CONN_CONFIG(CApath)? SSL_CONN_CONFIG(CApath) : "none"); return CURLE_SSL_CACERT_BADFILE; } else { /* Just continue with a warning if no strict certificate verification is required. */ infof(data, "error setting certificate verify locations," " continuing anyway:\n"); } } else { /* Everything is fine. */ infof(data, "successfully set certificate verify locations:\n"); } infof(data, " CAfile: %s\n" " CApath: %s\n", SSL_CONN_CONFIG(CAfile) ? SSL_CONN_CONFIG(CAfile): "none", SSL_CONN_CONFIG(CApath) ? SSL_CONN_CONFIG(CApath): "none"); } /* Load the client certificate, and private key */ if(SSL_SET_OPTION(cert) && SSL_SET_OPTION(key)) { int file_type = do_file_type(SSL_SET_OPTION(cert_type)); if(SSL_CTX_use_certificate_file(BACKEND->ctx, SSL_SET_OPTION(cert), file_type) != 1) { failf(data, "unable to use client certificate (no key or wrong pass" " phrase?)"); return CURLE_SSL_CONNECT_ERROR; } file_type = do_file_type(SSL_SET_OPTION(key_type)); if(SSL_CTX_use_PrivateKey_file(BACKEND->ctx, SSL_SET_OPTION(key), file_type) != 1) { failf(data, "unable to set private key"); return CURLE_SSL_CONNECT_ERROR; } } #endif /* !NO_FILESYSTEM */ /* SSL always tries to verify the peer, this only says whether it should * fail to connect if the verification fails, or if it should continue * anyway. In the latter case the result of the verification is checked with * SSL_get_verify_result() below. */ SSL_CTX_set_verify(BACKEND->ctx, SSL_CONN_CONFIG(verifypeer)?SSL_VERIFY_PEER: SSL_VERIFY_NONE, NULL); #ifdef HAVE_SNI if(sni) { struct in_addr addr4; #ifdef ENABLE_IPV6 struct in6_addr addr6; #endif const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; size_t hostname_len = strlen(hostname); if((hostname_len < USHRT_MAX) && (0 == Curl_inet_pton(AF_INET, hostname, &addr4)) && #ifdef ENABLE_IPV6 (0 == Curl_inet_pton(AF_INET6, hostname, &addr6)) && #endif (CyaSSL_CTX_UseSNI(BACKEND->ctx, CYASSL_SNI_HOST_NAME, hostname, (unsigned short)hostname_len) != 1)) { infof(data, "WARNING: failed to configure server name indication (SNI) " "TLS extension\n"); } } #endif /* give application a chance to interfere with SSL set up. */ if(data->set.ssl.fsslctx) { CURLcode result = CURLE_OK; result = (*data->set.ssl.fsslctx)(data, BACKEND->ctx, data->set.ssl.fsslctxp); if(result) { failf(data, "error signaled by ssl ctx callback"); return result; } } #ifdef NO_FILESYSTEM else if(SSL_CONN_CONFIG(verifypeer)) { failf(data, "SSL: Certificates couldn't be loaded because CyaSSL was built" " with \"no filesystem\". Either disable peer verification" " (insecure) or if you are building an application with libcurl you" " can load certificates via CURLOPT_SSL_CTX_FUNCTION."); return CURLE_SSL_CONNECT_ERROR; } #endif /* Let's make an SSL structure */ if(BACKEND->handle) SSL_free(BACKEND->handle); BACKEND->handle = SSL_new(BACKEND->ctx); if(!BACKEND->handle) { failf(data, "SSL: couldn't create a context (handle)!"); return CURLE_OUT_OF_MEMORY; } #ifdef HAVE_ALPN if(conn->bits.tls_enable_alpn) { char protocols[128]; *protocols = '\0'; /* wolfSSL's ALPN protocol name list format is a comma separated string of protocols in descending order of preference, eg: "h2,http/1.1" */ #ifdef USE_NGHTTP2 if(data->set.httpversion >= CURL_HTTP_VERSION_2) { strcpy(protocols + strlen(protocols), NGHTTP2_PROTO_VERSION_ID ","); infof(data, "ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID); } #endif strcpy(protocols + strlen(protocols), ALPN_HTTP_1_1); infof(data, "ALPN, offering %s\n", ALPN_HTTP_1_1); if(wolfSSL_UseALPN(BACKEND->handle, protocols, (unsigned)strlen(protocols), WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != SSL_SUCCESS) { failf(data, "SSL: failed setting ALPN protocols"); return CURLE_SSL_CONNECT_ERROR; } } #endif /* HAVE_ALPN */ /* Check if there's a cached ID we can/should use here! */ if(SSL_SET_OPTION(primary.sessionid)) { void *ssl_sessionid = NULL; Curl_ssl_sessionid_lock(conn); if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL, sockindex)) { /* we got a session id, use it! */ if(!SSL_set_session(BACKEND->handle, ssl_sessionid)) { Curl_ssl_sessionid_unlock(conn); failf(data, "SSL: SSL_set_session failed: %s", ERR_error_string(SSL_get_error(BACKEND->handle, 0), error_buffer)); return CURLE_SSL_CONNECT_ERROR; } /* Informational message */ infof(data, "SSL re-using session ID\n"); } Curl_ssl_sessionid_unlock(conn); } /* pass the raw socket into the SSL layer */ if(!SSL_set_fd(BACKEND->handle, (int)sockfd)) { failf(data, "SSL: SSL_set_fd failed"); return CURLE_SSL_CONNECT_ERROR; } connssl->connecting_state = ssl_connect_2; return CURLE_OK; }
CURLcode Curl_http2_switched(struct connectdata *conn, const char *mem, size_t nread) { CURLcode result; struct http_conn *httpc = &conn->proto.httpc; int rv; ssize_t nproc; struct SessionHandle *data = conn->data; struct HTTP *stream = conn->data->req.protop; result = Curl_http2_setup(conn); if(result) return result; httpc->recv_underlying = (recving)conn->recv[FIRSTSOCKET]; httpc->send_underlying = (sending)conn->send[FIRSTSOCKET]; conn->recv[FIRSTSOCKET] = http2_recv; conn->send[FIRSTSOCKET] = http2_send; if(conn->data->req.upgr101 == UPGR101_RECEIVED) { /* stream 1 is opened implicitly on upgrade */ stream->stream_id = 1; /* queue SETTINGS frame (again) */ rv = nghttp2_session_upgrade(httpc->h2, httpc->binsettings, httpc->binlen, NULL); if(rv != 0) { failf(data, "nghttp2_session_upgrade() failed: %s(%d)", nghttp2_strerror(rv), rv); return CURLE_HTTP2; } nghttp2_session_set_stream_user_data(httpc->h2, stream->stream_id, conn->data); } else { /* stream ID is unknown at this point */ stream->stream_id = -1; rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE, NULL, 0); if(rv != 0) { failf(data, "nghttp2_submit_settings() failed: %s(%d)", nghttp2_strerror(rv), rv); return CURLE_HTTP2; } } /* we are going to copy mem to httpc->inbuf. This is required since mem is part of buffer pointed by stream->mem, and callbacks called by nghttp2_session_mem_recv() will write stream specific data into stream->mem, overwriting data already there. */ if(H2_BUFSIZE < nread) { failf(data, "connection buffer size is too small to store data following " "HTTP Upgrade response header: buflen=%zu, datalen=%zu", H2_BUFSIZE, nread); return CURLE_HTTP2; } infof(conn->data, "Copying HTTP/2 data in stream buffer to connection buffer" " after upgrade: len=%zu\n", nread); memcpy(httpc->inbuf, mem, nread); httpc->inbuflen = nread; nproc = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)httpc->inbuf, httpc->inbuflen); if(nghttp2_is_fatal((int)nproc)) { failf(data, "nghttp2_session_mem_recv() failed: %s(%d)", nghttp2_strerror((int)nproc), (int)nproc); return CURLE_HTTP2; } DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", nproc)); if((ssize_t)nread == nproc) { httpc->inbuflen = 0; httpc->nread_inbuf = 0; } else { httpc->nread_inbuf += nproc; } /* Try to send some frames since we may read SETTINGS already. */ rv = nghttp2_session_send(httpc->h2); if(rv != 0) { failf(data, "nghttp2_session_send() failed: %s(%d)", nghttp2_strerror(rv), rv); return CURLE_HTTP2; } return CURLE_OK; }
static CURLcode mbedtls_connect_step1(struct connectdata *conn, int sockindex) { struct SessionHandle *data = conn->data; struct ssl_connect_data* connssl = &conn->ssl[sockindex]; bool sni = TRUE; /* default is SNI enabled */ int ret = -1; #ifdef ENABLE_IPV6 struct in6_addr addr; #else struct in_addr addr; #endif void *old_session = NULL; size_t old_session_size = 0; char errorbuf[128]; errorbuf[0]=0; /* mbedTLS only supports SSLv3 and TLSv1 */ if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) { failf(data, "mbedTLS does not support SSLv2"); return CURLE_SSL_CONNECT_ERROR; } else if(data->set.ssl.version == CURL_SSLVERSION_SSLv3) sni = FALSE; /* SSLv3 has no SNI */ #ifdef THREADING_SUPPORT entropy_init_mutex(&entropy); mbedtls_ctr_drbg_init(&connssl->ctr_drbg); ret = mbedtls_ctr_drbg_seed(&connssl->ctr_drbg, entropy_func_mutex, &entropy, connssl->ssn.id, connssl->ssn.id_len); if(ret) { #ifdef MBEDTLS_ERROR_C mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* MBEDTLS_ERROR_C */ failf(data, "Failed - mbedTLS: ctr_drbg_init returned (-0x%04X) %s\n", -ret, errorbuf); } #else mbedtls_entropy_init(&connssl->entropy); mbedtls_ctr_drbg_init(&connssl->ctr_drbg); ret = mbedtls_ctr_drbg_seed(&connssl->ctr_drbg, mbedtls_entropy_func, &connssl->entropy, connssl->ssn.id, connssl->ssn.id_len); if(ret) { #ifdef MBEDTLS_ERROR_C mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* MBEDTLS_ERROR_C */ failf(data, "Failed - mbedTLS: ctr_drbg_init returned (-0x%04X) %s\n", -ret, errorbuf); } #endif /* THREADING_SUPPORT */ /* Load the trusted CA */ memset(&connssl->cacert, 0, sizeof(mbedtls_x509_crt)); if(data->set.str[STRING_SSL_CAFILE]) { ret = mbedtls_x509_crt_parse_file(&connssl->cacert, data->set.str[STRING_SSL_CAFILE]); if(ret<0) { #ifdef MBEDTLS_ERROR_C mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* MBEDTLS_ERROR_C */ failf(data, "Error reading ca cert file %s - mbedTLS: (-0x%04X) %s", data->set.str[STRING_SSL_CAFILE], -ret, errorbuf); if(data->set.ssl.verifypeer) return CURLE_SSL_CACERT_BADFILE; } } if(data->set.str[STRING_SSL_CAPATH]) { ret = mbedtls_x509_crt_parse_path(&connssl->cacert, data->set.str[STRING_SSL_CAPATH]); if(ret<0) { #ifdef MBEDTLS_ERROR_C mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* MBEDTLS_ERROR_C */ failf(data, "Error reading ca cert path %s - mbedTLS: (-0x%04X) %s", data->set.str[STRING_SSL_CAPATH], -ret, errorbuf); if(data->set.ssl.verifypeer) return CURLE_SSL_CACERT_BADFILE; } } /* Load the client certificate */ memset(&connssl->clicert, 0, sizeof(mbedtls_x509_crt)); if(data->set.str[STRING_CERT]) { ret = mbedtls_x509_crt_parse_file(&connssl->clicert, data->set.str[STRING_CERT]); if(ret) { #ifdef MBEDTLS_ERROR_C mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* MBEDTLS_ERROR_C */ failf(data, "Error reading client cert file %s - mbedTLS: (-0x%04X) %s", data->set.str[STRING_CERT], -ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } } /* Load the client private key */ if(data->set.str[STRING_KEY]) { mbedtls_pk_init(&connssl->pk); ret = mbedtls_pk_parse_keyfile(&connssl->pk, data->set.str[STRING_KEY], data->set.str[STRING_KEY_PASSWD]); if(ret == 0 && !mbedtls_pk_can_do(&connssl->pk, MBEDTLS_PK_RSA)) ret = MBEDTLS_ERR_PK_TYPE_MISMATCH; if(ret) { #ifdef MBEDTLS_ERROR_C mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* MBEDTLS_ERROR_C */ failf(data, "Error reading private key %s - mbedTLS: (-0x%04X) %s", data->set.str[STRING_KEY], -ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } } /* Load the CRL */ memset(&connssl->crl, 0, sizeof(mbedtls_x509_crl)); if(data->set.str[STRING_SSL_CRLFILE]) { ret = mbedtls_x509_crl_parse_file(&connssl->crl, data->set.str[STRING_SSL_CRLFILE]); if(ret) { #ifdef MBEDTLS_ERROR_C mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* MBEDTLS_ERROR_C */ failf(data, "Error reading CRL file %s - mbedTLS: (-0x%04X) %s", data->set.str[STRING_SSL_CRLFILE], -ret, errorbuf); return CURLE_SSL_CRL_BADFILE; } } infof(data, "mbedTLS: Connecting to %s:%d\n", conn->host.name, conn->remote_port); mbedtls_ssl_config_init(&connssl->config); mbedtls_ssl_init(&connssl->ssl); if(mbedtls_ssl_setup(&connssl->ssl, &connssl->config)) { failf(data, "mbedTLS: ssl_init failed"); return CURLE_SSL_CONNECT_ERROR; } ret = mbedtls_ssl_config_defaults(&connssl->config, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); if(ret) { failf(data, "mbedTLS: ssl_config failed"); return CURLE_SSL_CONNECT_ERROR; } /* new profile with RSA min key len = 1024 ... */ mbedtls_ssl_conf_cert_profile( &connssl->config, &mbedtls_x509_crt_profile_fr); switch(data->set.ssl.version) { case CURL_SSLVERSION_SSLv3: mbedtls_ssl_conf_min_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_0); infof(data, "mbedTLS: Forced min. SSL Version to be SSLv3\n"); break; case CURL_SSLVERSION_TLSv1_0: mbedtls_ssl_conf_min_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_1); infof(data, "mbedTLS: Forced min. SSL Version to be TLS 1.0\n"); break; case CURL_SSLVERSION_TLSv1_1: mbedtls_ssl_conf_min_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_2); infof(data, "mbedTLS: Forced min. SSL Version to be TLS 1.1\n"); break; case CURL_SSLVERSION_TLSv1_2: mbedtls_ssl_conf_min_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3); infof(data, "mbedTLS: Forced min. SSL Version to be TLS 1.2\n"); break; } mbedtls_ssl_conf_authmode(&connssl->config, MBEDTLS_SSL_VERIFY_OPTIONAL); mbedtls_ssl_conf_rng(&connssl->config, mbedtls_ctr_drbg_random, &connssl->ctr_drbg); mbedtls_ssl_set_bio(&connssl->ssl, &conn->sock[sockindex], mbedtls_net_send, mbedtls_net_recv, NULL /* rev_timeout() */); mbedtls_ssl_conf_ciphersuites(&connssl->config, mbedtls_ssl_list_ciphersuites()); if(!Curl_ssl_getsessionid(conn, &old_session, &old_session_size)) { memcpy(&connssl->ssn, old_session, old_session_size); infof(data, "mbedTLS re-using session\n"); } mbedtls_ssl_set_session(&connssl->ssl, &connssl->ssn); mbedtls_ssl_conf_ca_chain(&connssl->config, &connssl->cacert, &connssl->crl); if(data->set.str[STRING_KEY]) { mbedtls_ssl_conf_own_cert(&connssl->config, &connssl->clicert, &connssl->pk); } if(!Curl_inet_pton(AF_INET, conn->host.name, &addr) && #ifdef ENABLE_IPV6 !Curl_inet_pton(AF_INET6, conn->host.name, &addr) && #endif sni && mbedtls_ssl_set_hostname(&connssl->ssl, conn->host.name)) { infof(data, "WARNING: failed to configure " "server name indication (SNI) TLS extension\n"); } #ifdef HAS_ALPN if(data->set.ssl_enable_alpn) { const char *protocols[3]; const char **p = protocols; #ifdef USE_NGHTTP2 if(data->set.httpversion >= CURL_HTTP_VERSION_2) *p++ = NGHTTP2_PROTO_VERSION_ID; #endif *p++ = ALPN_HTTP_1_1; *p = NULL; if(mbedtls_ssl_conf_alpn_protocols(&connssl->config, protocols)) { failf(data, "Failed setting ALPN protocols"); return CURLE_SSL_CONNECT_ERROR; } for(p = protocols; *p; ++p) infof(data, "ALPN, offering %s\n", *p); } #endif #ifdef MBEDTLS_DEBUG mbedtls_ssl_conf_dbg(&connssl->ssl, mbedtls_debug, data); #endif connssl->connecting_state = ssl_connect_2; return CURLE_OK; }
CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, struct connectdata *conn) { struct SessionHandle *data = conn->data; curl_socket_t sock = conn->sock[sockindex]; CURLcode code; ssize_t actualread; ssize_t written; int result; long timeout; OM_uint32 gss_major_status, gss_minor_status, gss_status; OM_uint32 gss_ret_flags; int gss_conf_state, gss_enc; gss_buffer_desc service = GSS_C_EMPTY_BUFFER; gss_buffer_desc gss_send_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc gss_recv_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc gss_w_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc* gss_token = GSS_C_NO_BUFFER; gss_name_t server = GSS_C_NO_NAME; gss_name_t gss_client_name = GSS_C_NO_NAME; u_short us_length; char *user=NULL; unsigned char socksreq[4]; /* room for gssapi exchange header only */ char *serviceptr = data->set.str[STRING_SOCKS5_GSSAPI_SERVICE]; /* get timeout */ timeout = Curl_timeleft(conn, NULL, TRUE); /* GSSAPI request looks like * +----+------+-----+----------------+ * |VER | MTYP | LEN | TOKEN | * +----+------+----------------------+ * | 1 | 1 | 2 | up to 2^16 - 1 | * +----+------+-----+----------------+ */ /* prepare service name */ if (strchr(serviceptr,'/')) { service.value = malloc(strlen(serviceptr)); if(!service.value) return CURLE_OUT_OF_MEMORY; service.length = strlen(serviceptr); memcpy(service.value, serviceptr, service.length); gss_major_status = gss_import_name(&gss_minor_status, &service, (gss_OID) GSS_C_NULL_OID, &server); } else { service.value = malloc(strlen(serviceptr) +strlen(conn->proxy.name)+2); if(!service.value) return CURLE_OUT_OF_MEMORY; service.length = strlen(serviceptr) +strlen(conn->proxy.name)+1; snprintf(service.value, service.length+1, "%s@%s", serviceptr, conn->proxy.name); gss_major_status = gss_import_name(&gss_minor_status, &service, gss_nt_service_name, &server); } gss_release_buffer(&gss_status, &service); /* clear allocated memory */ if(check_gss_err(data,gss_major_status, gss_minor_status,"gss_import_name()")) { failf(data, "Failed to create service name."); gss_release_name(&gss_status, &server); return CURLE_COULDNT_CONNECT; } /* As long as we need to keep sending some context info, and there's no */ /* errors, keep sending it... */ for(;;) { gss_major_status = gss_init_sec_context(&gss_minor_status, GSS_C_NO_CREDENTIAL, &gss_context, server, GSS_C_NULL_OID, GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG, 0, NULL, gss_token, NULL, &gss_send_token, &gss_ret_flags, NULL); if(gss_token != GSS_C_NO_BUFFER) gss_release_buffer(&gss_status, &gss_recv_token); if(check_gss_err(data,gss_major_status, gss_minor_status,"gss_init_sec_context")) { gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); gss_release_buffer(&gss_status, &gss_send_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); failf(data, "Failed to initial GSSAPI token."); return CURLE_COULDNT_CONNECT; } if(gss_send_token.length != 0) { socksreq[0] = 1; /* gssapi subnegotiation version */ socksreq[1] = 1; /* authentication message type */ us_length = htons((short)gss_send_token.length); memcpy(socksreq+2,&us_length,sizeof(short)); code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written); if((code != CURLE_OK) || (4 != written)) { failf(data, "Failed to send GSSAPI authentication request."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); gss_release_buffer(&gss_status, &gss_send_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } code = Curl_write_plain(conn, sock, (char *)gss_send_token.value, gss_send_token.length, &written); if((code != CURLE_OK) || ((ssize_t)gss_send_token.length != written)) { failf(data, "Failed to send GSSAPI authentication token."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); gss_release_buffer(&gss_status, &gss_send_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } } gss_release_buffer(&gss_status, &gss_send_token); gss_release_buffer(&gss_status, &gss_recv_token); if(gss_major_status != GSS_S_CONTINUE_NEEDED) break; /* analyse response */ /* GSSAPI response looks like * +----+------+-----+----------------+ * |VER | MTYP | LEN | TOKEN | * +----+------+----------------------+ * | 1 | 1 | 2 | up to 2^16 - 1 | * +----+------+-----+----------------+ */ result=Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread, timeout); if(result != CURLE_OK || actualread != 4) { failf(data, "Failed to receive GSSAPI authentication response."); gss_release_name(&gss_status, &server); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } /* ignore the first (VER) byte */ if(socksreq[1] == 255) { /* status / message type */ failf(data, "User was rejected by the SOCKS5 server (%d %d).", socksreq[0], socksreq[1]); gss_release_name(&gss_status, &server); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(socksreq[1] != 1) { /* status / messgae type */ failf(data, "Invalid GSSAPI authentication response type (%d %d).", socksreq[0], socksreq[1]); gss_release_name(&gss_status, &server); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } memcpy(&us_length, socksreq+2, sizeof(short)); us_length = ntohs(us_length); gss_recv_token.length=us_length; gss_recv_token.value=malloc(us_length); if(!gss_recv_token.value) { failf(data, "Could not allocate memory for GSSAPI authentication " "response token."); gss_release_name(&gss_status, &server); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_OUT_OF_MEMORY; } result=Curl_blockread_all(conn, sock, (char *)gss_recv_token.value, gss_recv_token.length, &actualread, timeout); if(result != CURLE_OK || actualread != us_length) { failf(data, "Failed to receive GSSAPI authentication token."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } gss_token = &gss_recv_token; } gss_release_name(&gss_status, &server); /* Everything is good so far, user was authenticated! */ gss_major_status = gss_inquire_context (&gss_minor_status, gss_context, &gss_client_name, NULL, NULL, NULL, NULL, NULL, NULL); if(check_gss_err(data,gss_major_status, gss_minor_status,"gss_inquire_context")) { gss_delete_sec_context(&gss_status, &gss_context, NULL); gss_release_name(&gss_status, &gss_client_name); failf(data, "Failed to determine user name."); return CURLE_COULDNT_CONNECT; } gss_major_status = gss_display_name(&gss_minor_status, gss_client_name, &gss_send_token, NULL); if(check_gss_err(data,gss_major_status, gss_minor_status,"gss_display_name")) { gss_delete_sec_context(&gss_status, &gss_context, NULL); gss_release_name(&gss_status, &gss_client_name); gss_release_buffer(&gss_status, &gss_send_token); failf(data, "Failed to determine user name."); return CURLE_COULDNT_CONNECT; } user=malloc(gss_send_token.length+1); if(!user) { gss_delete_sec_context(&gss_status, &gss_context, NULL); gss_release_name(&gss_status, &gss_client_name); gss_release_buffer(&gss_status, &gss_send_token); return CURLE_OUT_OF_MEMORY; } memcpy(user, gss_send_token.value, gss_send_token.length); user[gss_send_token.length] = '\0'; gss_release_name(&gss_status, &gss_client_name); gss_release_buffer(&gss_status, &gss_send_token); infof(data, "SOCKS5 server authencticated user %s with gssapi.\n",user); free(user); user=NULL; /* Do encryption */ socksreq[0] = 1; /* gssapi subnegotiation version */ socksreq[1] = 2; /* encryption message type */ gss_enc = 0; /* no data protection */ /* do confidentiality protection if supported */ if(gss_ret_flags & GSS_C_CONF_FLAG) gss_enc = 2; /* else do integrity protection */ else if(gss_ret_flags & GSS_C_INTEG_FLAG) gss_enc = 1; infof(data, "SOCKS5 server supports gssapi %s data protection.\n", (gss_enc==0)?"no":((gss_enc==1)?"integrity":"confidentiality")); /* force for the moment to no data protection */ gss_enc = 0; /* * Sending the encryption type in clear seems wrong. It should be * protected with gss_seal()/gss_wrap(). See RFC1961 extract below * The NEC reference implementations on which this is based is * therefore at fault * * +------+------+------+.......................+ * + ver | mtyp | len | token | * +------+------+------+.......................+ * + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets | * +------+------+------+.......................+ * * Where: * * - "ver" is the protocol version number, here 1 to represent the * first version of the SOCKS/GSS-API protocol * * - "mtyp" is the message type, here 2 to represent a protection * -level negotiation message * * - "len" is the length of the "token" field in octets * * - "token" is the GSS-API encapsulated protection level * * The token is produced by encapsulating an octet containing the * required protection level using gss_seal()/gss_wrap() with conf_req * set to FALSE. The token is verified using gss_unseal()/ * gss_unwrap(). * */ if(data->set.socks5_gssapi_nec) { us_length = htons((short)1); memcpy(socksreq+2,&us_length,sizeof(short)); } else { gss_send_token.length = 1; gss_send_token.value = malloc(1); if(!gss_send_token.value) { gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_OUT_OF_MEMORY; } memcpy(gss_send_token.value, &gss_enc, 1); gss_major_status = gss_wrap(&gss_minor_status, gss_context, 0, GSS_C_QOP_DEFAULT, &gss_send_token, &gss_conf_state, &gss_w_token); if(check_gss_err(data,gss_major_status,gss_minor_status,"gss_wrap")) { gss_release_buffer(&gss_status, &gss_send_token); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); failf(data, "Failed to wrap GSSAPI encryption value into token."); return CURLE_COULDNT_CONNECT; } gss_release_buffer(&gss_status, &gss_send_token); us_length = htons((short)gss_w_token.length); memcpy(socksreq+2,&us_length,sizeof(short)); } code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written); if((code != CURLE_OK) || (4 != written)) { failf(data, "Failed to send GSSAPI encryption request."); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(data->set.socks5_gssapi_nec) { memcpy(socksreq, &gss_enc, 1); code = Curl_write_plain(conn, sock, socksreq, 1, &written); if((code != CURLE_OK) || ( 1 != written)) { failf(data, "Failed to send GSSAPI encryption type."); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } } else { code = Curl_write_plain(conn, sock, (char *)gss_w_token.value, gss_w_token.length, &written); if((code != CURLE_OK) || ((ssize_t)gss_w_token.length != written)) { failf(data, "Failed to send GSSAPI encryption type."); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } gss_release_buffer(&gss_status, &gss_w_token); } result=Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread, timeout); if(result != CURLE_OK || actualread != 4) { failf(data, "Failed to receive GSSAPI encryption response."); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } /* ignore the first (VER) byte */ if(socksreq[1] == 255) { /* status / message type */ failf(data, "User was rejected by the SOCKS5 server (%d %d).", socksreq[0], socksreq[1]); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(socksreq[1] != 2) { /* status / messgae type */ failf(data, "Invalid GSSAPI encryption response type (%d %d).", socksreq[0], socksreq[1]); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } memcpy(&us_length, socksreq+2, sizeof(short)); us_length = ntohs(us_length); gss_recv_token.length= us_length; gss_recv_token.value=malloc(gss_recv_token.length); if(!gss_recv_token.value) { gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_OUT_OF_MEMORY; } result=Curl_blockread_all(conn, sock, (char *)gss_recv_token.value, gss_recv_token.length, &actualread, timeout); if(result != CURLE_OK || actualread != us_length) { failf(data, "Failed to receive GSSAPI encryptrion type."); gss_release_buffer(&gss_status, &gss_recv_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(!data->set.socks5_gssapi_nec) { gss_major_status = gss_unwrap(&gss_minor_status, gss_context, &gss_recv_token, &gss_w_token, 0, GSS_C_QOP_DEFAULT); if(check_gss_err(data,gss_major_status,gss_minor_status,"gss_unwrap")) { gss_release_buffer(&gss_status, &gss_recv_token); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); failf(data, "Failed to unwrap GSSAPI encryption value into token."); return CURLE_COULDNT_CONNECT; } gss_release_buffer(&gss_status, &gss_recv_token); if(gss_w_token.length != 1) { failf(data, "Invalid GSSAPI encryption response length (%d).", gss_w_token.length); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } memcpy(socksreq,gss_w_token.value,gss_w_token.length); gss_release_buffer(&gss_status, &gss_w_token); } else { if(gss_recv_token.length != 1) { failf(data, "Invalid GSSAPI encryption response length (%d).", gss_recv_token.length); gss_release_buffer(&gss_status, &gss_recv_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } memcpy(socksreq,gss_recv_token.value,gss_recv_token.length); gss_release_buffer(&gss_status, &gss_recv_token); } infof(data, "SOCKS5 access with%s protection granted.\n", (socksreq[0]==0)?"out gssapi data": ((socksreq[0]==1)?" gssapi integrity":" gssapi confidentiality")); conn->socks5_gssapi_enctype = socksreq[0]; if(socksreq[0] == 0) gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_OK; }