struct http_challenge *http_header_get_proxy_challenge(const struct http_header *header, struct http_challenge *challenge) { const struct http_header *p; http_challenge_init(challenge); p = NULL; while ((p = http_header_next(header, p, "Proxy-Authenticate")) != NULL) { const char *tmp; tmp = p->value; while (*tmp != '\0') { struct http_challenge tmp_info; tmp = http_read_challenge(tmp, &tmp_info); if (tmp == NULL) { http_challenge_free(challenge); return NULL; } if (auth_scheme_is_better(tmp_info.scheme, challenge->scheme)) { http_challenge_free(challenge); *challenge = tmp_info; } else { http_challenge_free(&tmp_info); } } } return challenge; }
static const char *http_read_challenge(const char *s, struct http_challenge *challenge) { const char *p; char *scheme; http_challenge_init(challenge); scheme = NULL; s = read_token(s, &scheme); if (s == NULL) goto bail; if (str_equal_i(scheme, "Basic")) { challenge->scheme = AUTH_BASIC; } else if (str_equal_i(scheme, "Digest")) { challenge->scheme = AUTH_DIGEST; } else { challenge->scheme = AUTH_UNKNOWN; } free(scheme); scheme = NULL; /* RFC 2617, section 1.2, requires at least one auth-param: challenge = auth-scheme 1*SP 1#auth-param But there are some schemes (NTLM and Negotiate) that can be without auth-params, so we allow that here. A comma indicates the end of this challenge and the beginning of the next (see the comment in the loop below). */ while (is_space_char(*s)) s++; if (*s == ',') { s++; while (is_space_char(*s)) s++; if (*s == '\0') goto bail; return s; } while (*s != '\0') { char *name, *value; p = read_token(s, &name); if (p == NULL) goto bail; while (is_space_char(*p)) p++; /* It's possible that we've hit the end of one challenge and the beginning of another. Section 14.33 says that the header value can be 1#challenge, in other words several challenges separated by commas. Because the auth-params are also separated by commas, the only way we can tell is if we find a token not followed by an equals sign. */ if (*p != '=') break; p++; while (is_space_char(*p)) p++; p = read_token_or_quoted_string(p, &value); if (p == NULL) { free(name); goto bail; } if (str_equal_i(name, "realm")) challenge->realm = Strdup(value); else if (challenge->scheme == AUTH_DIGEST) { if (str_equal_i(name, "nonce")) { if (challenge->digest.nonce != NULL) goto bail; challenge->digest.nonce = Strdup(value); } else if (str_equal_i(name, "opaque")) { if (challenge->digest.opaque != NULL) goto bail; challenge->digest.opaque = Strdup(value); } else if (str_equal_i(name, "algorithm")) { if (str_equal_i(value, "MD5")) challenge->digest.algorithm = ALGORITHM_MD5; else challenge->digest.algorithm = ALGORITHM_UNKNOWN; } else if (str_equal_i(name, "qop")) { char **tokens; size_t n; int i; const char *tmp; tmp = read_token_list(value, &tokens, &n); if (tmp == NULL) { free(name); free(value); goto bail; } for (i = 0; i < n; i++) { if (str_equal_i(tokens[i], "auth")) challenge->digest.qop |= QOP_AUTH; else if (str_equal_i(tokens[i], "auth-int")) challenge->digest.qop |= QOP_AUTH_INT; } for (i = 0; i < n; i++) free(tokens[i]); free(tokens); if (*tmp != '\0') { free(name); free(value); goto bail; } } } free(name); free(value); while (is_space_char(*p)) p++; if (*p == ',') { p++; while (is_space_char(*p)) p++; if (*p == '\0') goto bail; } s = p; } return s; bail: if (scheme != NULL) free(scheme); http_challenge_free(challenge); return NULL; }
/* Return a usable socket descriptor after proxy negotiation, or -1 on any error. If any bytes are received through the proxy after negotiation, they are written to stdout. */ static int do_proxy_http(void) { struct socket_buffer sockbuf; char *request; char *status_line, *header; char *remainder; size_t len; int sd, code; int n; sd = do_connect(SOCK_STREAM); if (sd == -1) { loguser("Proxy connection failed: %s.\n", socket_strerror(socket_errno())); return -1; } status_line = NULL; header = NULL; /* First try a request with no authentication. */ request = http_connect_request(&httpconnect, &n); if (send(sd, request, n, 0) < 0) { loguser("Error sending proxy request: %s.\n", socket_strerror(socket_errno())); free(request); return -1; } free(request); socket_buffer_init(&sockbuf, sd); if (http_read_status_line(&sockbuf, &status_line) != 0) { loguser("Error reading proxy response Status-Line.\n"); goto bail; } code = http_parse_status_line_code(status_line); logdebug("Proxy returned status code %d.\n", code); free(status_line); status_line = NULL; if (http_read_header(&sockbuf, &header) != 0) { loguser("Error reading proxy response header.\n"); goto bail; } if (code == 407 && o.proxy_auth != NULL) { struct http_header *h; struct http_challenge challenge; close(sd); sd = -1; if (http_parse_header(&h, header) != 0) { loguser("Error parsing proxy response header.\n"); goto bail; } free(header); header = NULL; if (http_header_get_proxy_challenge(h, &challenge) == NULL) { loguser("Error getting Proxy-Authenticate challenge.\n"); http_header_free(h); goto bail; } http_header_free(h); sd = do_connect(SOCK_STREAM); if (sd == -1) { loguser("Proxy reconnection failed: %s.\n", socket_strerror(socket_errno())); goto bail; } request = http_connect_request_auth(&httpconnect, &n, &challenge); if (request == NULL) { loguser("Error building Proxy-Authorization header.\n"); http_challenge_free(&challenge); goto bail; } logdebug("Reconnection header:\n%s", request); if (send(sd, request, n, 0) < 0) { loguser("Error sending proxy request: %s.\n", socket_strerror(socket_errno())); free(request); http_challenge_free(&challenge); goto bail; } free(request); http_challenge_free(&challenge); socket_buffer_init(&sockbuf, sd); if (http_read_status_line(&sockbuf, &status_line) != 0) { loguser("Error reading proxy response Status-Line.\n"); goto bail; } code = http_parse_status_line_code(status_line); logdebug("Proxy returned status code %d.\n", code); free(status_line); status_line = NULL; if (http_read_header(&sockbuf, &header) != 0) { loguser("Error reading proxy response header.\n"); goto bail; } } free(header); header = NULL; if (code != 200) { loguser("Proxy returned status code %d.\n", code); return -1; } remainder = socket_buffer_remainder(&sockbuf, &len); Write(STDOUT_FILENO, remainder, len); return sd; bail: if (sd != -1) close(sd); if (status_line != NULL) free(status_line); if (header != NULL) free(header); return -1; }