summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--contrib/check_http-with-client-certificate.c1567
1 files changed, 0 insertions, 1567 deletions
diff --git a/contrib/check_http-with-client-certificate.c b/contrib/check_http-with-client-certificate.c
deleted file mode 100644
index c47cbd4f..00000000
--- a/contrib/check_http-with-client-certificate.c
+++ /dev/null
@@ -1,1567 +0,0 @@
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
39const char *progname = "check_http";
40#define REVISION "$Revision: 1117 $"
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 "\
53This plugin tests the HTTP service on the specified host. It can test\n\
54normal (http) and secure (https) servers, follow redirects, search for\n\
55strings and regular expressions, check connection times, and report on\n\
56certificate 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 */
191int verbose = FALSE;
192
193/* time thresholds to determine exit code */
194int use_warning_interval = FALSE;
195double warning_interval = 0;
196int use_critical_interval = FALSE;
197double critical_interval = 0;
198double elapsed_time = 0;
199struct timeval start_tv;
200
201/* variables concerning the server host */
202int use_server_hostname = FALSE;
203char *server_hostname = ""; // hostname for use in HTTPs Host: header
204char *server_host = ""; // hostname or ip address for tcp connect
205int use_server_port = FALSE;
206int server_port = HTTP_PORT;
207
208int use_basic_auth = FALSE;
209char basic_auth[MAX_INPUT_BUFFER] = "";
210
211/* variables concerning server responce */
212struct pageref {
213 char *content;
214 size_t size;
215 char *status;
216 char *header;
217 char *body;
218};
219
220/* variables concerning ssl connections */
221int use_ssl = FALSE;
222#ifdef HAVE_SSL
223int server_certificate_min_days_valid = 0;
224int check_server_certificate = FALSE;
225X509 *server_certificate; // structure containing server certificate
226int use_client_certificate = FALSE;
227char *client_certificate_file = NULL;
228int use_client_certificate_passphrase = FALSE;
229char *client_certificate_passphrase = NULL;
230int use_ca_certificate = FALSE;
231char *ca_certificate_file = NULL;
232
233BIO *bio_err = 0; // error write context
234#endif
235
236
237/* variables concerning check behaviour */
238char *http_method = DEFAULT_HTTP_METHOD;
239char *http_url_path = "";
240int use_http_post_data = FALSE;
241char *http_post_data = "";
242int use_min_content_length = FALSE;
243int min_content_length = 0;
244int use_http_expect_only = FALSE;
245char http_expect[MAX_INPUT_BUFFER] = DEFAULT_HTTP_EXPECT;
246int check_content_string = FALSE;
247char content_string[MAX_INPUT_BUFFER] = "";
248int http_redirect_state = DEFAULT_HTTP_REDIRECT_STATE;
249int http_client_error_state = DEFAULT_HTTP_CLIENT_ERROR_STATE;
250
251#ifdef HAVE_REGEX_H
252regex_t regex_preg;
253regmatch_t regex_pmatch[REGEX_REGS];
254int check_content_regex = FALSE;
255char content_regex[MAX_REGEX_SIZE] = "";
256int regex_cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
257int regex_error = 0;
258char regex_error_buffer[MAX_INPUT_BUFFER] = "";
259#endif
260
261
262
263/*
264************************************************************************
265* FUNCTION PROTOTYPES *
266************************************************************************
267*/
268
269void print_usage( void );
270void print_help( void );
271int process_arguments (int, char **);
272int http_request( int sock, struct pageref *page);
273
274int parse_http_response( struct pageref *page );
275int check_http_response( struct pageref *page );
276int check_http_content( struct pageref *page );
277int prepare_follow_redirect( struct pageref *page );
278
279static char *base64 (char *bin, int len);
280
281#ifdef HAVE_SSL
282int ssl_terminate( int state, char *string );
283static int passwd_cb( char *buf, int num, int rwflag, void *userdata );
284static void sigpipe_handle( int x );
285SSL_CTX * initialize_ssl_ctx( void );
286void destroy_ssl_ctx( SSL_CTX *ctx );
287int fetch_server_certificate( SSL *ssl );
288int check_server_certificate_chain( SSL *ssl );
289int check_server_certificate_hostname( void );
290int check_server_certificate_expires( void );
291int 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 */
331int
332main (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
466void
467print_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
484void
485print_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*/
505int
506process_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 xasprintf( &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 xasprintf( &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 xasprintf( &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 xasprintf( &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 xasprintf( &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 xasprintf( &http_url_path, "%s", optarg );
679 break;
680 case 'P': /* POST DATA */
681 xasprintf( &http_post_data, "%s", optarg );
682 use_http_post_data = TRUE;
683 xasprintf( &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( &regex_preg, content_regex, regex_cflags );
718 if ( regex_error != 0 ) {
719 regerror( regex_error, &regex_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 xasprintf( &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 xasprintf( &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 xasprintf( &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 xasprintf( &http_method, "%s", DEFAULT_HTTP_METHOD );
811 if ( strcmp( http_url_path, "" ) == 0 ) {
812 xasprintf( &http_url_path, "%s", DEFAULT_HTTP_URL_PATH );
813 }
814
815 return TRUE;
816}
817
818
819int
820http_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 xasprintf( &buffer, HTTP_TEMPLATE_REQUEST, buffer, http_method, http_url_path );
833
834 xasprintf( &buffer, HTTP_TEMPLATE_HEADER_USERAGENT, buffer, progname, REVISION, PACKAGE_VERSION );
835
836 if ( use_server_hostname ) {
837 xasprintf( &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 xasprintf( &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 xasprintf( &buffer, "Content-Type: application/x-www-form-urlencoded\r\n" );
849 xasprintf( &buffer, "Content-Length: %i\r\n\r\n", buffer, content_len );
850 xasprintf( &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 xasprintf( &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 xasprintf( &content, "%s%s", content, recvbuff );
874 size += recvsize;
875 }
876 } while ( recvsize > (size_t) 0 );
877
878 xasprintf( &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
889int
890parse_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 xasprintf( &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
958int
959check_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 xasprintf( &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 xasprintf( &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 xasprintf( &msg, "Expected HTTP response received from host\n" );
994 else
995 xasprintf( &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 xasprintf( &msg, "Invalid HTTP response received from host\n" );
1006 else
1007 xasprintf( &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 xasprintf( &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 xasprintf( &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 xasprintf( &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
1082int
1083check_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 xasprintf( &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 xasprintf( &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( &regex_preg, page->content, REGEX_REGS, regex_pmatch, 0);
1112 if ( regex_error == 0 ) {
1113 xasprintf( &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 xasprintf( &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, &regex_preg, regex_error_buffer, MAX_INPUT_BUFFER);
1128 xasprintf( &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
1142int
1143prepare_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 xasprintf( &header, "%s", page->header );
1156
1157
1158 /* restore some default values */
1159 use_http_post_data = FALSE;
1160 xasprintf( &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 xasprintf( &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 xasprintf( &server_hostname, "%s", hostname );
1176 xasprintf( &server_host, "%s", hostname );
1177 use_ssl = chk_protocol(protocol);
1178 server_port = atoi( port );
1179 xasprintf( &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 xasprintf( &server_hostname, "%s", hostname );
1184 xasprintf( &server_host, "%s", hostname );
1185 use_ssl = chk_protocol(protocol);
1186 server_port = protocol_std_port(use_ssl);
1187 xasprintf( &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 xasprintf( &server_hostname, "%s", hostname );
1192 xasprintf( &server_host, "%s", hostname );
1193 use_ssl = chk_protocol(protocol);
1194 server_port = atoi( port );
1195 xasprintf( &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 xasprintf( &server_hostname, "%s", hostname );
1200 xasprintf( &server_host, "%s", hostname );
1201 use_ssl = chk_protocol(protocol);
1202 server_port = protocol_std_port(use_ssl);
1203 xasprintf( &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 xasprintf( &http_url_path, "%s%s", orig_url_path, url_path );
1210 } else {
1211 xasprintf( &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 xasprintf( &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
1229int
1230https_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 xasprintf( &buffer, HTTP_TEMPLATE_REQUEST, buffer, http_method, http_url_path );
1243
1244 xasprintf( &buffer, HTTP_TEMPLATE_HEADER_USERAGENT, buffer, progname, REVISION, PACKAGE_VERSION );
1245
1246 if ( use_server_hostname ) {
1247 xasprintf( &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 xasprintf( &buffer, HTTP_TEMPLATE_HEADER_AUTH, buffer, basic_auth_encoded );
1253 }
1254
1255 /* either send http POST data */
1256 if ( use_http_post_data ) {
1257 xasprintf( &buffer, "%sContent-Type: application/x-www-form-urlencoded\r\n", buffer );
1258 xasprintf( &buffer, "%sContent-Length: %i\r\n\r\n", buffer, content_len );
1259 xasprintf( &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 xasprintf( &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 xasprintf( &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 xasprintf( &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
1333int
1334ssl_terminate(int state, char *string ) {
1335 ERR_print_errors( bio_err );
1336 terminate( state, string );
1337}
1338#endif
1339
1340#ifdef HAVE_SSL
1341static int
1342password_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
1353static void
1354sigpipe_handle( int x ) {
1355}
1356#endif
1357
1358#ifdef HAVE_SSL
1359SSL_CTX *
1360initialize_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
1410void destroy_ssl_ctx( SSL_CTX *ctx )
1411{
1412 SSL_CTX_free( ctx );
1413}
1414#endif
1415
1416#ifdef HAVE_SSL
1417int
1418fetch_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
1430int
1431check_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
1442int
1443check_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 xasprintf( &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
1458int
1459check_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 xasprintf( &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 xasprintf( &msg, "Certificate expired on %s.\n", timestamp );
1515 ssl_terminate( STATE_CRITICAL, msg );
1516 }
1517
1518 if (days_left == 0) {
1519 xasprintf( &msg, "Certificate expires today (%s).\n", timestamp );
1520 ssl_terminate( STATE_WARNING, msg );
1521 }
1522
1523 xasprintf( &msg, "Certificate will expire on %s.\n", timestamp );
1524 ssl_terminate( STATE_OK, msg );
1525}
1526#endif
1527
1528/* written by lauri alanko */
1529static char *
1530base64 (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