1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 **/
19 
20 #include "common.h"
21 #include "log.h"
22 #include "zbxhttp.h"
23 
24 #ifdef HAVE_LIBCURL
25 
26 extern char	*CONFIG_SOURCE_IP;
27 
28 extern char	*CONFIG_SSL_CA_LOCATION;
29 extern char	*CONFIG_SSL_CERT_LOCATION;
30 extern char	*CONFIG_SSL_KEY_LOCATION;
31 
zbx_curl_write_cb(void * ptr,size_t size,size_t nmemb,void * userdata)32 size_t	zbx_curl_write_cb(void *ptr, size_t size, size_t nmemb, void *userdata)
33 {
34 	size_t			r_size = size * nmemb;
35 	zbx_http_response_t	*response;
36 
37 	response = (zbx_http_response_t*)userdata;
38 
39 	if (ZBX_MAX_RECV_DATA_SIZE < response->offset + r_size)
40 		return 0;
41 
42 	zbx_str_memcpy_alloc(&response->data, &response->allocated, &response->offset, (const char *)ptr, r_size);
43 
44 	return r_size;
45 }
46 
zbx_curl_ignore_cb(void * ptr,size_t size,size_t nmemb,void * userdata)47 size_t	zbx_curl_ignore_cb(void *ptr, size_t size, size_t nmemb, void *userdata)
48 {
49 	ZBX_UNUSED(ptr);
50 	ZBX_UNUSED(userdata);
51 
52 	return size * nmemb;
53 }
54 
zbx_http_prepare_callbacks(CURL * easyhandle,zbx_http_response_t * header,zbx_http_response_t * body,void * header_cb,void * body_cb,char * errbuf,char ** error)55 int	zbx_http_prepare_callbacks(CURL *easyhandle, zbx_http_response_t *header,
56 		zbx_http_response_t *body, void *header_cb, void *body_cb, char *errbuf, char **error)
57 {
58 	CURLcode	err;
59 
60 	if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_HEADERFUNCTION, header_cb)))
61 	{
62 		*error = zbx_dsprintf(*error, "Cannot set header function: %s", curl_easy_strerror(err));
63 		return FAIL;
64 	}
65 
66 	if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_HEADERDATA, header)))
67 	{
68 		*error = zbx_dsprintf(*error, "Cannot set header callback: %s", curl_easy_strerror(err));
69 		return FAIL;
70 	}
71 
72 	if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, body_cb)))
73 	{
74 		*error = zbx_dsprintf(*error, "Cannot set write function: %s", curl_easy_strerror(err));
75 		return FAIL;
76 	}
77 
78 	if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, body)))
79 	{
80 		*error = zbx_dsprintf(*error, "Cannot set write callback: %s", curl_easy_strerror(err));
81 		return FAIL;
82 	}
83 
84 	if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_ERRORBUFFER, errbuf)))
85 	{
86 		*error = zbx_dsprintf(*error, "Cannot set error buffer: %s", curl_easy_strerror(err));
87 		return FAIL;
88 	}
89 
90 	return SUCCEED;
91 }
92 
zbx_http_prepare_ssl(CURL * easyhandle,const char * ssl_cert_file,const char * ssl_key_file,const char * ssl_key_password,unsigned char verify_peer,unsigned char verify_host,char ** error)93 int	zbx_http_prepare_ssl(CURL *easyhandle, const char *ssl_cert_file, const char *ssl_key_file,
94 		const char *ssl_key_password, unsigned char verify_peer, unsigned char verify_host,
95 		char **error)
96 {
97 	CURLcode	err;
98 
99 	if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_SSL_VERIFYPEER, 0 == verify_peer ? 0L : 1L)))
100 	{
101 		*error = zbx_dsprintf(*error, "Cannot set verify the peer's SSL certificate: %s",
102 				curl_easy_strerror(err));
103 		return FAIL;
104 	}
105 
106 	if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_SSL_VERIFYHOST, 0 == verify_host ? 0L : 2L)))
107 	{
108 		*error = zbx_dsprintf(*error, "Cannot set verify the certificate's name against host: %s",
109 				curl_easy_strerror(err));
110 		return FAIL;
111 	}
112 
113 	if (NULL != CONFIG_SOURCE_IP)
114 	{
115 		if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_INTERFACE, CONFIG_SOURCE_IP)))
116 		{
117 			*error = zbx_dsprintf(*error, "Cannot specify source interface for outgoing traffic: %s",
118 					curl_easy_strerror(err));
119 			return FAIL;
120 		}
121 	}
122 
123 	if (0 != verify_peer && NULL != CONFIG_SSL_CA_LOCATION)
124 	{
125 		if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_CAPATH, CONFIG_SSL_CA_LOCATION)))
126 		{
127 			*error = zbx_dsprintf(*error, "Cannot specify directory holding CA certificates: %s",
128 					curl_easy_strerror(err));
129 			return FAIL;
130 		}
131 	}
132 
133 	if ('\0' != *ssl_cert_file)
134 	{
135 		char	*file_name;
136 
137 		file_name = zbx_dsprintf(NULL, "%s/%s", CONFIG_SSL_CERT_LOCATION, ssl_cert_file);
138 		zabbix_log(LOG_LEVEL_DEBUG, "using SSL certificate file: '%s'", file_name);
139 
140 		err = curl_easy_setopt(easyhandle, CURLOPT_SSLCERT, file_name);
141 		zbx_free(file_name);
142 
143 		if (CURLE_OK != err)
144 		{
145 			*error = zbx_dsprintf(*error, "Cannot set SSL client certificate: %s", curl_easy_strerror(err));
146 			return FAIL;
147 		}
148 
149 		if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_SSLCERTTYPE, "PEM")))
150 		{
151 			*error = zbx_dsprintf(NULL, "Cannot specify type of the client SSL certificate: %s",
152 					curl_easy_strerror(err));
153 			return FAIL;
154 		}
155 	}
156 
157 	if ('\0' != *ssl_key_file)
158 	{
159 		char	*file_name;
160 
161 		file_name = zbx_dsprintf(NULL, "%s/%s", CONFIG_SSL_KEY_LOCATION, ssl_key_file);
162 		zabbix_log(LOG_LEVEL_DEBUG, "using SSL private key file: '%s'", file_name);
163 
164 		err = curl_easy_setopt(easyhandle, CURLOPT_SSLKEY, file_name);
165 		zbx_free(file_name);
166 
167 		if (CURLE_OK != err)
168 		{
169 			*error = zbx_dsprintf(NULL, "Cannot specify private keyfile for TLS and SSL client cert: %s",
170 					curl_easy_strerror(err));
171 			return FAIL;
172 		}
173 
174 		if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_SSLKEYTYPE, "PEM")))
175 		{
176 			*error = zbx_dsprintf(NULL, "Cannot set type of the private key file: %s",
177 					curl_easy_strerror(err));
178 			return FAIL;
179 		}
180 	}
181 
182 	if ('\0' != *ssl_key_password)
183 	{
184 		if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_KEYPASSWD, ssl_key_password)))
185 		{
186 			*error = zbx_dsprintf(NULL, "Cannot set passphrase to private key: %s",
187 					curl_easy_strerror(err));
188 			return FAIL;
189 		}
190 	}
191 
192 	return SUCCEED;
193 }
194 
zbx_http_prepare_auth(CURL * easyhandle,unsigned char authtype,const char * username,const char * password,char ** error)195 int	zbx_http_prepare_auth(CURL *easyhandle, unsigned char authtype, const char *username, const char *password,
196 		char **error)
197 {
198 	if (HTTPTEST_AUTH_NONE != authtype)
199 	{
200 		long		curlauth = 0;
201 		char		auth[MAX_STRING_LEN];
202 		CURLcode	err;
203 
204 		zabbix_log(LOG_LEVEL_DEBUG, "setting HTTPAUTH [%d]", authtype);
205 
206 		switch (authtype)
207 		{
208 			case HTTPTEST_AUTH_BASIC:
209 				curlauth = CURLAUTH_BASIC;
210 				break;
211 			case HTTPTEST_AUTH_NTLM:
212 				curlauth = CURLAUTH_NTLM;
213 				break;
214 			case HTTPTEST_AUTH_NEGOTIATE:
215 #if LIBCURL_VERSION_NUM >= 0x072600
216 				curlauth = CURLAUTH_NEGOTIATE;
217 #else
218 				curlauth = CURLAUTH_GSSNEGOTIATE;
219 #endif
220 				break;
221 			case HTTPTEST_AUTH_DIGEST:
222 				curlauth = CURLAUTH_DIGEST;
223 				break;
224 			default:
225 				THIS_SHOULD_NEVER_HAPPEN;
226 				break;
227 		}
228 
229 		if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_HTTPAUTH, curlauth)))
230 		{
231 			*error = zbx_dsprintf(*error, "Cannot set HTTP server authentication method: %s",
232 					curl_easy_strerror(err));
233 			return FAIL;
234 		}
235 
236 		zbx_snprintf(auth, sizeof(auth), "%s:%s", username, password);
237 		if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_USERPWD, auth)))
238 		{
239 			*error = zbx_dsprintf(*error, "Cannot set user name and password: %s",
240 					curl_easy_strerror(err));
241 			return FAIL;
242 		}
243 	}
244 
245 	return SUCCEED;
246 }
247 
zbx_http_parse_header(char ** headers)248 char	*zbx_http_parse_header(char **headers)
249 {
250 	while ('\0' != **headers)
251 	{
252 		char	c, *p_end, *line;
253 
254 		while ('\r' == **headers || '\n' == **headers)
255 			(*headers)++;
256 
257 		p_end = *headers;
258 
259 		while ('\0' != *p_end && '\r' != *p_end && '\n' != *p_end)
260 			p_end++;
261 
262 		if (*headers == p_end)
263 			return NULL;
264 
265 		if ('\0' != (c = *p_end))
266 			*p_end = '\0';
267 		line = zbx_strdup(NULL, *headers);
268 		if ('\0' != c)
269 			*p_end = c;
270 
271 		*headers = p_end;
272 
273 		zbx_lrtrim(line, " \t");
274 		if ('\0' == *line)
275 			zbx_free(line);
276 		else
277 			return line;
278 	}
279 
280 	return NULL;
281 }
282 
zbx_http_get(const char * url,const char * header,long timeout,char ** out,long * response_code,char ** error)283 int	zbx_http_get(const char *url, const char *header, long timeout, char **out, long *response_code, char **error)
284 {
285 	CURL			*easyhandle;
286 	CURLcode		err;
287 	char			errbuf[CURL_ERROR_SIZE];
288 	int			ret = FAIL;
289 	struct curl_slist	*headers_slist = NULL;
290 	zbx_http_response_t	body = {0}, response_header = {0};
291 
292 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() URL '%s'", __func__, url);
293 
294 	*errbuf = '\0';
295 
296 	if (NULL == (easyhandle = curl_easy_init()))
297 	{
298 		*error = zbx_strdup(NULL, "Cannot initialize cURL library");
299 		goto clean;
300 	}
301 
302 	if (SUCCEED != zbx_http_prepare_callbacks(easyhandle, &response_header, &body, zbx_curl_ignore_cb,
303 			zbx_curl_write_cb, errbuf, error))
304 	{
305 		goto clean;
306 	}
307 
308 	if (SUCCEED != zbx_http_prepare_ssl(easyhandle, "", "", "", 1, 1, error))
309 		goto clean;
310 
311 	if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_USERAGENT, "Zabbix " ZABBIX_VERSION)))
312 	{
313 		*error = zbx_dsprintf(NULL, "Cannot set user agent: %s", curl_easy_strerror(err));
314 		goto clean;
315 	}
316 
317 	if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_PROXY, "")))
318 	{
319 		*error = zbx_dsprintf(NULL, "Cannot set proxy: %s", curl_easy_strerror(err));
320 		goto clean;
321 	}
322 
323 	if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_TIMEOUT, timeout)))
324 	{
325 		*error = zbx_dsprintf(NULL, "Cannot specify timeout: %s", curl_easy_strerror(err));
326 		goto clean;
327 	}
328 
329 	if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER,
330 			(headers_slist = curl_slist_append(headers_slist, header)))))
331 	{
332 		*error = zbx_dsprintf(NULL, "Cannot specify headers: %s", curl_easy_strerror(err));
333 		goto clean;
334 	}
335 
336 	if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_URL, url)))
337 	{
338 		*error = zbx_dsprintf(NULL, "Cannot specify URL: %s", curl_easy_strerror(err));
339 		goto clean;
340 	}
341 
342 	if (CURLE_OK != (err = curl_easy_perform(easyhandle)))
343 	{
344 		*error = zbx_dsprintf(NULL, "Cannot perform request: %s", '\0' == *errbuf ? curl_easy_strerror(err) :
345 				errbuf);
346 		goto clean;
347 	}
348 
349 	if (CURLE_OK != (err = curl_easy_getinfo(easyhandle, CURLINFO_RESPONSE_CODE, response_code)))
350 	{
351 		*error = zbx_dsprintf(NULL, "Cannot get the response code: %s", curl_easy_strerror(err));
352 		goto clean;
353 	}
354 
355 	if (NULL != body.data)
356 	{
357 		*out = body.data;
358 		body.data = NULL;
359 	}
360 
361 	else
362 		*out = zbx_strdup(NULL, "");
363 
364 	ret = SUCCEED;
365 clean:
366 	curl_slist_free_all(headers_slist);	/* must be called after curl_easy_perform() */
367 	curl_easy_cleanup(easyhandle);
368 	zbx_free(body.data);
369 
370 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
371 
372 	return ret;
373 }
374 
375 #endif
376