diff options
author | Stanley Hopcroft <stanleyhopcroft@users.sourceforge.net> | 2005-02-01 04:20:04 +0000 |
---|---|---|
committer | Stanley Hopcroft <stanleyhopcroft@users.sourceforge.net> | 2005-02-01 04:20:04 +0000 |
commit | b7c0754632ece2e05cbb5d3d7af6a925d2dfba7b (patch) | |
tree | 04a119448bf489413e4b9356c0525d65b230d21e /contrib/check_http-with-client-certificate.c | |
parent | 0e1ff207546bf7205cc1532287fc2e0499b273cb (diff) | |
download | monitoring-plugins-b7c0754632ece2e05cbb5d3d7af6a925d2dfba7b.tar.gz |
New or revised plugin in /contrib
git-svn-id: https://nagiosplug.svn.sourceforge.net/svnroot/nagiosplug/nagiosplug/trunk@1117 f882894a-f735-0410-b71e-b25c423dba1c
Diffstat (limited to 'contrib/check_http-with-client-certificate.c')
-rw-r--r-- | contrib/check_http-with-client-certificate.c | 1567 |
1 files changed, 1567 insertions, 0 deletions
diff --git a/contrib/check_http-with-client-certificate.c b/contrib/check_http-with-client-certificate.c new file mode 100644 index 00000000..157d028a --- /dev/null +++ b/contrib/check_http-with-client-certificate.c | |||
@@ -0,0 +1,1567 @@ | |||
1 | /**************************************************************************** | ||
2 | * | ||
3 | * Program: HTTP plugin for Nagios | ||
4 | * License: GPL | ||
5 | * | ||
6 | * License Information: | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | * | ||
22 | *****************************************************************************/ | ||
23 | |||
24 | /**************************************************************************** | ||
25 | * | ||
26 | * check_http is derived from the original check_http provided by | ||
27 | * Ethan Galstad/Karl DeBisschop | ||
28 | * | ||
29 | * This provides some additional functionality including: | ||
30 | * - check server certificate against supplied hostname (Host: header) if any | ||
31 | * - check server certificate against local CA certificates (as browsers do) | ||
32 | * - authenticate with client certificate (and optional passphrase) | ||
33 | * - specify HTTP returncodes to return a status of WARNING or OK instead of | ||
34 | * CRITICAL (only global for 3xx or 4xx errors) | ||
35 | * - check only against HTTP status line and exit immediately if not matched | ||
36 | * | ||
37 | *****************************************************************************/ | ||
38 | |||
39 | const char *progname = "check_http"; | ||
40 | #define REVISION "$Revision$" | ||
41 | #define CVSREVISION "1.24" | ||
42 | #define COPYRIGHT "2003" | ||
43 | #define AUTHORS "Fabian Pehla" | ||
44 | #define EMAIL "fabian@pehla.de" | ||
45 | |||
46 | #include "config.h" | ||
47 | #include "common.h" | ||
48 | #include "netutils.h" | ||
49 | #include "utils.h" | ||
50 | |||
51 | |||
52 | #define HELP_TXT_SUMMARY "\ | ||
53 | This plugin tests the HTTP service on the specified host. It can test\n\ | ||
54 | normal (http) and secure (https) servers, follow redirects, search for\n\ | ||
55 | strings and regular expressions, check connection times, and report on\n\ | ||
56 | certificate expiration times.\n" | ||
57 | |||
58 | #define HELP_TXT_OPTIONS "\ | ||
59 | -H <virtual host> -I <ip address> [-p <port>] [-u <uri>]\n\ | ||
60 | [-w <warn time>] [-c <critical time>] [-t <timeout>]\n\ | ||
61 | [-S] [-C <days>] [-a <basic auth>] [-A <certificate file>]\n\ | ||
62 | [-Z <ca certificate file>] [-e <expect>] [-E <expect only>]\n\ | ||
63 | [-s <string>] [-r <regex>] [-R <regex case insensitive>]\n\ | ||
64 | [-f (ok|warn|critical|follow)] [-g (ok|warn|critical)]\n" | ||
65 | |||
66 | #define HELP_TXT_LONGOPTIONS "\ | ||
67 | -H, --hostname=<virtual host>\n\ | ||
68 | FQDN host name argument for use in HTTP Host:-Header (virtual host)\n\ | ||
69 | If used together wich the -S option, the server certificate will\n\ | ||
70 | be checked against this hostname\n\ | ||
71 | -I, --ip-address=<address>\n\ | ||
72 | IP address or hostname for TCP connect (use IP to avoid DNS lookup)\n\ | ||
73 | -p, --port=<port>\n\ | ||
74 | Port number (default: %d)\n\ | ||
75 | -u, --url-path=<uri>\n\ | ||
76 | URL to request from host (default: %s)\n\ | ||
77 | -S, --ssl\n\ | ||
78 | Use SSL (default port: %d)\n\ | ||
79 | -C, --server-certificate-days=<days>\n\ | ||
80 | Minimum number of days a server certificate must be valid\n\ | ||
81 | No other check can be combined with this option\n\ | ||
82 | -a, --basic-auth=<username:password>\n\ | ||
83 | Colon separated username and password for basic authentication\n\ | ||
84 | -A, --client-certificate=<certificate file>\n\ | ||
85 | File containing X509 client certificate and key\n\ | ||
86 | -K, --passphrase=<passphrase>\n\ | ||
87 | Passphrase for the client certificate key\n\ | ||
88 | This option can only be used in combination with the -A option\n\ | ||
89 | -Z, --ca-certificate=<certificate file>\n\ | ||
90 | File containing certificates of trusted CAs\n\ | ||
91 | The server certificate will be checked against these CAs\n\ | ||
92 | -e, --http-expect=<expect string>\n\ | ||
93 | String to expect in HTTP response line (Default: %s)\n\ | ||
94 | -E, --http-expect-only=<expect only string>\n\ | ||
95 | String to expect in HTTP response line\n\ | ||
96 | No other checks are made, this either matches the response\n\ | ||
97 | or exits immediately\n\ | ||
98 | -s, --content-string=<string>\n\ | ||
99 | String to expect in content\n\ | ||
100 | -r, --content-ereg=<regex>\n\ | ||
101 | Regular expression to expect in content\n\ | ||
102 | -R, --content-eregi=<regex case insensitive>\n\ | ||
103 | Case insensitive regular expression to expect in content\n\ | ||
104 | -f, --onredirect=(ok|warning|critical|follow)\n\ | ||
105 | Follow a redirect (3xx) or return with a user defined state\n\ | ||
106 | Default: OK\n\ | ||
107 | -g, --onerror=(ok|warning|critical)\n\ | ||
108 | Status to return on a client error (4xx)\n\ | ||
109 | -m, --min=INTEGER\n\ | ||
110 | Minimum page size required (bytes)\n\ | ||
111 | -t, --timeout=<timeout>\n\ | ||
112 | Seconds before connection times out (default: %d)\n\ | ||
113 | -c, --critical=<critical time>\n\ | ||
114 | Response time to result in critical status (seconds)\n\ | ||
115 | -w, --warning=<warn time>\n\ | ||
116 | Response time to result in warning status (seconds)\n\ | ||
117 | -V, --version\n\ | ||
118 | Print version information\n\ | ||
119 | -v, --verbose\n\ | ||
120 | Show details for command-line debugging (do not use with nagios server)\n\ | ||
121 | -h, --help\n\ | ||
122 | Print detailed help screen\n" | ||
123 | |||
124 | |||
125 | |||
126 | #define HTTP_PORT 80 | ||
127 | #define DEFAULT_HTTP_URL_PATH "/" | ||
128 | #define DEFAULT_HTTP_EXPECT "HTTP/1." | ||
129 | #define DEFAULT_HTTP_METHOD "GET" | ||
130 | #define DEFAULT_HTTP_REDIRECT_STATE STATE_OK | ||
131 | #define DEFAULT_HTTP_CLIENT_ERROR_STATE STATE_WARNING | ||
132 | |||
133 | #define HTTP_TEMPLATE_REQUEST "%s%s %s HTTP/1.0\r\n" | ||
134 | #define HTTP_TEMPLATE_HEADER_USERAGENT "%sUser-Agent: %s/%s (nagios-plugins %s)\r\n" | ||
135 | #define HTTP_TEMPLATE_HEADER_HOST "%sHost: %s\r\n" | ||
136 | #define HTTP_TEMPLATE_HEADER_AUTH "%sAuthorization: Basic %s\r\n" | ||
137 | |||
138 | /* fill in printf with protocol_text(use_ssl), state_text(state), page->status, elapsed_time */ | ||
139 | #define RESULT_TEMPLATE_STATUS_RESPONSE_TIME "%s %s: %s - %7.3f seconds response time|time=%7.3f\n" | ||
140 | #define RESULT_TEMPLATE_RESPONSE_TIME "%s %s: %7.3f seconds response time|time=%7.3f\n" | ||
141 | |||
142 | #ifdef HAVE_SSL | ||
143 | |||
144 | #ifdef HAVE_SSL_H | ||
145 | #include <rsa.h> | ||
146 | #include <crypto.h> | ||
147 | #include <x509.h> | ||
148 | #include <pem.h> | ||
149 | #include <ssl.h> | ||
150 | #include <err.h> | ||
151 | #include <rand.h> | ||
152 | #endif | ||
153 | |||
154 | #ifdef HAVE_OPENSSL_SSL_H | ||
155 | #include <openssl/rsa.h> | ||
156 | #include <openssl/crypto.h> | ||
157 | #include <openssl/x509.h> | ||
158 | #include <openssl/pem.h> | ||
159 | #include <openssl/ssl.h> | ||
160 | #include <openssl/err.h> | ||
161 | #include <openssl/rand.h> | ||
162 | #endif | ||
163 | |||
164 | #define HTTPS_PORT 443 | ||
165 | #endif | ||
166 | |||
167 | #ifdef HAVE_REGEX_H | ||
168 | #include <regex.h> | ||
169 | #define REGEX_REGS 2 | ||
170 | #define MAX_REGEX_SIZE 256 | ||
171 | #endif | ||
172 | |||
173 | #define chk_protocol(protocol) ( strstr( protocol, "https" ) ? TRUE : FALSE ); | ||
174 | #define protocol_std_port(use_ssl) ( use_ssl ? HTTPS_PORT : HTTP_PORT ); | ||
175 | #define protocol_text(use_ssl) ( use_ssl ? "HTTPS" : "HTTP" ) | ||
176 | |||
177 | #define MAX_IPV4_HOSTLENGTH 64 | ||
178 | #define HTTP_HEADER_LOCATION_MATCH "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: " | ||
179 | #define HTTP_HEADER_PROTOCOL_MATCH "%[HTPShtps]://" | ||
180 | #define HTTP_HEADER_HOSTNAME_MATCH "%[a-zA-Z0-9.-]" | ||
181 | #define HTTP_HEADER_PORT_MATCH ":%[0-9]" | ||
182 | #define HTTP_HEADER_URL_PATH_MATCH "%[/a-zA-Z0-9._-=@,]" | ||
183 | |||
184 | /* | ||
185 | ************************************************************************ | ||
186 | * GLOBAL VARIABLE/POINTER DEFINITIONS * | ||
187 | ************************************************************************ | ||
188 | */ | ||
189 | |||
190 | /* misc variables */ | ||
191 | int verbose = FALSE; | ||
192 | |||
193 | /* time thresholds to determine exit code */ | ||
194 | int use_warning_interval = FALSE; | ||
195 | double warning_interval = 0; | ||
196 | int use_critical_interval = FALSE; | ||
197 | double critical_interval = 0; | ||
198 | double elapsed_time = 0; | ||
199 | struct timeval start_tv; | ||
200 | |||
201 | /* variables concerning the server host */ | ||
202 | int use_server_hostname = FALSE; | ||
203 | char *server_hostname = ""; // hostname for use in HTTPs Host: header | ||
204 | char *server_host = ""; // hostname or ip address for tcp connect | ||
205 | int use_server_port = FALSE; | ||
206 | int server_port = HTTP_PORT; | ||
207 | |||
208 | int use_basic_auth = FALSE; | ||
209 | char basic_auth[MAX_INPUT_BUFFER] = ""; | ||
210 | |||
211 | /* variables concerning server responce */ | ||
212 | struct pageref { | ||
213 | char *content; | ||
214 | size_t size; | ||
215 | char *status; | ||
216 | char *header; | ||
217 | char *body; | ||
218 | }; | ||
219 | |||
220 | /* variables concerning ssl connections */ | ||
221 | int use_ssl = FALSE; | ||
222 | #ifdef HAVE_SSL | ||
223 | int server_certificate_min_days_valid = 0; | ||
224 | int check_server_certificate = FALSE; | ||
225 | X509 *server_certificate; // structure containing server certificate | ||
226 | int use_client_certificate = FALSE; | ||
227 | char *client_certificate_file = NULL; | ||
228 | int use_client_certificate_passphrase = FALSE; | ||
229 | char *client_certificate_passphrase = NULL; | ||
230 | int use_ca_certificate = FALSE; | ||
231 | char *ca_certificate_file = NULL; | ||
232 | |||
233 | BIO *bio_err = 0; // error write context | ||
234 | #endif | ||
235 | |||
236 | |||
237 | /* variables concerning check behaviour */ | ||
238 | char *http_method = DEFAULT_HTTP_METHOD; | ||
239 | char *http_url_path = ""; | ||
240 | int use_http_post_data = FALSE; | ||
241 | char *http_post_data = ""; | ||
242 | int use_min_content_length = FALSE; | ||
243 | int min_content_length = 0; | ||
244 | int use_http_expect_only = FALSE; | ||
245 | char http_expect[MAX_INPUT_BUFFER] = DEFAULT_HTTP_EXPECT; | ||
246 | int check_content_string = FALSE; | ||
247 | char content_string[MAX_INPUT_BUFFER] = ""; | ||
248 | int http_redirect_state = DEFAULT_HTTP_REDIRECT_STATE; | ||
249 | int http_client_error_state = DEFAULT_HTTP_CLIENT_ERROR_STATE; | ||
250 | |||
251 | #ifdef HAVE_REGEX_H | ||
252 | regex_t regex_preg; | ||
253 | regmatch_t regex_pmatch[REGEX_REGS]; | ||
254 | int check_content_regex = FALSE; | ||
255 | char content_regex[MAX_REGEX_SIZE] = ""; | ||
256 | int regex_cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE; | ||
257 | int regex_error = 0; | ||
258 | char regex_error_buffer[MAX_INPUT_BUFFER] = ""; | ||
259 | #endif | ||
260 | |||
261 | |||
262 | |||
263 | /* | ||
264 | ************************************************************************ | ||
265 | * FUNCTION PROTOTYPES * | ||
266 | ************************************************************************ | ||
267 | */ | ||
268 | |||
269 | void print_usage( void ); | ||
270 | void print_help( void ); | ||
271 | int process_arguments (int, char **); | ||
272 | int http_request( int sock, struct pageref *page); | ||
273 | |||
274 | int parse_http_response( struct pageref *page ); | ||
275 | int check_http_response( struct pageref *page ); | ||
276 | int check_http_content( struct pageref *page ); | ||
277 | int prepare_follow_redirect( struct pageref *page ); | ||
278 | |||
279 | static char *base64 (char *bin, int len); | ||
280 | |||
281 | #ifdef HAVE_SSL | ||
282 | int ssl_terminate( int state, char *string ); | ||
283 | static int passwd_cb( char *buf, int num, int rwflag, void *userdata ); | ||
284 | static void sigpipe_handle( int x ); | ||
285 | SSL_CTX * initialize_ssl_ctx( void ); | ||
286 | void destroy_ssl_ctx( SSL_CTX *ctx ); | ||
287 | int fetch_server_certificate( SSL *ssl ); | ||
288 | int check_server_certificate_chain( SSL *ssl ); | ||
289 | int check_server_certificate_hostname( void ); | ||
290 | int check_server_certificate_expires( void ); | ||
291 | int https_request( SSL_CTX *ctx, SSL *ssl, struct pageref *page ); | ||
292 | #endif | ||
293 | |||
294 | /* | ||
295 | ************************************************************************ | ||
296 | * IMPLEMENTATION * | ||
297 | ************************************************************************ | ||
298 | */ | ||
299 | |||
300 | /* | ||
301 | * main() | ||
302 | * | ||
303 | * PSEUDOCODE OF HOW MAIN IS SUPPOSED TO WORK | ||
304 | * | ||
305 | * process command line arguments including sanity check | ||
306 | * initialize alarm signal handling | ||
307 | * if use_ssl | ||
308 | * build ssl context | ||
309 | * LOOP: | ||
310 | * make tcp connection | ||
311 | * if use_ssl | ||
312 | * make ssl connection | ||
313 | * if use_server_hostname | ||
314 | * check if certificate matches hostname | ||
315 | * if check_server_certificate | ||
316 | * check expiration date of server certificate | ||
317 | * return STATUS | ||
318 | * else | ||
319 | * request http page | ||
320 | * handle ssl rehandshake | ||
321 | * close ssl connection | ||
322 | * else | ||
323 | * request http page | ||
324 | * close tcp connection | ||
325 | * analyze http page | ||
326 | * if follow on redirect | ||
327 | * repeat LOOP | ||
328 | * end of LOOP | ||
329 | * destroy ssl context | ||
330 | */ | ||
331 | int | ||
332 | main (int argc, char **argv) | ||
333 | { | ||
334 | int result = STATE_UNKNOWN; | ||
335 | int sock; | ||
336 | struct pageref page; | ||
337 | #ifdef HAVE_SSL | ||
338 | SSL_CTX *ctx; | ||
339 | SSL *ssl; | ||
340 | BIO *sbio; | ||
341 | #endif | ||
342 | |||
343 | if ( process_arguments(argc, argv) == ERROR ) | ||
344 | usage( "check_http: could not parse arguments\n" ); | ||
345 | |||
346 | #ifdef HAVE_SSL | ||
347 | /* build SSL context if required: | ||
348 | * a) either we use ssl from the beginning OR | ||
349 | * b) or we follor redirects wich may lead os to a ssl page | ||
350 | */ | ||
351 | if ( use_ssl || ( http_redirect_state == STATE_DEPENDENT ) ) | ||
352 | ctx=initialize_ssl_ctx(); | ||
353 | #endif | ||
354 | |||
355 | /* Loop around 3xx onredirect=follow */ | ||
356 | do { | ||
357 | |||
358 | /* | ||
359 | * initialize alarm signal handling, set socket timeout, start timer | ||
360 | * socket_timeout and socket_timeout_alarm_handler are defined in | ||
361 | * netutils.c | ||
362 | */ | ||
363 | (void) signal( SIGALRM, socket_timeout_alarm_handler ); | ||
364 | (void) alarm( socket_timeout ); | ||
365 | gettimeofday( &start_tv, NULL ); | ||
366 | |||
367 | /* make a tcp connection */ | ||
368 | result = my_tcp_connect( server_host, server_port, &sock ); | ||
369 | |||
370 | /* result of tcp connect */ | ||
371 | if ( result == STATE_OK ) | ||
372 | { | ||
373 | #ifdef HAVE_SSL | ||
374 | /* make a ssl connection */ | ||
375 | if ( use_ssl ) { | ||
376 | ssl=SSL_new( ctx ); | ||
377 | sbio=BIO_new_socket( sock, BIO_NOCLOSE ); | ||
378 | SSL_set_bio( ssl, sbio, sbio); | ||
379 | if ( SSL_connect( ssl ) <= 0 ) | ||
380 | ssl_terminate( STATE_CRITICAL, "check_http: SSL connect error" ); | ||
381 | |||
382 | /* fetch server certificate */ | ||
383 | result = fetch_server_certificate( ssl ); | ||
384 | |||
385 | /* verify server certificate against CAs */ | ||
386 | if ( ( result == STATE_OK ) && use_ca_certificate ) { | ||
387 | result = check_server_certificate_chain( ssl ); | ||
388 | } | ||
389 | |||
390 | /* check if certificate matches hostname */ | ||
391 | if ( ( result == STATE_OK ) && use_server_hostname ) { | ||
392 | result = check_server_certificate_hostname(); | ||
393 | } | ||
394 | |||
395 | if ( result == STATE_OK ) { | ||
396 | /* check server certificate expire date */ | ||
397 | if ( check_server_certificate ) { | ||
398 | result = check_server_certificate_expires(); | ||
399 | /* OR: perform http request */ | ||
400 | } else { | ||
401 | result = https_request( ctx, ssl, (struct pageref *) &page ); | ||
402 | } | ||
403 | } | ||
404 | SSL_shutdown( ssl ); | ||
405 | SSL_free( ssl ); | ||
406 | } else { | ||
407 | #endif | ||
408 | /* HTTP implementation */ | ||
409 | result = http_request( sock, (struct pageref *) &page ); | ||
410 | #ifdef HAVE_SSL | ||
411 | } | ||
412 | #endif | ||
413 | /* stop timer and calculate elapsed_time */ | ||
414 | elapsed_time = delta_time( start_tv ); | ||
415 | |||
416 | /* close the tcp connection */ | ||
417 | close( sock ); | ||
418 | |||
419 | /* reset the alarm */ | ||
420 | alarm( 0 ); | ||
421 | |||
422 | /* analyze http page */ | ||
423 | /* TO DO */ | ||
424 | if ( result == STATE_OK ) | ||
425 | result = parse_http_response( (struct pageref *) &page ); | ||
426 | |||
427 | if ( result == STATE_OK ) | ||
428 | result = check_http_response( (struct pageref *) &page ); | ||
429 | |||
430 | switch ( result ) { | ||
431 | case STATE_OK: | ||
432 | /* weiter geht's */ | ||
433 | result = check_http_content( (struct pageref *) &page ); | ||
434 | break; | ||
435 | case STATE_DEPENDENT: | ||
436 | /* try to determine redirect parameters */ | ||
437 | result = prepare_follow_redirect( (struct pageref *) &page ); | ||
438 | break; | ||
439 | } | ||
440 | |||
441 | } else { | ||
442 | /* some error occured while trying to make a tcp connect */ | ||
443 | exit( result ); | ||
444 | } | ||
445 | |||
446 | } while ( result == STATE_DEPENDENT ); // end of onredirect loop | ||
447 | |||
448 | /* destroy SSL context */ | ||
449 | #ifdef HAVE_SSL | ||
450 | if ( use_ssl || ( http_redirect_state == STATE_DEPENDENT ) ) | ||
451 | destroy_ssl_ctx( ctx ); | ||
452 | #endif | ||
453 | |||
454 | /* if we ever get to this point, everything went fine */ | ||
455 | printf( RESULT_TEMPLATE_STATUS_RESPONSE_TIME, | ||
456 | protocol_text( use_ssl ), | ||
457 | state_text( result ), | ||
458 | page.status, | ||
459 | elapsed_time, | ||
460 | elapsed_time ); | ||
461 | |||
462 | return result; | ||
463 | } | ||
464 | |||
465 | |||
466 | void | ||
467 | print_help( void ) | ||
468 | { | ||
469 | print_revision( progname, REVISION ); | ||
470 | printf | ||
471 | ( "Copyright (c) %s %s <%s>\n\n%s\n", | ||
472 | COPYRIGHT, AUTHORS, EMAIL, HELP_TXT_SUMMARY ); | ||
473 | print_usage(); | ||
474 | printf( "NOTE: One or both of -H and -I must be specified\n" ); | ||
475 | printf( "\nOptions:\n" HELP_TXT_LONGOPTIONS "\n", | ||
476 | HTTP_PORT, DEFAULT_HTTP_URL_PATH, HTTPS_PORT, | ||
477 | DEFAULT_HTTP_EXPECT, DEFAULT_SOCKET_TIMEOUT ); | ||
478 | #ifdef HAVE_SSL | ||
479 | //printf( SSLDESCRIPTION ); | ||
480 | #endif | ||
481 | } | ||
482 | |||
483 | |||
484 | void | ||
485 | print_usage( void ) | ||
486 | { | ||
487 | printf( "Usage:\n" " %s %s\n" | ||
488 | #ifdef HAVE_GETOPT_H | ||
489 | " %s (-h | --help) for detailed help\n" | ||
490 | " %s (-V | --version) for version information\n", | ||
491 | #else | ||
492 | " %s -h for detailed help\n" | ||
493 | " %s -V for version information\n", | ||
494 | #endif | ||
495 | progname, HELP_TXT_OPTIONS, progname, progname ); | ||
496 | } | ||
497 | |||
498 | |||
499 | /* | ||
500 | * process_arguments() | ||
501 | * | ||
502 | * process command line arguments either using getopt_long or getopt | ||
503 | * (parsing long argumants manually) | ||
504 | */ | ||
505 | int | ||
506 | process_arguments( int argc, char **argv ) | ||
507 | { | ||
508 | int c, i = 1; | ||
509 | extern char *optarg; | ||
510 | |||
511 | #ifdef HAVE_GETOPT_H | ||
512 | int option_index = 0; | ||
513 | static struct option long_options[] = { | ||
514 | STD_LONG_OPTS, | ||
515 | {"file", required_argument, 0, 'F'}, | ||
516 | {"ip-address", required_argument, 0, 'I'}, | ||
517 | {"port", required_argument, 0, 'p'}, | ||
518 | {"url-path", required_argument, 0, 'u'}, | ||
519 | {"post-data", required_argument, 0, 'P'}, | ||
520 | {"ssl", no_argument, 0, 'S'}, | ||
521 | {"server-certificate-days", required_argument, 0, 'C'}, | ||
522 | {"basic-auth", required_argument, 0, 'a'}, | ||
523 | {"client-certificate", required_argument, 0, 'A'}, | ||
524 | {"passphrase", required_argument, 0, 'K'}, | ||
525 | {"ca-certificate", required_argument, 0, 'Z'}, | ||
526 | {"http-expect", required_argument, 0, 'e'}, | ||
527 | {"http-expect-only", required_argument, 0, 'E'}, | ||
528 | {"content-string", required_argument, 0, 's'}, | ||
529 | {"content-ereg-linespan", required_argument, 0, 'l'}, | ||
530 | {"content-ereg", required_argument, 0, 'r'}, | ||
531 | {"content-eregi", required_argument, 0, 'R'}, | ||
532 | {"onredirect", required_argument, 0, 'f'}, | ||
533 | {"onerror", required_argument, 0, 'g'}, | ||
534 | {"min", required_argument, 0, 'm'}, | ||
535 | {0, 0, 0, 0} | ||
536 | }; | ||
537 | #endif | ||
538 | |||
539 | |||
540 | /* convert commonly used arguments to their equivalent standard options */ | ||
541 | for (c = 1; c < argc; c++) { | ||
542 | if ( strcmp( "-to", argv[c]) == 0 ) | ||
543 | strcpy( argv[c], "-t" ); | ||
544 | if ( strcmp( "-hn", argv[c]) == 0 ) | ||
545 | strcpy( argv[c], "-H" ); | ||
546 | if ( strcmp( "-wt", argv[c]) == 0 ) | ||
547 | strcpy( argv[c], "-w" ); | ||
548 | if ( strcmp( "-ct", argv[c]) == 0 ) | ||
549 | strcpy( argv[c], "-c" ); | ||
550 | } | ||
551 | |||
552 | #define OPTCHARS "Vvht:c:w:H:F:I:p:u:P:SC:a:A:K:Z:e:E:s:r:R:f:g:lm:" | ||
553 | |||
554 | |||
555 | while (1) { | ||
556 | |||
557 | #ifdef HAVE_GETOPT_H | ||
558 | c = getopt_long( argc, argv, OPTCHARS, long_options, &option_index ); | ||
559 | #else | ||
560 | c = getopt( argc, argv, OPTCHARS ); | ||
561 | #endif | ||
562 | |||
563 | if ( ( c == -1 ) || ( c == EOF ) ) { | ||
564 | break; | ||
565 | } | ||
566 | |||
567 | switch (c) { | ||
568 | case '?': /* usage */ | ||
569 | usage2( "unknown argument", optarg ); | ||
570 | break; | ||
571 | |||
572 | /* Standard options */ | ||
573 | case 'h': /* help */ | ||
574 | print_help(); | ||
575 | exit( STATE_OK ); | ||
576 | break; | ||
577 | case 'V': /* version */ | ||
578 | print_revision( progname, REVISION ); | ||
579 | exit( STATE_OK ); | ||
580 | break; | ||
581 | case 'v': /* verbose */ | ||
582 | verbose = TRUE; | ||
583 | break; | ||
584 | case 't': /* timeout period */ | ||
585 | if ( !is_intnonneg( optarg ) ) | ||
586 | usage2( "timeout interval must be a non-negative integer", optarg ); | ||
587 | /* socket_timeout is defined in netutils.h and defaults to | ||
588 | * DEFAULT_SOCKET_TIMEOUT from common.h | ||
589 | */ | ||
590 | socket_timeout = atoi( optarg ); | ||
591 | break; | ||
592 | case 'c': /* critical time threshold */ | ||
593 | if ( !is_nonnegative( optarg ) ) | ||
594 | usage2( "invalid critical threshold", optarg ); | ||
595 | critical_interval = strtod( optarg, NULL ); | ||
596 | use_critical_interval = TRUE; | ||
597 | break; | ||
598 | case 'w': /* warning time threshold */ | ||
599 | if ( !is_nonnegative( optarg ) ) | ||
600 | usage2( "invalid warning threshold", optarg ); | ||
601 | warning_interval = strtod( optarg, NULL ); | ||
602 | use_warning_interval = TRUE; | ||
603 | break; | ||
604 | case 'H': /* Host Name (virtual host) */ | ||
605 | /* this rejects FQDNs, so we leave it for now... | ||
606 | *if ( !is_hostname( optarg ) ) | ||
607 | * usage2( "invalid hostname", optarg ); | ||
608 | */ | ||
609 | asprintf( &server_hostname, "%s", optarg ); | ||
610 | use_server_hostname = TRUE; | ||
611 | break; | ||
612 | case 'F': /* File (dummy) */ | ||
613 | break; | ||
614 | /* End of standard options */ | ||
615 | |||
616 | |||
617 | case 'I': /* Server IP-address or Hostname */ | ||
618 | /* this rejects FQDNs, so we leave it for now... | ||
619 | *if ( !is_host( optarg ) ) | ||
620 | * usage2( "invalid ip address or hostname", optarg ) | ||
621 | */ | ||
622 | asprintf( &server_host, "%s", optarg ); | ||
623 | break; | ||
624 | case 'p': /* Server port */ | ||
625 | if ( !is_intnonneg( optarg ) ) | ||
626 | usage2( "invalid port number", optarg ); | ||
627 | server_port = atoi( optarg ); | ||
628 | use_server_port = TRUE; | ||
629 | break; | ||
630 | case 'S': /* use SSL */ | ||
631 | #ifdef HAVE_SSL | ||
632 | use_ssl = TRUE; | ||
633 | if ( use_server_port == FALSE ) | ||
634 | server_port = HTTPS_PORT; | ||
635 | #else | ||
636 | usage( "check_http: invalid option - SSL is not available\n" ); | ||
637 | #endif | ||
638 | break; | ||
639 | case 'C': /* Server certificate warning time threshold */ | ||
640 | #ifdef HAVE_SSL | ||
641 | if ( !is_intnonneg( optarg ) ) | ||
642 | usage2( "invalid certificate expiration period", optarg ); | ||
643 | server_certificate_min_days_valid = atoi( optarg ); | ||
644 | check_server_certificate = TRUE; | ||
645 | #else | ||
646 | usage( "check_http: invalid option - SSL is not available\n" ); | ||
647 | #endif | ||
648 | break; | ||
649 | case 'a': /* basic authorization info */ | ||
650 | strncpy( basic_auth, optarg, MAX_INPUT_BUFFER - 1 ); | ||
651 | basic_auth[MAX_INPUT_BUFFER - 1] = 0; | ||
652 | use_basic_auth = TRUE; | ||
653 | break; | ||
654 | case 'A': /* client certificate */ | ||
655 | #ifdef HAVE_SSL | ||
656 | asprintf( &client_certificate_file, "%s", optarg ); | ||
657 | use_client_certificate = TRUE; | ||
658 | #else | ||
659 | usage( "check_http: invalid option - SSL is not available\n" ); | ||
660 | #endif | ||
661 | break; | ||
662 | case 'K': /* client certificate passphrase */ | ||
663 | #ifdef HAVE_SSL | ||
664 | asprintf( &client_certificate_passphrase, "%s", optarg ); | ||
665 | use_client_certificate_passphrase = TRUE; | ||
666 | #else | ||
667 | usage( "check_http: invalid option - SSL is not available\n" ); | ||
668 | #endif | ||
669 | case 'Z': /* valid CA certificates */ | ||
670 | #ifdef HAVE_SSL | ||
671 | asprintf( &ca_certificate_file, "%s", optarg ); | ||
672 | use_ca_certificate = TRUE; | ||
673 | #else | ||
674 | usage( "check_http: invalid option - SSL is not available\n" ); | ||
675 | #endif | ||
676 | break; | ||
677 | case 'u': /* URL PATH */ | ||
678 | asprintf( &http_url_path, "%s", optarg ); | ||
679 | break; | ||
680 | case 'P': /* POST DATA */ | ||
681 | asprintf( &http_post_data, "%s", optarg ); | ||
682 | use_http_post_data = TRUE; | ||
683 | asprintf( &http_method, "%s", "POST" ); | ||
684 | break; | ||
685 | case 'e': /* expected string in first line of HTTP response */ | ||
686 | strncpy( http_expect , optarg, MAX_INPUT_BUFFER - 1 ); | ||
687 | http_expect[MAX_INPUT_BUFFER - 1] = 0; | ||
688 | break; | ||
689 | case 'E': /* expected string in first line of HTTP response and process no other check*/ | ||
690 | strncpy( http_expect , optarg, MAX_INPUT_BUFFER - 1 ); | ||
691 | http_expect[MAX_INPUT_BUFFER - 1] = 0; | ||
692 | use_http_expect_only = TRUE; | ||
693 | break; | ||
694 | case 's': /* expected (sub-)string in content */ | ||
695 | strncpy( content_string , optarg, MAX_INPUT_BUFFER - 1 ); | ||
696 | content_string[MAX_INPUT_BUFFER - 1] = 0; | ||
697 | check_content_string = TRUE; | ||
698 | break; | ||
699 | case 'l': /* regex linespan */ | ||
700 | #ifdef HAVE_REGEX_H | ||
701 | regex_cflags &= ~REG_NEWLINE; | ||
702 | #else | ||
703 | usage( "check_http: call for regex which was not a compiled option\n" ); | ||
704 | #endif | ||
705 | break; | ||
706 | case 'R': /* expected case insensitive regular expression in content */ | ||
707 | #ifdef HAVE_REGEX_H | ||
708 | regex_cflags |= REG_ICASE; | ||
709 | #else | ||
710 | usage( "check_http: call for regex which was not a compiled option\n" ); | ||
711 | #endif | ||
712 | case 'r': /* expected regular expression in content */ | ||
713 | #ifdef HAVE_REGEX_H | ||
714 | strncpy( content_regex , optarg, MAX_REGEX_SIZE - 1 ); | ||
715 | content_regex[MAX_REGEX_SIZE - 1] = 0; | ||
716 | check_content_regex = TRUE; | ||
717 | regex_error = regcomp( ®ex_preg, content_regex, regex_cflags ); | ||
718 | if ( regex_error != 0 ) { | ||
719 | regerror( regex_error, ®ex_preg, regex_error_buffer, MAX_INPUT_BUFFER ); | ||
720 | printf( "Could Not Compile Regular Expression: %s", regex_error_buffer ); | ||
721 | return ERROR; | ||
722 | } | ||
723 | #else | ||
724 | usage( "check_http: call for regex which was not a compiled option\n" ); | ||
725 | #endif | ||
726 | break; | ||
727 | case 'f': /* onredirect (3xx errors) */ | ||
728 | if ( !strcmp( optarg, "follow" ) ) | ||
729 | http_redirect_state = STATE_DEPENDENT; | ||
730 | if ( !strcmp( optarg, "unknown" ) ) | ||
731 | http_redirect_state = STATE_UNKNOWN; | ||
732 | if ( !strcmp( optarg, "ok" ) ) | ||
733 | http_redirect_state = STATE_OK; | ||
734 | if ( !strcmp( optarg, "warning" ) ) | ||
735 | http_redirect_state = STATE_WARNING; | ||
736 | if ( !strcmp( optarg, "critical" ) ) | ||
737 | http_redirect_state = STATE_CRITICAL; | ||
738 | break; | ||
739 | case 'g': /* onerror (4xx errors) */ | ||
740 | if ( !strcmp( optarg, "unknown" ) ) | ||
741 | http_client_error_state = STATE_UNKNOWN; | ||
742 | if ( !strcmp( optarg, "ok" ) ) | ||
743 | http_client_error_state = STATE_OK; | ||
744 | if ( !strcmp( optarg, "warning" ) ) | ||
745 | http_client_error_state = STATE_WARNING; | ||
746 | if ( !strcmp( optarg, "critical" ) ) | ||
747 | http_client_error_state = STATE_CRITICAL; | ||
748 | break; | ||
749 | case 'm': /* min */ | ||
750 | if ( !is_intnonneg( optarg ) ) | ||
751 | usage2( "invalid page size", optarg ); | ||
752 | min_content_length = atoi( optarg ); | ||
753 | use_min_content_length = TRUE; | ||
754 | break; | ||
755 | } // end switch | ||
756 | } // end while(1) | ||
757 | |||
758 | c = optind; | ||
759 | |||
760 | |||
761 | /* Sanity checks on supplied command line arguments */ | ||
762 | |||
763 | /* 1. if both host and hostname are not defined, try to | ||
764 | * fetch one more argument which is possibly supplied | ||
765 | * without an option | ||
766 | */ | ||
767 | if ( ( strcmp( server_host, "" ) ) && (c < argc) ) { | ||
768 | asprintf( &server_host, "%s", argv[c++] ); | ||
769 | } | ||
770 | |||
771 | /* 2. check if another artument is supplied | ||
772 | */ | ||
773 | if ( ( strcmp( server_hostname, "" ) == 0 ) && (c < argc) ) { | ||
774 | asprintf( &server_hostname, "%s", argv[c++] ); | ||
775 | } | ||
776 | |||
777 | /* 3. if host is still not defined, just copy hostname, | ||
778 | * which is then guaranteed to be defined by now | ||
779 | */ | ||
780 | if ( strcmp( server_host, "") == 0 ) { | ||
781 | if ( strcmp( server_hostname, "" ) == 0 ) { | ||
782 | usage ("check_http: you must specify a server address or host name\n"); | ||
783 | } else { | ||
784 | asprintf( &server_host, "%s", server_hostname ); | ||
785 | } | ||
786 | } | ||
787 | |||
788 | /* 4. check if content checks for a string and a regex | ||
789 | * are requested for only one of both is possible at | ||
790 | * a time | ||
791 | */ | ||
792 | if ( check_content_string && check_content_regex ) | ||
793 | usage( "check_http: you can only check for string OR regex at a time\n" ); | ||
794 | |||
795 | /* 5. check for options which require use_ssl */ | ||
796 | if ( check_server_certificate && !use_ssl ) | ||
797 | usage( "check_http: you must use -S to check server certificate\n" ); | ||
798 | if ( use_client_certificate && !use_ssl ) | ||
799 | usage( "check_http: you must use -S to authenticate with a client certificate\n" ); | ||
800 | if ( use_ca_certificate && !use_ssl ) | ||
801 | usage( "check_http: you must use -S to check server certificate against CA certificates\n" ); | ||
802 | |||
803 | /* 6. check for passphrase without client certificate */ | ||
804 | if ( use_client_certificate_passphrase && !use_client_certificate ) | ||
805 | usage( "check_http: you must supply a client certificate to use a passphrase\n" ); | ||
806 | |||
807 | |||
808 | /* Finally set some default values if necessary */ | ||
809 | if ( strcmp( http_method, "" ) == 0 ) | ||
810 | asprintf( &http_method, "%s", DEFAULT_HTTP_METHOD ); | ||
811 | if ( strcmp( http_url_path, "" ) == 0 ) { | ||
812 | asprintf( &http_url_path, "%s", DEFAULT_HTTP_URL_PATH ); | ||
813 | } | ||
814 | |||
815 | return TRUE; | ||
816 | } | ||
817 | |||
818 | |||
819 | int | ||
820 | http_request( int sock, struct pageref *page ) | ||
821 | { | ||
822 | char *buffer = ""; | ||
823 | char recvbuff[MAX_INPUT_BUFFER] = ""; | ||
824 | int buffer_len = 0; | ||
825 | int content_len = 0; | ||
826 | size_t sendsize = 0; | ||
827 | size_t recvsize = 0; | ||
828 | char *content = ""; | ||
829 | size_t size = 0; | ||
830 | char *basic_auth_encoded = NULL; | ||
831 | |||
832 | asprintf( &buffer, HTTP_TEMPLATE_REQUEST, buffer, http_method, http_url_path ); | ||
833 | |||
834 | asprintf( &buffer, HTTP_TEMPLATE_HEADER_USERAGENT, buffer, progname, REVISION, PACKAGE_VERSION ); | ||
835 | |||
836 | if ( use_server_hostname ) { | ||
837 | asprintf( &buffer, HTTP_TEMPLATE_HEADER_HOST, buffer, server_hostname ); | ||
838 | } | ||
839 | |||
840 | if ( use_basic_auth ) { | ||
841 | basic_auth_encoded = base64( basic_auth, strlen( basic_auth ) ); | ||
842 | asprintf( &buffer, HTTP_TEMPLATE_HEADER_AUTH, buffer, basic_auth_encoded ); | ||
843 | } | ||
844 | |||
845 | /* either send http POST data */ | ||
846 | if ( use_http_post_data ) { | ||
847 | /* based on code written by Chris Henesy <lurker@shadowtech.org> */ | ||
848 | asprintf( &buffer, "Content-Type: application/x-www-form-urlencoded\r\n" ); | ||
849 | asprintf( &buffer, "Content-Length: %i\r\n\r\n", buffer, content_len ); | ||
850 | asprintf( &buffer, "%s%s%s", buffer, http_post_data, "\r\n" ); | ||
851 | sendsize = send( sock, buffer, strlen( buffer ), 0 ); | ||
852 | if ( sendsize < strlen( buffer ) ) { | ||
853 | printf( "ERROR: Incomplete write\n" ); | ||
854 | return STATE_CRITICAL; | ||
855 | } | ||
856 | /* or just a newline */ | ||
857 | } else { | ||
858 | asprintf( &buffer, "%s%s", buffer, "\r\n" ); | ||
859 | sendsize = send( sock, buffer, strlen( buffer ) , 0 ); | ||
860 | if ( sendsize < strlen( buffer ) ) { | ||
861 | printf( "ERROR: Incomplete write\n" ); | ||
862 | return STATE_CRITICAL; | ||
863 | } | ||
864 | } | ||
865 | |||
866 | |||
867 | /* read server's response */ | ||
868 | |||
869 | do { | ||
870 | recvsize = recv( sock, recvbuff, MAX_INPUT_BUFFER - 1, 0 ); | ||
871 | if ( recvsize > (size_t) 0 ) { | ||
872 | recvbuff[recvsize] = '\0'; | ||
873 | asprintf( &content, "%s%s", content, recvbuff ); | ||
874 | size += recvsize; | ||
875 | } | ||
876 | } while ( recvsize > (size_t) 0 ); | ||
877 | |||
878 | asprintf( &page->content, "%s", content ); | ||
879 | page->size = size; | ||
880 | |||
881 | /* return a CRITICAL status if we couldn't read any data */ | ||
882 | if ( size == (size_t) 0) | ||
883 | ssl_terminate( STATE_CRITICAL, "No data received" ); | ||
884 | |||
885 | return STATE_OK; | ||
886 | } | ||
887 | |||
888 | |||
889 | int | ||
890 | parse_http_response( struct pageref *page ) | ||
891 | { | ||
892 | char *content = ""; //local copy of struct member | ||
893 | char *status = ""; //local copy of struct member | ||
894 | char *header = ""; //local copy of struct member | ||
895 | size_t len = 0; //temporary used | ||
896 | char *pos = ""; //temporary used | ||
897 | |||
898 | asprintf( &content, "%s", page->content ); | ||
899 | |||
900 | /* find status line and null-terminate it */ | ||
901 | // copy content to status | ||
902 | status = content; | ||
903 | |||
904 | // find end of status line and copy pointer to pos | ||
905 | content += (size_t) strcspn( content, "\r\n" ); | ||
906 | pos = content; | ||
907 | |||
908 | // advance content pointer behind the newline of status line | ||
909 | content += (size_t) strspn( content, "\r\n" ); | ||
910 | |||
911 | // null-terminate status line at pos | ||
912 | status[strcspn( status, "\r\n")] = 0; | ||
913 | strip( status ); | ||
914 | |||
915 | // copy final status to struct member | ||
916 | page->status = status; | ||
917 | |||
918 | |||
919 | /* find header and null-terminate it */ | ||
920 | // copy remaining content to header | ||
921 | header = content; | ||
922 | |||
923 | // loop until line containing only newline is found (end of header) | ||
924 | while ( strcspn( content, "\r\n" ) > 0 ) { | ||
925 | //find end of line and copy pointer to pos | ||
926 | content += (size_t) strcspn( content, "\r\n" ); | ||
927 | pos = content; | ||
928 | |||
929 | if ( ( strspn( content, "\r" ) == 1 && strspn( content, "\r\n" ) >= 2 ) || | ||
930 | ( strspn( content, "\n" ) == 1 && strspn( content, "\r\n" ) >= 2 ) ) | ||
931 | content += (size_t) 2; | ||
932 | else | ||
933 | content += (size_t) 1; | ||
934 | } | ||
935 | // advance content pointer behind the newline | ||
936 | content += (size_t) strspn( content, "\r\n" ); | ||
937 | |||
938 | // null-terminate header at pos | ||
939 | header[pos - header] = 0; | ||
940 | |||
941 | // copy final header to struct member | ||
942 | page->header = header; | ||
943 | |||
944 | |||
945 | // copy remaining content to body | ||
946 | page->body = content; | ||
947 | |||
948 | if ( verbose ) { | ||
949 | printf( "STATUS: %s\n", page->status ); | ||
950 | printf( "HEADER: \n%s\n", page->header ); | ||
951 | printf( "BODY: \n%s\n", page->body ); | ||
952 | } | ||
953 | |||
954 | return STATE_OK; | ||
955 | } | ||
956 | |||
957 | |||
958 | int | ||
959 | check_http_response( struct pageref *page ) | ||
960 | { | ||
961 | char *msg = ""; | ||
962 | |||
963 | /* check response time befor anything else */ | ||
964 | if ( use_critical_interval && ( elapsed_time > critical_interval ) ) { | ||
965 | asprintf( &msg, RESULT_TEMPLATE_RESPONSE_TIME, | ||
966 | protocol_text( use_ssl ), | ||
967 | state_text( STATE_CRITICAL ), | ||
968 | elapsed_time, | ||
969 | elapsed_time ); | ||
970 | terminate( STATE_CRITICAL, msg ); | ||
971 | } | ||
972 | if ( use_warning_interval && ( elapsed_time > warning_interval ) ) { | ||
973 | asprintf( &msg, RESULT_TEMPLATE_RESPONSE_TIME, | ||
974 | protocol_text( use_ssl ), | ||
975 | state_text( STATE_WARNING ), | ||
976 | elapsed_time, | ||
977 | elapsed_time ); | ||
978 | terminate( STATE_WARNING, msg ); | ||
979 | } | ||
980 | |||
981 | |||
982 | /* make sure the status line matches the response we are looking for */ | ||
983 | if ( strstr( page->status, http_expect ) ) { | ||
984 | /* The result is only checked against the expected HTTP status line, | ||
985 | so exit immediately after this check */ | ||
986 | if ( use_http_expect_only ) { | ||
987 | if ( ( server_port == HTTP_PORT ) | ||
988 | #ifdef HAVE_SSL | ||
989 | || ( server_port == HTTPS_PORT ) ) | ||
990 | #else | ||
991 | ) | ||
992 | #endif | ||
993 | asprintf( &msg, "Expected HTTP response received from host\n" ); | ||
994 | else | ||
995 | asprintf( &msg, "Expected HTTP response received from host on port %d\n", server_port ); | ||
996 | terminate( STATE_OK, msg ); | ||
997 | } | ||
998 | } else { | ||
999 | if ( ( server_port == HTTP_PORT ) | ||
1000 | #ifdef HAVE_SSL | ||
1001 | || ( server_port == HTTPS_PORT ) ) | ||
1002 | #else | ||
1003 | ) | ||
1004 | #endif | ||
1005 | asprintf( &msg, "Invalid HTTP response received from host\n" ); | ||
1006 | else | ||
1007 | asprintf( &msg, "Invalid HTTP response received from host on port %d\n", server_port ); | ||
1008 | terminate( STATE_CRITICAL, msg ); | ||
1009 | } | ||
1010 | |||
1011 | /* check the return code */ | ||
1012 | /* server errors result in a critical state */ | ||
1013 | if ( strstr( page->status, "500" ) || | ||
1014 | strstr( page->status, "501" ) || | ||
1015 | strstr( page->status, "502" ) || | ||
1016 | strstr( page->status, "503" ) || | ||
1017 | strstr( page->status, "504" ) || | ||
1018 | strstr( page->status, "505" )) { | ||
1019 | asprintf( &msg, RESULT_TEMPLATE_STATUS_RESPONSE_TIME, | ||
1020 | protocol_text( use_ssl ), | ||
1021 | state_text( http_client_error_state ), | ||
1022 | page->status, | ||
1023 | elapsed_time, | ||
1024 | elapsed_time ); | ||
1025 | terminate( STATE_CRITICAL, msg ); | ||
1026 | } | ||
1027 | |||
1028 | /* client errors result in a warning state */ | ||
1029 | if ( strstr( page->status, "400" ) || | ||
1030 | strstr( page->status, "401" ) || | ||
1031 | strstr( page->status, "402" ) || | ||
1032 | strstr( page->status, "403" ) || | ||
1033 | strstr( page->status, "404" ) || | ||
1034 | strstr( page->status, "405" ) || | ||
1035 | strstr( page->status, "406" ) || | ||
1036 | strstr( page->status, "407" ) || | ||
1037 | strstr( page->status, "408" ) || | ||
1038 | strstr( page->status, "409" ) || | ||
1039 | strstr( page->status, "410" ) || | ||
1040 | strstr( page->status, "411" ) || | ||
1041 | strstr( page->status, "412" ) || | ||
1042 | strstr( page->status, "413" ) || | ||
1043 | strstr( page->status, "414" ) || | ||
1044 | strstr( page->status, "415" ) || | ||
1045 | strstr( page->status, "416" ) || | ||
1046 | strstr( page->status, "417" ) ) { | ||
1047 | asprintf( &msg, RESULT_TEMPLATE_STATUS_RESPONSE_TIME, | ||
1048 | protocol_text( use_ssl ), | ||
1049 | state_text( http_client_error_state ), | ||
1050 | page->status, | ||
1051 | elapsed_time, | ||
1052 | elapsed_time ); | ||
1053 | terminate( http_client_error_state, msg ); | ||
1054 | } | ||
1055 | |||
1056 | /* check redirected page if specified */ | ||
1057 | if (strstr( page->status, "300" ) || | ||
1058 | strstr( page->status, "301" ) || | ||
1059 | strstr( page->status, "302" ) || | ||
1060 | strstr( page->status, "303" ) || | ||
1061 | strstr( page->status, "304" ) || | ||
1062 | strstr( page->status, "305" ) || | ||
1063 | strstr( page->status, "306" ) || | ||
1064 | strstr( page->status, "307" ) ) { | ||
1065 | if ( http_redirect_state == STATE_DEPENDENT ) { | ||
1066 | /* returning STATE_DEPENDENT means follow redirect */ | ||
1067 | return STATE_DEPENDENT; | ||
1068 | } else { | ||
1069 | asprintf( &msg, RESULT_TEMPLATE_STATUS_RESPONSE_TIME, | ||
1070 | protocol_text( use_ssl ), | ||
1071 | state_text( http_redirect_state ), | ||
1072 | page->status, | ||
1073 | elapsed_time, | ||
1074 | elapsed_time ); | ||
1075 | terminate( http_redirect_state, msg ); | ||
1076 | } | ||
1077 | } | ||
1078 | |||
1079 | return STATE_OK; | ||
1080 | } | ||
1081 | |||
1082 | int | ||
1083 | check_http_content( struct pageref *page ) | ||
1084 | { | ||
1085 | char *msg = ""; | ||
1086 | |||
1087 | /* check for string in content */ | ||
1088 | if ( check_content_string ) { | ||
1089 | if ( strstr( page->content, content_string ) ) { | ||
1090 | asprintf( &msg, RESULT_TEMPLATE_STATUS_RESPONSE_TIME, | ||
1091 | protocol_text( use_ssl ), | ||
1092 | state_text( STATE_OK ), | ||
1093 | page->status, | ||
1094 | elapsed_time, | ||
1095 | elapsed_time ); | ||
1096 | terminate( STATE_OK, msg ); | ||
1097 | } else { | ||
1098 | asprintf( &msg, RESULT_TEMPLATE_STATUS_RESPONSE_TIME, | ||
1099 | protocol_text( use_ssl ), | ||
1100 | state_text( STATE_CRITICAL ), | ||
1101 | page->status, | ||
1102 | elapsed_time, | ||
1103 | elapsed_time ); | ||
1104 | terminate( STATE_CRITICAL, msg ); | ||
1105 | } | ||
1106 | } | ||
1107 | |||
1108 | #ifdef HAVE_REGEX_H | ||
1109 | /* check for regex in content */ | ||
1110 | if ( check_content_regex ) { | ||
1111 | regex_error = regexec( ®ex_preg, page->content, REGEX_REGS, regex_pmatch, 0); | ||
1112 | if ( regex_error == 0 ) { | ||
1113 | asprintf( &msg, RESULT_TEMPLATE_STATUS_RESPONSE_TIME, | ||
1114 | protocol_text( use_ssl ), | ||
1115 | state_text( STATE_OK ), | ||
1116 | page->status, | ||
1117 | elapsed_time, | ||
1118 | elapsed_time ); | ||
1119 | terminate( STATE_OK, msg ); | ||
1120 | } else { | ||
1121 | if ( regex_error == REG_NOMATCH ) { | ||
1122 | asprintf( &msg, "%s, %s: regex pattern not found\n", | ||
1123 | protocol_text( use_ssl) , | ||
1124 | state_text( STATE_CRITICAL ) ); | ||
1125 | terminate( STATE_CRITICAL, msg ); | ||
1126 | } else { | ||
1127 | regerror( regex_error, ®ex_preg, regex_error_buffer, MAX_INPUT_BUFFER); | ||
1128 | asprintf( &msg, "%s %s: Regex execute Error: %s\n", | ||
1129 | protocol_text( use_ssl) , | ||
1130 | state_text( STATE_CRITICAL ), | ||
1131 | regex_error_buffer ); | ||
1132 | terminate( STATE_CRITICAL, msg ); | ||
1133 | } | ||
1134 | } | ||
1135 | } | ||
1136 | #endif | ||
1137 | |||
1138 | return STATE_OK; | ||
1139 | } | ||
1140 | |||
1141 | |||
1142 | int | ||
1143 | prepare_follow_redirect( struct pageref *page ) | ||
1144 | { | ||
1145 | char *header = NULL; | ||
1146 | char *msg = ""; | ||
1147 | char protocol[6]; | ||
1148 | char hostname[MAX_IPV4_HOSTLENGTH]; | ||
1149 | char port[6]; | ||
1150 | char *url_path = NULL; | ||
1151 | char *orig_url_path = NULL; | ||
1152 | char *orig_url_dirname = NULL; | ||
1153 | size_t len = 0; | ||
1154 | |||
1155 | asprintf( &header, "%s", page->header ); | ||
1156 | |||
1157 | |||
1158 | /* restore some default values */ | ||
1159 | use_http_post_data = FALSE; | ||
1160 | asprintf( &http_method, "%s", DEFAULT_HTTP_METHOD ); | ||
1161 | |||
1162 | /* copy url of original request, maybe we need it to compose | ||
1163 | absolute url from relative Location: header */ | ||
1164 | asprintf( &orig_url_path, "%s", http_url_path ); | ||
1165 | |||
1166 | while ( strcspn( header, "\r\n" ) > (size_t) 0 ) { | ||
1167 | url_path = realloc( url_path, (size_t) strcspn( header, "\r\n" ) ); | ||
1168 | if ( url_path == NULL ) | ||
1169 | terminate( STATE_UNKNOWN, "HTTP UNKNOWN: could not reallocate url_path" ); | ||
1170 | |||
1171 | |||
1172 | /* Try to find a Location header combination of METHOD HOSTNAME PORT and PATH */ | ||
1173 | /* 1. scan for Location: http[s]://hostname:port/path */ | ||
1174 | if ( sscanf ( header, HTTP_HEADER_LOCATION_MATCH HTTP_HEADER_PROTOCOL_MATCH HTTP_HEADER_HOSTNAME_MATCH HTTP_HEADER_PORT_MATCH HTTP_HEADER_URL_PATH_MATCH, &protocol, &hostname, &port, url_path ) == 4 ) { | ||
1175 | asprintf( &server_hostname, "%s", hostname ); | ||
1176 | asprintf( &server_host, "%s", hostname ); | ||
1177 | use_ssl = chk_protocol(protocol); | ||
1178 | server_port = atoi( port ); | ||
1179 | asprintf( &http_url_path, "%s", url_path ); | ||
1180 | return STATE_DEPENDENT; | ||
1181 | } | ||
1182 | else if ( sscanf ( header, HTTP_HEADER_LOCATION_MATCH HTTP_HEADER_PROTOCOL_MATCH HTTP_HEADER_HOSTNAME_MATCH HTTP_HEADER_URL_PATH_MATCH, &protocol, &hostname, url_path ) == 3) { | ||
1183 | asprintf( &server_hostname, "%s", hostname ); | ||
1184 | asprintf( &server_host, "%s", hostname ); | ||
1185 | use_ssl = chk_protocol(protocol); | ||
1186 | server_port = protocol_std_port(use_ssl); | ||
1187 | asprintf( &http_url_path, "%s", url_path ); | ||
1188 | return STATE_DEPENDENT; | ||
1189 | } | ||
1190 | else if ( sscanf ( header, HTTP_HEADER_LOCATION_MATCH HTTP_HEADER_PROTOCOL_MATCH HTTP_HEADER_HOSTNAME_MATCH HTTP_HEADER_PORT_MATCH, &protocol, &hostname, &port ) == 3) { | ||
1191 | asprintf( &server_hostname, "%s", hostname ); | ||
1192 | asprintf( &server_host, "%s", hostname ); | ||
1193 | use_ssl = chk_protocol(protocol); | ||
1194 | server_port = atoi( port ); | ||
1195 | asprintf( &http_url_path, "%s", DEFAULT_HTTP_URL_PATH ); | ||
1196 | return STATE_DEPENDENT; | ||
1197 | } | ||
1198 | else if ( sscanf ( header, HTTP_HEADER_LOCATION_MATCH HTTP_HEADER_PROTOCOL_MATCH HTTP_HEADER_HOSTNAME_MATCH, protocol, hostname ) == 2 ) { | ||
1199 | asprintf( &server_hostname, "%s", hostname ); | ||
1200 | asprintf( &server_host, "%s", hostname ); | ||
1201 | use_ssl = chk_protocol(protocol); | ||
1202 | server_port = protocol_std_port(use_ssl); | ||
1203 | asprintf( &http_url_path, "%s", DEFAULT_HTTP_URL_PATH ); | ||
1204 | } | ||
1205 | else if ( sscanf ( header, HTTP_HEADER_LOCATION_MATCH HTTP_HEADER_URL_PATH_MATCH, url_path ) == 1 ) { | ||
1206 | /* check for relative url and prepend path if necessary */ | ||
1207 | if ( ( url_path[0] != '/' ) && ( orig_url_dirname = strrchr( orig_url_path, '/' ) ) ) { | ||
1208 | *orig_url_dirname = '\0'; | ||
1209 | asprintf( &http_url_path, "%s%s", orig_url_path, url_path ); | ||
1210 | } else { | ||
1211 | asprintf( &http_url_path, "%s", url_path ); | ||
1212 | } | ||
1213 | return STATE_DEPENDENT; | ||
1214 | } | ||
1215 | header += (size_t) strcspn( header, "\r\n" ); | ||
1216 | header += (size_t) strspn( header, "\r\n" ); | ||
1217 | } /* end while (header) */ | ||
1218 | |||
1219 | |||
1220 | /* default return value is STATE_DEPENDENT to continue looping in main() */ | ||
1221 | asprintf( &msg, "% %: % - Could not find redirect Location", | ||
1222 | protocol_text( use_ssl ), | ||
1223 | state_text( STATE_UNKNOWN ), | ||
1224 | page->status ); | ||
1225 | terminate( STATE_UNKNOWN, msg ); | ||
1226 | } | ||
1227 | |||
1228 | #ifdef HAVE_SSL | ||
1229 | int | ||
1230 | https_request( SSL_CTX *ctx, SSL *ssl, struct pageref *page ) | ||
1231 | { | ||
1232 | char *buffer = ""; | ||
1233 | char recvbuff[MAX_INPUT_BUFFER] = ""; | ||
1234 | int buffer_len = 0; | ||
1235 | int content_len = 0; | ||
1236 | size_t sendsize = 0; | ||
1237 | size_t recvsize = 0; | ||
1238 | char *content = ""; | ||
1239 | size_t size = 0; | ||
1240 | char *basic_auth_encoded = NULL; | ||
1241 | |||
1242 | asprintf( &buffer, HTTP_TEMPLATE_REQUEST, buffer, http_method, http_url_path ); | ||
1243 | |||
1244 | asprintf( &buffer, HTTP_TEMPLATE_HEADER_USERAGENT, buffer, progname, REVISION, PACKAGE_VERSION ); | ||
1245 | |||
1246 | if ( use_server_hostname ) { | ||
1247 | asprintf( &buffer, HTTP_TEMPLATE_HEADER_HOST, buffer, server_hostname ); | ||
1248 | } | ||
1249 | |||
1250 | if ( use_basic_auth ) { | ||
1251 | basic_auth_encoded = base64( basic_auth, strlen( basic_auth ) ); | ||
1252 | asprintf( &buffer, HTTP_TEMPLATE_HEADER_AUTH, buffer, basic_auth_encoded ); | ||
1253 | } | ||
1254 | |||
1255 | /* either send http POST data */ | ||
1256 | if ( use_http_post_data ) { | ||
1257 | asprintf( &buffer, "%sContent-Type: application/x-www-form-urlencoded\r\n", buffer ); | ||
1258 | asprintf( &buffer, "%sContent-Length: %i\r\n\r\n", buffer, content_len ); | ||
1259 | asprintf( &buffer, "%s%s%s", buffer, http_post_data, "\r\n" ); | ||
1260 | sendsize = SSL_write( ssl, buffer, strlen( buffer ) ); | ||
1261 | switch ( SSL_get_error( ssl, sendsize ) ) { | ||
1262 | case SSL_ERROR_NONE: | ||
1263 | if ( sendsize < strlen( buffer ) ) | ||
1264 | ssl_terminate( STATE_CRITICAL, "SSL ERROR: Incomplete write.\n" ); | ||
1265 | break; | ||
1266 | default: | ||
1267 | ssl_terminate( STATE_CRITICAL, "SSL ERROR: Write problem.\n" ); | ||
1268 | break; | ||
1269 | } | ||
1270 | /* or just a newline */ | ||
1271 | } else { | ||
1272 | |||
1273 | asprintf( &buffer, "%s\r\n", buffer ); | ||
1274 | sendsize = SSL_write( ssl, buffer, strlen( buffer ) ); | ||
1275 | switch ( SSL_get_error( ssl, sendsize ) ) { | ||
1276 | case SSL_ERROR_NONE: | ||
1277 | if ( sendsize < strlen( buffer ) ) | ||
1278 | ssl_terminate( STATE_CRITICAL, "SSL ERROR: Incomplete write.\n" ); | ||
1279 | break; | ||
1280 | default: | ||
1281 | ssl_terminate( STATE_CRITICAL, "SSL ERROR: Write problem.\n" ); | ||
1282 | break; | ||
1283 | } | ||
1284 | } | ||
1285 | |||
1286 | |||
1287 | /* read server's response */ | ||
1288 | |||
1289 | do { | ||
1290 | recvsize = SSL_read( ssl, recvbuff, MAX_INPUT_BUFFER - 1 ); | ||
1291 | |||
1292 | switch ( SSL_get_error( ssl, recvsize ) ) { | ||
1293 | case SSL_ERROR_NONE: | ||
1294 | if ( recvsize > (size_t) 0 ) { | ||
1295 | recvbuff[recvsize] = '\0'; | ||
1296 | asprintf( &content, "%s%s", content, recvbuff ); | ||
1297 | size += recvsize; | ||
1298 | } | ||
1299 | break; | ||
1300 | case SSL_ERROR_WANT_READ: | ||
1301 | if ( use_client_certificate ) { | ||
1302 | continue; | ||
1303 | } else { | ||
1304 | // workaround while we don't have anonymous client certificates: return OK | ||
1305 | //ssl_terminate( STATE_WARNING, "HTTPS WARNING - Client Certificate required.\n" ); | ||
1306 | ssl_terminate( STATE_OK, "HTTPS WARNING - Client Certificate required.\n" ); | ||
1307 | } | ||
1308 | break; | ||
1309 | case SSL_ERROR_ZERO_RETURN: | ||
1310 | break; | ||
1311 | case SSL_ERROR_SYSCALL: | ||
1312 | ssl_terminate( STATE_CRITICAL, "SSL ERROR: Premature close.\n" ); | ||
1313 | break; | ||
1314 | default: | ||
1315 | ssl_terminate( STATE_CRITICAL, "SSL ERROR: Read problem.\n" ); | ||
1316 | break; | ||
1317 | } | ||
1318 | } while ( recvsize > (size_t) 0 ); | ||
1319 | |||
1320 | asprintf( &page->content, "%s", content ); | ||
1321 | page->size = size; | ||
1322 | |||
1323 | /* return a CRITICAL status if we couldn't read any data */ | ||
1324 | if ( size == (size_t) 0) | ||
1325 | ssl_terminate( STATE_CRITICAL, "No data received" ); | ||
1326 | |||
1327 | return STATE_OK; | ||
1328 | } | ||
1329 | #endif | ||
1330 | |||
1331 | |||
1332 | #ifdef HAVE_SSL | ||
1333 | int | ||
1334 | ssl_terminate(int state, char *string ) { | ||
1335 | ERR_print_errors( bio_err ); | ||
1336 | terminate( state, string ); | ||
1337 | } | ||
1338 | #endif | ||
1339 | |||
1340 | #ifdef HAVE_SSL | ||
1341 | static int | ||
1342 | password_cb( char *buf, int num, int rwflag, void *userdata ) | ||
1343 | { | ||
1344 | if ( num < strlen( client_certificate_passphrase ) + 1 ) | ||
1345 | return( 0 ); | ||
1346 | |||
1347 | strcpy( buf, client_certificate_passphrase ); | ||
1348 | return( strlen( client_certificate_passphrase ) ); | ||
1349 | } | ||
1350 | #endif | ||
1351 | |||
1352 | #ifdef HAVE_SSL | ||
1353 | static void | ||
1354 | sigpipe_handle( int x ) { | ||
1355 | } | ||
1356 | #endif | ||
1357 | |||
1358 | #ifdef HAVE_SSL | ||
1359 | SSL_CTX * | ||
1360 | initialize_ssl_ctx( void ) | ||
1361 | { | ||
1362 | SSL_METHOD *meth; | ||
1363 | SSL_CTX *ctx; | ||
1364 | |||
1365 | if ( !bio_err ) { | ||
1366 | /* Global system initialization */ | ||
1367 | SSL_library_init(); | ||
1368 | SSL_load_error_strings(); | ||
1369 | |||
1370 | /* An error write context */ | ||
1371 | bio_err=BIO_new_fp( stderr, BIO_NOCLOSE ); | ||
1372 | } | ||
1373 | |||
1374 | /* set up as SIGPIPE handler */ | ||
1375 | signal( SIGPIPE, sigpipe_handle ); | ||
1376 | |||
1377 | /* create our context */ | ||
1378 | meth=SSLv3_method(); | ||
1379 | ctx=SSL_CTX_new( meth ); | ||
1380 | |||
1381 | /* load client certificate and key */ | ||
1382 | if ( use_client_certificate ) { | ||
1383 | if ( !(SSL_CTX_use_certificate_chain_file( ctx, client_certificate_file )) ) | ||
1384 | ssl_terminate( STATE_CRITICAL, "check_http: can't read client certificate file" ); | ||
1385 | |||
1386 | /* set client certificate key passphrase */ | ||
1387 | if ( use_client_certificate_passphrase ) { | ||
1388 | SSL_CTX_set_default_passwd_cb( ctx, password_cb ); | ||
1389 | } | ||
1390 | |||
1391 | if ( !(SSL_CTX_use_PrivateKey_file( ctx, client_certificate_file, SSL_FILETYPE_PEM )) ) | ||
1392 | ssl_terminate( STATE_CRITICAL, "check_http: can't read client certificate key file" ); | ||
1393 | } | ||
1394 | |||
1395 | /* load the CAs we trust */ | ||
1396 | if ( use_ca_certificate ) { | ||
1397 | if ( !(SSL_CTX_load_verify_locations( ctx, ca_certificate_file, 0 )) ) | ||
1398 | ssl_terminate( STATE_CRITICAL, "check_http: can't read CA certificate file" ); | ||
1399 | |||
1400 | #if (OPENSSL_VERSION_NUMBER < 0x00905100L) | ||
1401 | SSL_CTX_set_verify_depth( ctx, 1 ); | ||
1402 | #endif | ||
1403 | } | ||
1404 | |||
1405 | return ctx; | ||
1406 | } | ||
1407 | #endif | ||
1408 | |||
1409 | #ifdef HAVE_SSL | ||
1410 | void destroy_ssl_ctx( SSL_CTX *ctx ) | ||
1411 | { | ||
1412 | SSL_CTX_free( ctx ); | ||
1413 | } | ||
1414 | #endif | ||
1415 | |||
1416 | #ifdef HAVE_SSL | ||
1417 | int | ||
1418 | fetch_server_certificate( SSL *ssl ) | ||
1419 | { | ||
1420 | server_certificate = SSL_get_peer_certificate( ssl ); | ||
1421 | if ( server_certificate == NULL ) | ||
1422 | ssl_terminate( STATE_CRITICAL, "SSL ERROR: Cannot retrieve server certificate.\n" ); | ||
1423 | |||
1424 | return STATE_OK; | ||
1425 | } | ||
1426 | #endif | ||
1427 | |||
1428 | |||
1429 | #ifdef HAVE_SSL | ||
1430 | int | ||
1431 | check_server_certificate_chain( SSL *ssl ) | ||
1432 | { | ||
1433 | if ( SSL_get_verify_result( ssl ) != X509_V_OK ) | ||
1434 | ssl_terminate( STATE_CRITICAL, "SSL ERROR: Cannot verify server certificate chain.\n" ); | ||
1435 | |||
1436 | return STATE_OK; | ||
1437 | } | ||
1438 | #endif | ||
1439 | |||
1440 | |||
1441 | #ifdef HAVE_SSL | ||
1442 | int | ||
1443 | check_server_certificate_hostname( ) | ||
1444 | { | ||
1445 | char server_CN[256]; | ||
1446 | char *msg = NULL; | ||
1447 | X509_NAME_get_text_by_NID( X509_get_subject_name( server_certificate ), NID_commonName, server_CN, 256 ); | ||
1448 | if ( strcasecmp( server_CN, server_hostname ) ) { | ||
1449 | asprintf( &msg, "SSL ERROR: Server Certificate does not match Hostname %s.\n", server_hostname ); | ||
1450 | ssl_terminate( STATE_WARNING, msg ); | ||
1451 | } | ||
1452 | |||
1453 | return STATE_OK; | ||
1454 | } | ||
1455 | #endif | ||
1456 | |||
1457 | #ifdef HAVE_SSL | ||
1458 | int | ||
1459 | check_server_certificate_expires( ) | ||
1460 | { | ||
1461 | ASN1_STRING *tm; | ||
1462 | int offset; | ||
1463 | struct tm stamp; | ||
1464 | int days_left; | ||
1465 | char timestamp[17] = ""; | ||
1466 | char *msg = NULL; | ||
1467 | |||
1468 | /* Retrieve timestamp of certificate */ | ||
1469 | tm = X509_get_notAfter( server_certificate ); | ||
1470 | |||
1471 | /* Generate tm structure to process timestamp */ | ||
1472 | if ( tm->type == V_ASN1_UTCTIME ) { | ||
1473 | if ( tm->length < 10 ) { | ||
1474 | ssl_terminate( STATE_CRITICAL, "ERROR: Wrong time format in certificate.\n" ); | ||
1475 | } else { | ||
1476 | stamp.tm_year = ( tm->data[0] - '0' ) * 10 + ( tm->data[1] - '0' ); | ||
1477 | if ( stamp.tm_year < 50 ) | ||
1478 | stamp.tm_year += 100; | ||
1479 | offset = 0; | ||
1480 | } | ||
1481 | } else { | ||
1482 | if ( tm->length < 12 ) { | ||
1483 | ssl_terminate( STATE_CRITICAL, "ERROR: Wrong time format in certificate.\n" ); | ||
1484 | } else { | ||
1485 | stamp.tm_year = | ||
1486 | ( tm->data[0] - '0' ) * 1000 + ( tm->data[1] - '0' ) * 100 + | ||
1487 | ( tm->data[2] - '0' ) * 10 + ( tm->data[3] - '0' ); | ||
1488 | stamp.tm_year -= 1900; | ||
1489 | offset = 2; | ||
1490 | } | ||
1491 | } | ||
1492 | stamp.tm_mon = | ||
1493 | ( tm->data[2 + offset] - '0' ) * 10 + ( tm->data[3 + offset] - '0' ) - 1; | ||
1494 | stamp.tm_mday = | ||
1495 | ( tm->data[4 + offset] - '0' ) * 10 + ( tm->data[5 + offset] - '0' ); | ||
1496 | stamp.tm_hour = | ||
1497 | ( tm->data[6 + offset] - '0' ) * 10 + ( tm->data[7 + offset] - '0' ); | ||
1498 | stamp.tm_min = | ||
1499 | ( tm->data[8 + offset] - '0' ) * 10 + ( tm->data[9 + offset] - '0' ); | ||
1500 | stamp.tm_sec = 0; | ||
1501 | stamp.tm_isdst = -1; | ||
1502 | |||
1503 | days_left = ( mktime( &stamp ) - time( NULL ) ) / 86400; | ||
1504 | snprintf | ||
1505 | ( timestamp, 17, "%02d.%02d.%04d %02d:%02d", | ||
1506 | stamp.tm_mday, stamp.tm_mon +1, stamp.tm_year + 1900, | ||
1507 | stamp.tm_hour, stamp.tm_min ); | ||
1508 | |||
1509 | if ( ( days_left > 0 ) && ( days_left <= server_certificate_min_days_valid ) ) { | ||
1510 | asprintf( &msg, "Certificate expires in %d day(s) (%s).\n", days_left, timestamp ); | ||
1511 | ssl_terminate( STATE_WARNING, msg ); | ||
1512 | } | ||
1513 | if ( days_left < 0 ) { | ||
1514 | asprintf( &msg, "Certificate expired on %s.\n", timestamp ); | ||
1515 | ssl_terminate( STATE_CRITICAL, msg ); | ||
1516 | } | ||
1517 | |||
1518 | if (days_left == 0) { | ||
1519 | asprintf( &msg, "Certificate expires today (%s).\n", timestamp ); | ||
1520 | ssl_terminate( STATE_WARNING, msg ); | ||
1521 | } | ||
1522 | |||
1523 | asprintf( &msg, "Certificate will expire on %s.\n", timestamp ); | ||
1524 | ssl_terminate( STATE_OK, msg ); | ||
1525 | } | ||
1526 | #endif | ||
1527 | |||
1528 | /* written by lauri alanko */ | ||
1529 | static char * | ||
1530 | base64 (char *bin, int len) | ||
1531 | { | ||
1532 | |||
1533 | char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1); | ||
1534 | int i = 0, j = 0; | ||
1535 | |||
1536 | char BASE64_END = '='; | ||
1537 | char base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | ||
1538 | |||
1539 | while (j < len - 2) { | ||
1540 | buf[i++] = base64_table[bin[j] >> 2]; | ||
1541 | buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)]; | ||
1542 | buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)]; | ||
1543 | buf[i++] = base64_table[bin[j + 2] & 63]; | ||
1544 | j += 3; | ||
1545 | } | ||
1546 | |||
1547 | switch (len - j) { | ||
1548 | case 1: | ||
1549 | buf[i++] = base64_table[bin[j] >> 2]; | ||
1550 | buf[i++] = base64_table[(bin[j] & 3) << 4]; | ||
1551 | buf[i++] = BASE64_END; | ||
1552 | buf[i++] = BASE64_END; | ||
1553 | break; | ||
1554 | case 2: | ||
1555 | buf[i++] = base64_table[bin[j] >> 2]; | ||
1556 | buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)]; | ||
1557 | buf[i++] = base64_table[(bin[j + 1] & 15) << 2]; | ||
1558 | buf[i++] = BASE64_END; | ||
1559 | break; | ||
1560 | case 0: | ||
1561 | break; | ||
1562 | } | ||
1563 | |||
1564 | buf[i] = '\0'; | ||
1565 | return buf; | ||
1566 | } | ||
1567 | |||