Code

Fix for regex input of '|', being output causing problems with Nagios' parsing of
[nagiosplug.git] / contrib / check_http-with-client-certificate.c
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: 1117 $"
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)
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 );
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" );
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;
466 void
467 print_help( void )
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
484 void
485 print_usage( void )
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 );
499 /*
500 * process_arguments()
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 )
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( &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)
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;
819 int 
820 http_request( int sock, struct pageref *page )
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;
889 int
890 parse_http_response( struct pageref *page )
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;
903         
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;
958 int
959 check_http_response( struct pageref *page )
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;
1082 int
1083 check_http_content( struct pageref *page )
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( &regex_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, &regex_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;
1142 int
1143 prepare_follow_redirect( struct pageref *page )
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) */
1218         
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 );
1228 #ifdef HAVE_SSL
1229 int
1230 https_request( SSL_CTX *ctx, SSL *ssl, struct pageref *page )
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 );
1245         
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;
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 );
1338 #endif
1340 #ifdef HAVE_SSL
1341 static int 
1342 password_cb( char *buf, int num, int rwflag, void *userdata )
1344         if ( num < strlen( client_certificate_passphrase ) + 1 )
1345                 return( 0 );
1347         strcpy( buf, client_certificate_passphrase );
1348         return( strlen( client_certificate_passphrase ) );
1350 #endif
1352 #ifdef HAVE_SSL
1353 static void 
1354 sigpipe_handle( int x ) {
1356 #endif
1358 #ifdef HAVE_SSL
1359 SSL_CTX *
1360 initialize_ssl_ctx( void )
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;
1407 #endif
1409 #ifdef HAVE_SSL
1410 void destroy_ssl_ctx( SSL_CTX *ctx )
1412         SSL_CTX_free( ctx );
1414 #endif
1416 #ifdef HAVE_SSL
1417 int
1418 fetch_server_certificate( SSL *ssl )
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;
1426 #endif
1429 #ifdef HAVE_SSL
1430 int
1431 check_server_certificate_chain( SSL *ssl )
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;
1438 #endif
1441 #ifdef HAVE_SSL
1442 int
1443 check_server_certificate_hostname( )
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;
1455 #endif
1457 #ifdef HAVE_SSL
1458 int
1459 check_server_certificate_expires( )
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 );
1526 #endif
1528 /* written by lauri alanko */
1529 static char *
1530 base64 (char *bin, int len)
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;