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 *****************************************************************************/
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 *****************************************************************************/
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"
46 #include "config.h"
47 #include "common.h"
48 #include "netutils.h"
49 #include "utils.h"
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"
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"
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"
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
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"
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"
142 #ifdef HAVE_SSL
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
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
164 #define HTTPS_PORT 443
165 #endif
167 #ifdef HAVE_REGEX_H
168 #include <regex.h>
169 #define REGEX_REGS 2
170 #define MAX_REGEX_SIZE 256
171 #endif
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" )
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._-=@,]"
184 /*
185 ************************************************************************
186 * GLOBAL VARIABLE/POINTER DEFINITIONS *
187 ************************************************************************
188 */
190 /* misc variables */
191 int verbose = FALSE;
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;
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;
208 int use_basic_auth = FALSE;
209 char basic_auth[MAX_INPUT_BUFFER] = "";
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 };
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;
233 BIO *bio_err = 0; // error write context
234 #endif
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;
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
263 /*
264 ************************************************************************
265 * FUNCTION PROTOTYPES *
266 ************************************************************************
267 */
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);
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 );
279 static char *base64 (char *bin, int len);
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
294 /*
295 ************************************************************************
296 * IMPLEMENTATION *
297 ************************************************************************
298 */
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
343 if ( process_arguments(argc, argv) == ERROR )
344 usage( "check_http: could not parse arguments\n" );
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
355 /* Loop around 3xx onredirect=follow */
356 do {
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 );
367 /* make a tcp connection */
368 result = my_tcp_connect( server_host, server_port, &sock );
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" );
382 /* fetch server certificate */
383 result = fetch_server_certificate( ssl );
385 /* verify server certificate against CAs */
386 if ( ( result == STATE_OK ) && use_ca_certificate ) {
387 result = check_server_certificate_chain( ssl );
388 }
390 /* check if certificate matches hostname */
391 if ( ( result == STATE_OK ) && use_server_hostname ) {
392 result = check_server_certificate_hostname();
393 }
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 );
416 /* close the tcp connection */
417 close( sock );
419 /* reset the alarm */
420 alarm( 0 );
422 /* analyze http page */
423 /* TO DO */
424 if ( result == STATE_OK )
425 result = parse_http_response( (struct pageref *) &page );
427 if ( result == STATE_OK )
428 result = check_http_response( (struct pageref *) &page );
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 }
441 } else {
442 /* some error occured while trying to make a tcp connect */
443 exit( result );
444 }
446 } while ( result == STATE_DEPENDENT ); // end of onredirect loop
448 /* destroy SSL context */
449 #ifdef HAVE_SSL
450 if ( use_ssl || ( http_redirect_state == STATE_DEPENDENT ) )
451 destroy_ssl_ctx( ctx );
452 #endif
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 );
462 return result;
463 }
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 }
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 }
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;
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
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 }
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:"
555 while (1) {
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
563 if ( ( c == -1 ) || ( c == EOF ) ) {
564 break;
565 }
567 switch (c) {
568 case '?': /* usage */
569 usage2( "unknown argument", optarg );
570 break;
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 */
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)
758 c = optind;
761 /* Sanity checks on supplied command line arguments */
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 }
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 }
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 }
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" );
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" );
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" );
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 }
815 return TRUE;
816 }
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;
832 asprintf( &buffer, HTTP_TEMPLATE_REQUEST, buffer, http_method, http_url_path );
834 asprintf( &buffer, HTTP_TEMPLATE_HEADER_USERAGENT, buffer, progname, REVISION, PACKAGE_VERSION );
836 if ( use_server_hostname ) {
837 asprintf( &buffer, HTTP_TEMPLATE_HEADER_HOST, buffer, server_hostname );
838 }
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 }
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 }
867 /* read server's response */
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 );
878 asprintf( &page->content, "%s", content );
879 page->size = size;
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" );
885 return STATE_OK;
886 }
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
898 asprintf( &content, "%s", page->content );
900 /* find status line and null-terminate it */
901 // copy content to status
902 status = content;
904 // find end of status line and copy pointer to pos
905 content += (size_t) strcspn( content, "\r\n" );
906 pos = content;
908 // advance content pointer behind the newline of status line
909 content += (size_t) strspn( content, "\r\n" );
911 // null-terminate status line at pos
912 status[strcspn( status, "\r\n")] = 0;
913 strip( status );
915 // copy final status to struct member
916 page->status = status;
919 /* find header and null-terminate it */
920 // copy remaining content to header
921 header = content;
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;
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" );
938 // null-terminate header at pos
939 header[pos - header] = 0;
941 // copy final header to struct member
942 page->header = header;
945 // copy remaining content to body
946 page->body = content;
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 }
954 return STATE_OK;
955 }
958 int
959 check_http_response( struct pageref *page )
960 {
961 char *msg = "";
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 }
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 }
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 }
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 }
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 }
1079 return STATE_OK;
1080 }
1082 int
1083 check_http_content( struct pageref *page )
1084 {
1085 char *msg = "";
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 }
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
1138 return STATE_OK;
1139 }
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;
1155 asprintf( &header, "%s", page->header );
1158 /* restore some default values */
1159 use_http_post_data = FALSE;
1160 asprintf( &http_method, "%s", DEFAULT_HTTP_METHOD );
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 );
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" );
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) */
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 }
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;
1242 asprintf( &buffer, HTTP_TEMPLATE_REQUEST, buffer, http_method, http_url_path );
1244 asprintf( &buffer, HTTP_TEMPLATE_HEADER_USERAGENT, buffer, progname, REVISION, PACKAGE_VERSION );
1246 if ( use_server_hostname ) {
1247 asprintf( &buffer, HTTP_TEMPLATE_HEADER_HOST, buffer, server_hostname );
1248 }
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 }
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 {
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 }
1287 /* read server's response */
1289 do {
1290 recvsize = SSL_read( ssl, recvbuff, MAX_INPUT_BUFFER - 1 );
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 );
1320 asprintf( &page->content, "%s", content );
1321 page->size = size;
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" );
1327 return STATE_OK;
1328 }
1329 #endif
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
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 );
1347 strcpy( buf, client_certificate_passphrase );
1348 return( strlen( client_certificate_passphrase ) );
1349 }
1350 #endif
1352 #ifdef HAVE_SSL
1353 static void
1354 sigpipe_handle( int x ) {
1355 }
1356 #endif
1358 #ifdef HAVE_SSL
1359 SSL_CTX *
1360 initialize_ssl_ctx( void )
1361 {
1362 SSL_METHOD *meth;
1363 SSL_CTX *ctx;
1365 if ( !bio_err ) {
1366 /* Global system initialization */
1367 SSL_library_init();
1368 SSL_load_error_strings();
1370 /* An error write context */
1371 bio_err=BIO_new_fp( stderr, BIO_NOCLOSE );
1372 }
1374 /* set up as SIGPIPE handler */
1375 signal( SIGPIPE, sigpipe_handle );
1377 /* create our context */
1378 meth=SSLv3_method();
1379 ctx=SSL_CTX_new( meth );
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" );
1386 /* set client certificate key passphrase */
1387 if ( use_client_certificate_passphrase ) {
1388 SSL_CTX_set_default_passwd_cb( ctx, password_cb );
1389 }
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 }
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" );
1400 #if (OPENSSL_VERSION_NUMBER < 0x00905100L)
1401 SSL_CTX_set_verify_depth( ctx, 1 );
1402 #endif
1403 }
1405 return ctx;
1406 }
1407 #endif
1409 #ifdef HAVE_SSL
1410 void destroy_ssl_ctx( SSL_CTX *ctx )
1411 {
1412 SSL_CTX_free( ctx );
1413 }
1414 #endif
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" );
1424 return STATE_OK;
1425 }
1426 #endif
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" );
1436 return STATE_OK;
1437 }
1438 #endif
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 }
1453 return STATE_OK;
1454 }
1455 #endif
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;
1468 /* Retrieve timestamp of certificate */
1469 tm = X509_get_notAfter( server_certificate );
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;
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 );
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 }
1518 if (days_left == 0) {
1519 asprintf( &msg, "Certificate expires today (%s).\n", timestamp );
1520 ssl_terminate( STATE_WARNING, msg );
1521 }
1523 asprintf( &msg, "Certificate will expire on %s.\n", timestamp );
1524 ssl_terminate( STATE_OK, msg );
1525 }
1526 #endif
1528 /* written by lauri alanko */
1529 static char *
1530 base64 (char *bin, int len)
1531 {
1533 char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
1534 int i = 0, j = 0;
1536 char BASE64_END = '=';
1537 char base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
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 }
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 }
1564 buf[i] = '\0';
1565 return buf;
1566 }