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