Back to index

nagios-plugins  1.4.16
check_http-with-client-certificate.c
Go to the documentation of this file.
00001 /****************************************************************************
00002  *
00003  * Program: HTTP plugin for Nagios
00004  * License: GPL
00005  *
00006  * License Information:
00007  *
00008  * This program is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software
00020  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00021  *
00022  *****************************************************************************/
00023 
00024 /****************************************************************************
00025  *
00026  * check_http is derived from the original check_http provided by
00027  * Ethan Galstad/Karl DeBisschop
00028  *
00029  * This provides some additional functionality including:
00030  * - check server certificate against supplied hostname (Host: header) if any
00031  * - check server certificate against local CA certificates (as browsers do)
00032  * - authenticate with client certificate (and optional passphrase)
00033  * - specify HTTP returncodes to return a status of WARNING or OK instead of
00034  *   CRITICAL (only global for 3xx or 4xx errors)
00035  * - check only against HTTP status line and exit immediately if not matched
00036  *
00037  *****************************************************************************/
00038 
00039 const char *progname = "check_http";
00040 #define REVISION "$Revision: 1117 $"
00041 #define CVSREVISION "1.24"
00042 #define COPYRIGHT "2003"
00043 #define AUTHORS "Fabian Pehla"
00044 #define EMAIL "fabian@pehla.de"
00045 
00046 #include "config.h"
00047 #include "common.h"
00048 #include "netutils.h"
00049 #include "utils.h"
00050 
00051 
00052 #define HELP_TXT_SUMMARY "\
00053 This plugin tests the HTTP service on the specified host. It can test\n\
00054 normal (http) and secure (https) servers, follow redirects, search for\n\
00055 strings and regular expressions, check connection times, and report on\n\
00056 certificate expiration times.\n"
00057 
00058 #define HELP_TXT_OPTIONS "\
00059 -H <virtual host> -I <ip address> [-p <port>] [-u <uri>]\n\
00060             [-w <warn time>] [-c <critical time>] [-t <timeout>]\n\
00061             [-S] [-C <days>] [-a <basic auth>] [-A <certificate file>]\n\
00062             [-Z <ca certificate file>] [-e <expect>] [-E <expect only>]\n\
00063             [-s <string>] [-r <regex>] [-R <regex case insensitive>]\n\
00064             [-f (ok|warn|critical|follow)] [-g (ok|warn|critical)]\n"
00065 
00066 #define HELP_TXT_LONGOPTIONS "\
00067  -H, --hostname=<virtual host>\n\
00068     FQDN host name argument for use in HTTP Host:-Header (virtual host)\n\
00069     If used together wich the -S option, the server certificate will\n\
00070     be checked against this hostname\n\
00071  -I, --ip-address=<address>\n\
00072    IP address or hostname for TCP connect (use IP to avoid DNS lookup)\n\
00073  -p, --port=<port>\n\
00074    Port number (default: %d)\n\
00075  -u, --url-path=<uri>\n\
00076    URL to request from host (default: %s)\n\
00077  -S, --ssl\n\
00078    Use SSL (default port: %d)\n\
00079  -C, --server-certificate-days=<days>\n\
00080    Minimum number of days a server certificate must be valid\n\
00081    No other check can be combined with this option\n\
00082  -a, --basic-auth=<username:password>\n\
00083    Colon separated username and password for basic authentication\n\
00084  -A, --client-certificate=<certificate file>\n\
00085    File containing X509 client certificate and key\n\
00086  -K, --passphrase=<passphrase>\n\
00087    Passphrase for the client certificate key\n\
00088    This option can only be used in combination with the -A option\n\
00089  -Z, --ca-certificate=<certificate file>\n\
00090    File containing certificates of trusted CAs\n\
00091    The server certificate will be checked against these CAs\n\
00092  -e, --http-expect=<expect string>\n\
00093    String to expect in HTTP response line (Default: %s)\n\
00094  -E, --http-expect-only=<expect only string>\n\
00095    String to expect in HTTP response line\n\
00096    No other checks are made, this either matches the response\n\
00097    or exits immediately\n\
00098  -s, --content-string=<string>\n\
00099    String to expect in content\n\
00100  -r, --content-ereg=<regex>\n\
00101    Regular expression to expect in content\n\
00102  -R, --content-eregi=<regex case insensitive>\n\
00103    Case insensitive regular expression to expect in content\n\
00104  -f, --onredirect=(ok|warning|critical|follow)\n\
00105    Follow a redirect (3xx) or return with a user defined state\n\
00106    Default: OK\n\
00107  -g, --onerror=(ok|warning|critical)\n\
00108    Status to return on a client error (4xx)\n\
00109  -m, --min=INTEGER\n\
00110    Minimum page size required (bytes)\n\
00111  -t, --timeout=<timeout>\n\
00112    Seconds before connection times out (default: %d)\n\
00113  -c, --critical=<critical time>\n\
00114    Response time to result in critical status (seconds)\n\
00115  -w, --warning=<warn time>\n\
00116    Response time to result in warning status (seconds)\n\
00117  -V, --version\n\
00118     Print version information\n\
00119  -v, --verbose\n\
00120     Show details for command-line debugging (do not use with nagios server)\n\
00121  -h, --help\n\
00122     Print detailed help screen\n"
00123 
00124 
00125 
00126 #define HTTP_PORT 80
00127 #define DEFAULT_HTTP_URL_PATH "/"
00128 #define DEFAULT_HTTP_EXPECT "HTTP/1."
00129 #define DEFAULT_HTTP_METHOD "GET"
00130 #define DEFAULT_HTTP_REDIRECT_STATE STATE_OK
00131 #define DEFAULT_HTTP_CLIENT_ERROR_STATE STATE_WARNING
00132 
00133 #define HTTP_TEMPLATE_REQUEST "%s%s %s HTTP/1.0\r\n"
00134 #define HTTP_TEMPLATE_HEADER_USERAGENT "%sUser-Agent: %s/%s (nagios-plugins %s)\r\n"
00135 #define HTTP_TEMPLATE_HEADER_HOST "%sHost: %s\r\n"
00136 #define HTTP_TEMPLATE_HEADER_AUTH "%sAuthorization: Basic %s\r\n"
00137 
00138 /* fill in printf with protocol_text(use_ssl), state_text(state), page->status, elapsed_time */
00139 #define RESULT_TEMPLATE_STATUS_RESPONSE_TIME "%s %s: %s - %7.3f seconds response time|time=%7.3f\n"
00140 #define RESULT_TEMPLATE_RESPONSE_TIME "%s %s: %7.3f seconds response time|time=%7.3f\n"
00141 
00142 #ifdef HAVE_SSL
00143 
00144 #ifdef HAVE_SSL_H
00145 #include <rsa.h>
00146 #include <crypto.h>
00147 #include <x509.h>
00148 #include <pem.h>
00149 #include <ssl.h>
00150 #include <err.h>
00151 #include <rand.h>
00152 #endif
00153 
00154 #ifdef HAVE_OPENSSL_SSL_H
00155 #include <openssl/rsa.h>
00156 #include <openssl/crypto.h>
00157 #include <openssl/x509.h>
00158 #include <openssl/pem.h>
00159 #include <openssl/ssl.h>
00160 #include <openssl/err.h>
00161 #include <openssl/rand.h>
00162 #endif
00163 
00164 #define HTTPS_PORT 443
00165 #endif 
00166 
00167 #ifdef HAVE_REGEX_H
00168 #include <regex.h>
00169 #define REGEX_REGS 2
00170 #define MAX_REGEX_SIZE 256
00171 #endif
00172 
00173 #define chk_protocol(protocol) ( strstr( protocol, "https" ) ? TRUE : FALSE );
00174 #define protocol_std_port(use_ssl) ( use_ssl ? HTTPS_PORT : HTTP_PORT );
00175 #define protocol_text(use_ssl) ( use_ssl ? "HTTPS" : "HTTP" )
00176 
00177 #define MAX_IPV4_HOSTLENGTH 64
00178 #define HTTP_HEADER_LOCATION_MATCH "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
00179 #define HTTP_HEADER_PROTOCOL_MATCH "%[HTPShtps]://"
00180 #define HTTP_HEADER_HOSTNAME_MATCH "%[a-zA-Z0-9.-]"
00181 #define HTTP_HEADER_PORT_MATCH ":%[0-9]"
00182 #define HTTP_HEADER_URL_PATH_MATCH "%[/a-zA-Z0-9._-=@,]"
00183 
00184 /*
00185 ************************************************************************
00186 * GLOBAL VARIABLE/POINTER DEFINITIONS                                  *
00187 ************************************************************************
00188 */
00189 
00190 /* misc variables */
00191 int verbose = FALSE;
00192 
00193 /* time thresholds to determine exit code */
00194 int use_warning_interval = FALSE;
00195 double warning_interval = 0;
00196 int use_critical_interval = FALSE;
00197 double critical_interval = 0;
00198 double elapsed_time = 0;
00199 struct timeval start_tv;
00200 
00201 /* variables concerning the server host */
00202 int use_server_hostname = FALSE;
00203 char *server_hostname = "";  // hostname for use in HTTPs Host: header
00204 char *server_host = "";      // hostname or ip address for tcp connect
00205 int use_server_port = FALSE;
00206 int server_port = HTTP_PORT;
00207 
00208 int use_basic_auth = FALSE;
00209 char basic_auth[MAX_INPUT_BUFFER] = "";
00210 
00211 /* variables concerning server responce */
00212 struct pageref {
00213         char   *content;
00214         size_t size;
00215         char   *status;
00216         char   *header;
00217         char   *body;
00218 };
00219 
00220 /* variables concerning ssl connections */
00221 int use_ssl = FALSE;
00222 #ifdef HAVE_SSL
00223 int server_certificate_min_days_valid = 0;
00224 int check_server_certificate = FALSE;
00225 X509 *server_certificate;            // structure containing server certificate
00226 int use_client_certificate = FALSE;
00227 char *client_certificate_file = NULL;
00228 int use_client_certificate_passphrase = FALSE;
00229 char *client_certificate_passphrase = NULL;
00230 int use_ca_certificate = FALSE;
00231 char *ca_certificate_file = NULL;
00232 
00233 BIO *bio_err = 0;             // error write context
00234 #endif
00235 
00236 
00237 /* variables concerning check behaviour */
00238 char *http_method = DEFAULT_HTTP_METHOD;
00239 char *http_url_path = "";
00240 int use_http_post_data = FALSE;
00241 char *http_post_data = "";
00242 int use_min_content_length = FALSE;
00243 int min_content_length = 0;
00244 int use_http_expect_only = FALSE;
00245 char http_expect[MAX_INPUT_BUFFER] = DEFAULT_HTTP_EXPECT;
00246 int check_content_string = FALSE;
00247 char content_string[MAX_INPUT_BUFFER] = "";
00248 int http_redirect_state = DEFAULT_HTTP_REDIRECT_STATE;
00249 int http_client_error_state = DEFAULT_HTTP_CLIENT_ERROR_STATE;
00250 
00251 #ifdef HAVE_REGEX_H
00252 regex_t regex_preg;
00253 regmatch_t regex_pmatch[REGEX_REGS];
00254 int check_content_regex = FALSE;
00255 char content_regex[MAX_REGEX_SIZE] = "";
00256 int regex_cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
00257 int regex_error = 0;
00258 char regex_error_buffer[MAX_INPUT_BUFFER] = "";
00259 #endif
00260 
00261 
00262 
00263 /*
00264 ************************************************************************
00265 * FUNCTION PROTOTYPES                                                  *
00266 ************************************************************************
00267 */
00268 
00269 void print_usage( void );
00270 void print_help( void );
00271 int process_arguments (int, char **);
00272 int http_request( int sock, struct pageref *page);
00273 
00274 int parse_http_response( struct pageref *page );
00275 int check_http_response( struct pageref *page );
00276 int check_http_content( struct pageref *page );
00277 int prepare_follow_redirect( struct pageref *page );
00278 
00279 static char *base64 (char *bin, int len);
00280 
00281 #ifdef HAVE_SSL
00282 int ssl_terminate( int state, char *string );
00283 static int passwd_cb( char *buf, int num, int rwflag, void *userdata );
00284 static void sigpipe_handle( int x );
00285 SSL_CTX * initialize_ssl_ctx( void );
00286 void destroy_ssl_ctx( SSL_CTX *ctx );
00287 int fetch_server_certificate( SSL *ssl );
00288 int check_server_certificate_chain( SSL *ssl );
00289 int check_server_certificate_hostname( void );
00290 int check_server_certificate_expires( void );
00291 int https_request( SSL_CTX *ctx, SSL *ssl, struct pageref *page );
00292 #endif
00293 
00294 /*
00295 ************************************************************************
00296 * IMPLEMENTATION                                                       * 
00297 ************************************************************************
00298 */
00299 
00300 /*
00301  * main()
00302  *
00303  * PSEUDOCODE OF HOW MAIN IS SUPPOSED TO WORK
00304  *
00305  * process command line arguments including sanity check
00306  * initialize alarm signal handling
00307  * if use_ssl
00308  *   build ssl context
00309  * LOOP:
00310  * make tcp connection
00311  * if use_ssl 
00312  *   make ssl connection
00313  *   if use_server_hostname
00314  *     check if certificate matches hostname
00315  *   if check_server_certificate
00316  *     check expiration date of server certificate
00317  *     return STATUS
00318  *   else 
00319  *     request http page
00320  *     handle ssl rehandshake
00321  *   close ssl connection
00322  * else
00323  *   request http page
00324  * close tcp connection
00325  * analyze http page
00326  * if follow on redirect
00327  *   repeat LOOP
00328  * end of LOOP
00329  * destroy ssl context
00330  */
00331 int
00332 main (int argc, char **argv)
00333 {
00334         int result = STATE_UNKNOWN;
00335         int sock;
00336         struct pageref page;
00337 #ifdef HAVE_SSL
00338         SSL_CTX *ctx;
00339         SSL *ssl;
00340         BIO *sbio;
00341 #endif
00342 
00343         if ( process_arguments(argc, argv) == ERROR )
00344                 usage( "check_http: could not parse arguments\n" );
00345 
00346 #ifdef HAVE_SSL
00347         /* build SSL context if required:
00348          * a) either we use ssl from the beginning OR
00349          * b) or we follor redirects wich may lead os to a ssl page 
00350          */
00351         if ( use_ssl || ( http_redirect_state == STATE_DEPENDENT ) )
00352                 ctx=initialize_ssl_ctx();
00353 #endif
00354 
00355         /* Loop around 3xx onredirect=follow */
00356         do {
00357 
00358                 /*
00359                  * initialize alarm signal handling, set socket timeout, start timer
00360                  * socket_timeout and socket_timeout_alarm_handler are defined in
00361                  * netutils.c
00362                  */
00363                 (void) signal( SIGALRM, socket_timeout_alarm_handler );
00364                 (void) alarm( socket_timeout );
00365                 gettimeofday( &start_tv, NULL );
00366         
00367                 /* make a tcp connection */
00368                 result = my_tcp_connect( server_host, server_port, &sock );
00369         
00370                 /* result of tcp connect */
00371                 if ( result == STATE_OK )
00372                 {
00373 #ifdef HAVE_SSL
00374                         /* make a ssl connection */
00375                         if ( use_ssl ) {
00376                                 ssl=SSL_new( ctx );
00377                                 sbio=BIO_new_socket( sock, BIO_NOCLOSE );
00378                                 SSL_set_bio( ssl, sbio, sbio);
00379                                 if ( SSL_connect( ssl ) <= 0 )
00380                                         ssl_terminate( STATE_CRITICAL, "check_http: SSL connect error" );
00381 
00382                                 /* fetch server certificate */
00383                                 result = fetch_server_certificate( ssl );
00384 
00385                                 /* verify server certificate against CAs */
00386                                 if ( ( result == STATE_OK ) && use_ca_certificate ) {
00387                                         result = check_server_certificate_chain( ssl );
00388                                 }
00389 
00390                                 /* check if certificate matches hostname */
00391                                 if ( ( result == STATE_OK ) && use_server_hostname ) {
00392                                         result = check_server_certificate_hostname();
00393                                 }
00394 
00395                                 if ( result == STATE_OK ) {
00396                                         /* check server certificate expire date */
00397                                         if ( check_server_certificate ) {
00398                                                 result = check_server_certificate_expires();
00399                                         /* OR: perform http request */
00400                                         } else {
00401                                                 result = https_request( ctx, ssl, (struct pageref *) &page );
00402                                         }
00403                                 }
00404                                 SSL_shutdown( ssl );
00405                                 SSL_free( ssl );
00406                         } else {
00407 #endif
00408                         /* HTTP implementation */
00409                         result = http_request( sock, (struct pageref *) &page );
00410 #ifdef HAVE_SSL
00411                         }
00412 #endif
00413                         /* stop timer and calculate elapsed_time */
00414                         elapsed_time = delta_time( start_tv );
00415 
00416                         /* close the tcp connection */
00417                         close( sock );
00418 
00419                         /* reset the alarm */
00420                         alarm( 0 );
00421 
00422                         /* analyze http page */
00423                         /* TO DO */
00424                         if ( result == STATE_OK )
00425                                 result = parse_http_response( (struct pageref *) &page );
00426 
00427                         if ( result == STATE_OK )
00428                                 result = check_http_response( (struct pageref *) &page );
00429 
00430                         switch ( result ) {
00431                                 case STATE_OK:
00432                                         /* weiter geht's */
00433                                         result = check_http_content( (struct pageref *) &page );
00434                                         break;
00435                                 case STATE_DEPENDENT:
00436                                         /* try to determine redirect parameters */
00437                                         result = prepare_follow_redirect( (struct pageref *) &page );
00438                                         break;
00439                         }
00440 
00441                 } else {
00442                         /* some error occured while trying to make a tcp connect */
00443                         exit( result );
00444                 }
00445 
00446         } while ( result == STATE_DEPENDENT ); // end of onredirect loop
00447 
00448         /* destroy SSL context */
00449 #ifdef HAVE_SSL
00450         if ( use_ssl || ( http_redirect_state == STATE_DEPENDENT ) )
00451                 destroy_ssl_ctx( ctx );
00452 #endif
00453 
00454         /* if we ever get to this point, everything went fine */
00455         printf( RESULT_TEMPLATE_STATUS_RESPONSE_TIME, 
00456                 protocol_text( use_ssl ),
00457                 state_text( result ),
00458                 page.status,
00459                 elapsed_time,
00460                 elapsed_time );
00461 
00462         return result;
00463 }
00464 
00465 
00466 void
00467 print_help( void )
00468 {
00469         print_revision( progname, REVISION );
00470         printf
00471                 ( "Copyright (c) %s %s <%s>\n\n%s\n",
00472                  COPYRIGHT, AUTHORS, EMAIL, HELP_TXT_SUMMARY );
00473         print_usage();
00474         printf( "NOTE: One or both of -H and -I must be specified\n" );
00475         printf( "\nOptions:\n" HELP_TXT_LONGOPTIONS "\n",
00476                 HTTP_PORT, DEFAULT_HTTP_URL_PATH, HTTPS_PORT,
00477                 DEFAULT_HTTP_EXPECT, DEFAULT_SOCKET_TIMEOUT );
00478 #ifdef HAVE_SSL
00479         //printf( SSLDESCRIPTION );
00480 #endif
00481 }
00482 
00483 
00484 void
00485 print_usage( void )
00486 {
00487         printf( "Usage:\n" " %s %s\n"
00488 #ifdef HAVE_GETOPT_H
00489                 " %s (-h | --help) for detailed help\n"
00490                 " %s (-V | --version) for version information\n",
00491 #else
00492                 " %s -h for detailed help\n"
00493                 " %s -V for version information\n",
00494 #endif
00495         progname, HELP_TXT_OPTIONS, progname, progname );
00496 }
00497 
00498 
00499 /*
00500 * process_arguments()
00501 *
00502 * process command line arguments either using getopt_long or getopt
00503 * (parsing long argumants manually)
00504 */
00505 int
00506 process_arguments( int argc, char **argv )
00507 {
00508         int c, i = 1;
00509         extern char *optarg;
00510 
00511 #ifdef HAVE_GETOPT_H
00512         int option_index = 0;
00513         static struct option long_options[] = {
00514                 STD_LONG_OPTS,
00515                 {"file", required_argument, 0, 'F'},
00516                 {"ip-address", required_argument, 0, 'I'},
00517                 {"port", required_argument, 0, 'p'},
00518                 {"url-path", required_argument, 0, 'u'},
00519                 {"post-data", required_argument, 0, 'P'},
00520                 {"ssl", no_argument, 0, 'S'},
00521                 {"server-certificate-days", required_argument, 0, 'C'},
00522                 {"basic-auth", required_argument, 0, 'a'},
00523                 {"client-certificate", required_argument, 0, 'A'},
00524                 {"passphrase", required_argument, 0, 'K'},
00525                 {"ca-certificate", required_argument, 0, 'Z'},
00526                 {"http-expect", required_argument, 0, 'e'},
00527                 {"http-expect-only", required_argument, 0, 'E'},
00528                 {"content-string", required_argument, 0, 's'},
00529                 {"content-ereg-linespan", required_argument, 0, 'l'},
00530                 {"content-ereg", required_argument, 0, 'r'},
00531                 {"content-eregi", required_argument, 0, 'R'},
00532                 {"onredirect", required_argument, 0, 'f'},
00533                 {"onerror", required_argument, 0, 'g'},
00534                 {"min", required_argument, 0, 'm'},
00535                 {0, 0, 0, 0}
00536         };
00537 #endif
00538 
00539 
00540         /* convert commonly used arguments to their equivalent standard options */
00541         for (c = 1; c < argc; c++) {
00542                 if ( strcmp( "-to", argv[c]) == 0 )
00543                         strcpy( argv[c], "-t" );
00544                 if ( strcmp( "-hn", argv[c]) == 0 )
00545                         strcpy( argv[c], "-H" );
00546                 if ( strcmp( "-wt", argv[c]) == 0 )
00547                         strcpy( argv[c], "-w" );
00548                 if ( strcmp( "-ct", argv[c]) == 0 )
00549                         strcpy( argv[c], "-c" );
00550         }
00551 
00552 #define OPTCHARS "Vvht:c:w:H:F:I:p:u:P:SC:a:A:K:Z:e:E:s:r:R:f:g:lm:"
00553 
00554 
00555         while (1) {
00556 
00557 #ifdef HAVE_GETOPT_H
00558                 c = getopt_long( argc, argv, OPTCHARS, long_options, &option_index );
00559 #else
00560                 c = getopt( argc, argv, OPTCHARS );
00561 #endif
00562 
00563                 if ( ( c == -1 ) || ( c == EOF ) ) {
00564                         break;
00565                 }
00566 
00567                 switch (c) {
00568                 case '?': /* usage */
00569                         usage2( "unknown argument", optarg );
00570                         break;
00571 
00572               /* Standard options */
00573                 case 'h': /* help */
00574                         print_help();
00575                         exit( STATE_OK );
00576                         break;
00577                 case 'V': /* version */
00578                         print_revision( progname, REVISION );
00579                         exit( STATE_OK );
00580                         break;
00581                 case 'v': /* verbose */
00582                         verbose = TRUE;
00583                         break;
00584                 case 't': /* timeout period */
00585                         if ( !is_intnonneg( optarg ) )
00586                                 usage2( "timeout interval must be a non-negative integer", optarg );
00587                         /* socket_timeout is defined in netutils.h and defaults to
00588                          * DEFAULT_SOCKET_TIMEOUT from common.h
00589                          */
00590                         socket_timeout = atoi( optarg );
00591                         break;
00592                 case 'c': /* critical time threshold */
00593                         if ( !is_nonnegative( optarg ) )
00594                                 usage2( "invalid critical threshold", optarg );
00595                         critical_interval = strtod( optarg, NULL );
00596                         use_critical_interval = TRUE;
00597                         break;
00598                 case 'w': /* warning time threshold */
00599                         if ( !is_nonnegative( optarg ) )
00600                                 usage2( "invalid warning threshold", optarg );
00601                         warning_interval = strtod( optarg, NULL );
00602                         use_warning_interval = TRUE;
00603                         break;
00604                 case 'H': /* Host Name (virtual host) */
00605                         /* this rejects FQDNs, so we leave it for now... 
00606                          *if ( !is_hostname( optarg ) )
00607                          *        usage2( "invalid hostname", optarg );
00608                          */
00609                         asprintf( &server_hostname, "%s", optarg );
00610                         use_server_hostname = TRUE;
00611                         break;
00612                 case 'F': /* File (dummy) */
00613                         break;
00614               /* End of standard options */
00615 
00616 
00617                 case 'I': /* Server IP-address or Hostname */
00618                         /* this rejects FQDNs, so we leave it for now... 
00619                          *if ( !is_host( optarg ) )
00620                          *        usage2( "invalid ip address or hostname", optarg )
00621                          */
00622                         asprintf( &server_host, "%s", optarg );
00623                         break;
00624                 case 'p': /* Server port */
00625                         if ( !is_intnonneg( optarg ) )
00626                                 usage2( "invalid port number", optarg );
00627                         server_port = atoi( optarg );
00628                         use_server_port = TRUE;
00629                         break;
00630                 case 'S': /* use SSL */
00631 #ifdef HAVE_SSL
00632                         use_ssl = TRUE;
00633                         if ( use_server_port == FALSE )
00634                                 server_port = HTTPS_PORT;
00635 #else
00636                         usage( "check_http: invalid option - SSL is not available\n" );
00637 #endif
00638                         break;
00639                 case 'C': /* Server certificate warning time threshold */
00640 #ifdef HAVE_SSL
00641                         if ( !is_intnonneg( optarg ) )
00642                                 usage2( "invalid certificate expiration period", optarg );
00643                         server_certificate_min_days_valid = atoi( optarg );
00644                         check_server_certificate = TRUE;
00645 #else
00646                         usage( "check_http: invalid option - SSL is not available\n" );
00647 #endif
00648                         break;
00649                 case 'a': /* basic authorization info */
00650                         strncpy( basic_auth, optarg, MAX_INPUT_BUFFER - 1 );
00651                         basic_auth[MAX_INPUT_BUFFER - 1] = 0;
00652                         use_basic_auth = TRUE;
00653                         break;
00654                 case 'A': /* client certificate */
00655 #ifdef HAVE_SSL
00656                         asprintf( &client_certificate_file, "%s", optarg );
00657                         use_client_certificate = TRUE;
00658 #else
00659                         usage( "check_http: invalid option - SSL is not available\n" );
00660 #endif
00661                         break;
00662                 case 'K': /* client certificate passphrase */
00663 #ifdef HAVE_SSL
00664                         asprintf( &client_certificate_passphrase, "%s", optarg );
00665                         use_client_certificate_passphrase = TRUE;
00666 #else
00667                         usage( "check_http: invalid option - SSL is not available\n" );
00668 #endif
00669                 case 'Z': /* valid CA certificates */
00670 #ifdef HAVE_SSL
00671                         asprintf( &ca_certificate_file, "%s", optarg );
00672                         use_ca_certificate = TRUE;
00673 #else
00674                         usage( "check_http: invalid option - SSL is not available\n" );
00675 #endif
00676                         break;
00677                 case 'u': /* URL PATH */
00678                         asprintf( &http_url_path, "%s", optarg );
00679                         break;
00680                 case 'P': /* POST DATA */
00681                         asprintf( &http_post_data, "%s", optarg );
00682                         use_http_post_data = TRUE;
00683                         asprintf( &http_method, "%s", "POST" );
00684                         break;
00685                 case 'e': /* expected string in first line of HTTP response */
00686                         strncpy( http_expect , optarg, MAX_INPUT_BUFFER - 1 );
00687                         http_expect[MAX_INPUT_BUFFER - 1] = 0;
00688                         break;
00689                 case 'E': /* expected string in first line of HTTP response and process no other check*/
00690                         strncpy( http_expect , optarg, MAX_INPUT_BUFFER - 1 );
00691                         http_expect[MAX_INPUT_BUFFER - 1] = 0;
00692                         use_http_expect_only = TRUE;
00693                         break;
00694                 case 's': /* expected (sub-)string in content */
00695                         strncpy( content_string , optarg, MAX_INPUT_BUFFER - 1 );
00696                         content_string[MAX_INPUT_BUFFER - 1] = 0;
00697                         check_content_string = TRUE;
00698                         break;
00699                 case 'l': /* regex linespan */
00700 #ifdef HAVE_REGEX_H
00701                         regex_cflags &= ~REG_NEWLINE;
00702 #else
00703                         usage( "check_http: call for regex which was not a compiled option\n" );
00704 #endif
00705                         break;
00706                 case 'R': /* expected case insensitive regular expression in content */
00707 #ifdef HAVE_REGEX_H
00708                         regex_cflags |= REG_ICASE;
00709 #else
00710                         usage( "check_http: call for regex which was not a compiled option\n" );
00711 #endif
00712                 case 'r': /* expected regular expression in content */
00713 #ifdef HAVE_REGEX_H
00714                         strncpy( content_regex , optarg, MAX_REGEX_SIZE - 1 );
00715                         content_regex[MAX_REGEX_SIZE - 1] = 0;
00716                         check_content_regex = TRUE;
00717                         regex_error = regcomp( &regex_preg, content_regex, regex_cflags );
00718                         if ( regex_error != 0 ) {
00719                                 regerror( regex_error, &regex_preg, regex_error_buffer, MAX_INPUT_BUFFER );
00720                                 printf( "Could Not Compile Regular Expression: %s", regex_error_buffer );
00721                                 return ERROR;
00722                         }
00723 #else
00724                         usage( "check_http: call for regex which was not a compiled option\n" );
00725 #endif
00726                         break;
00727                 case 'f': /* onredirect (3xx errors) */
00728                         if ( !strcmp( optarg, "follow" ) )
00729                                 http_redirect_state = STATE_DEPENDENT;
00730                         if ( !strcmp( optarg, "unknown" ) )
00731                                 http_redirect_state = STATE_UNKNOWN;
00732                         if ( !strcmp( optarg, "ok" ) )
00733                                 http_redirect_state = STATE_OK;
00734                         if ( !strcmp( optarg, "warning" ) )
00735                                 http_redirect_state = STATE_WARNING;
00736                         if ( !strcmp( optarg, "critical" ) )
00737                                 http_redirect_state = STATE_CRITICAL;
00738                         break;
00739                 case 'g': /* onerror (4xx errors) */
00740                         if ( !strcmp( optarg, "unknown" ) )
00741                                 http_client_error_state = STATE_UNKNOWN;
00742                         if ( !strcmp( optarg, "ok" ) )
00743                                 http_client_error_state = STATE_OK;
00744                         if ( !strcmp( optarg, "warning" ) )
00745                                 http_client_error_state = STATE_WARNING;
00746                         if ( !strcmp( optarg, "critical" ) )
00747                                 http_client_error_state = STATE_CRITICAL;
00748                         break;
00749                 case 'm': /* min */
00750                         if ( !is_intnonneg( optarg ) )
00751                                 usage2( "invalid page size", optarg );
00752                         min_content_length = atoi( optarg );
00753                         use_min_content_length = TRUE;
00754                         break;
00755                 } // end switch 
00756         } // end while(1)
00757 
00758         c = optind;
00759 
00760 
00761         /* Sanity checks on supplied command line arguments */
00762 
00763         /* 1. if both host and hostname are not defined, try to
00764          *    fetch one more argument which is possibly supplied
00765          *    without an option
00766          */
00767         if ( ( strcmp( server_host, "" ) ) && (c < argc) ) {
00768                 asprintf( &server_host, "%s", argv[c++] );
00769         }
00770 
00771         /* 2. check if another artument is supplied
00772          */
00773         if ( ( strcmp( server_hostname, "" ) == 0 ) && (c < argc) ) {
00774                asprintf( &server_hostname, "%s", argv[c++] ); 
00775         }
00776 
00777         /* 3. if host is still not defined, just copy hostname, 
00778          *    which is then guaranteed to be defined by now
00779          */
00780         if ( strcmp( server_host, "") == 0 ) {
00781                 if ( strcmp( server_hostname, "" ) == 0 ) {
00782                      usage ("check_http: you must specify a server address or host name\n");
00783                 } else {
00784                         asprintf( &server_host, "%s", server_hostname );
00785                 }
00786         }
00787 
00788         /* 4. check if content checks for a string and a regex
00789          *    are requested for only one of both is possible at
00790          *    a time
00791          */
00792         if ( check_content_string && check_content_regex )
00793                 usage( "check_http: you can only check for string OR regex at a time\n" );
00794 
00795         /* 5. check for options which require use_ssl */
00796         if ( check_server_certificate && !use_ssl ) 
00797                 usage( "check_http: you must use -S to check server certificate\n" );
00798         if ( use_client_certificate && !use_ssl ) 
00799                 usage( "check_http: you must use -S to authenticate with a client certificate\n" );
00800         if ( use_ca_certificate && !use_ssl ) 
00801                 usage( "check_http: you must use -S to check server certificate against CA certificates\n" );
00802 
00803         /* 6. check for passphrase without client certificate */
00804         if ( use_client_certificate_passphrase && !use_client_certificate )
00805                 usage( "check_http: you must supply a client certificate to use a passphrase\n" );
00806 
00807 
00808         /* Finally set some default values if necessary */
00809         if ( strcmp( http_method, "" ) == 0 )
00810                 asprintf( &http_method, "%s", DEFAULT_HTTP_METHOD );
00811         if ( strcmp( http_url_path, "" ) == 0 ) {
00812                 asprintf( &http_url_path, "%s", DEFAULT_HTTP_URL_PATH );
00813         }
00814 
00815         return TRUE;
00816 }
00817 
00818 
00819 int 
00820 http_request( int sock, struct pageref *page )
00821 {
00822         char *buffer = "";
00823         char recvbuff[MAX_INPUT_BUFFER] = ""; 
00824         int buffer_len = 0;
00825         int content_len = 0;
00826         size_t sendsize = 0;
00827         size_t recvsize = 0;
00828         char *content = ""; 
00829         size_t size = 0;
00830         char *basic_auth_encoded = NULL;
00831 
00832         asprintf( &buffer, HTTP_TEMPLATE_REQUEST, buffer, http_method, http_url_path );
00833 
00834         asprintf( &buffer, HTTP_TEMPLATE_HEADER_USERAGENT, buffer, progname, REVISION, PACKAGE_VERSION );
00835 
00836         if ( use_server_hostname ) {
00837                 asprintf( &buffer, HTTP_TEMPLATE_HEADER_HOST, buffer, server_hostname );
00838         }
00839 
00840         if ( use_basic_auth ) {
00841                 basic_auth_encoded = base64( basic_auth, strlen( basic_auth ) );
00842                 asprintf( &buffer, HTTP_TEMPLATE_HEADER_AUTH, buffer, basic_auth_encoded );
00843         }
00844 
00845         /* either send http POST data */
00846         if ( use_http_post_data ) {
00847         /* based on code written by Chris Henesy <lurker@shadowtech.org> */
00848                 asprintf( &buffer, "Content-Type: application/x-www-form-urlencoded\r\n" );
00849                 asprintf( &buffer, "Content-Length: %i\r\n\r\n", buffer, content_len );
00850                 asprintf( &buffer, "%s%s%s", buffer, http_post_data, "\r\n" );
00851                 sendsize = send( sock, buffer, strlen( buffer ), 0 );
00852                 if ( sendsize < strlen( buffer ) ) {
00853                         printf( "ERROR: Incomplete write\n" );
00854                         return STATE_CRITICAL;
00855                 }
00856         /* or just a newline */
00857         } else {
00858                asprintf( &buffer, "%s%s", buffer, "\r\n" );
00859                      sendsize = send( sock, buffer, strlen( buffer ) , 0 );
00860                      if ( sendsize < strlen( buffer ) ) {
00861                       printf( "ERROR: Incomplete write\n" );
00862                        return STATE_CRITICAL;
00863                }
00864        }
00865 
00866 
00867         /* read server's response */
00868 
00869         do {
00870                 recvsize = recv( sock, recvbuff, MAX_INPUT_BUFFER - 1, 0 );
00871                 if ( recvsize > (size_t) 0 ) {
00872                         recvbuff[recvsize] = '\0';
00873                         asprintf( &content, "%s%s", content, recvbuff );
00874                         size += recvsize;
00875                 }
00876         } while ( recvsize > (size_t) 0 );
00877 
00878         asprintf( &page->content, "%s", content );
00879         page->size = size;
00880 
00881         /* return a CRITICAL status if we couldn't read any data */
00882         if ( size == (size_t) 0)
00883                 ssl_terminate( STATE_CRITICAL, "No data received" );
00884 
00885         return STATE_OK;
00886 }
00887 
00888 
00889 int
00890 parse_http_response( struct pageref *page )
00891 {
00892        char *content = "";     //local copy of struct member
00893         char *status = "";      //local copy of struct member
00894         char *header = "";      //local copy of struct member
00895         size_t len = 0;         //temporary used
00896         char *pos = "";         //temporary used
00897 
00898         asprintf( &content, "%s", page->content );
00899 
00900         /* find status line and null-terminate it */
00901         // copy content to status
00902         status = content;
00903         
00904         // find end of status line and copy pointer to pos
00905         content += (size_t) strcspn( content, "\r\n" );
00906         pos = content;
00907 
00908         // advance content pointer behind the newline of status line
00909         content += (size_t) strspn( content, "\r\n" );
00910 
00911         // null-terminate status line at pos
00912         status[strcspn( status, "\r\n")] = 0;
00913         strip( status );
00914 
00915         // copy final status to struct member
00916         page->status = status;
00917 
00918 
00919         /* find header and null-terminate it */
00920         // copy remaining content to header
00921         header = content;
00922 
00923         // loop until line containing only newline is found (end of header)
00924         while ( strcspn( content, "\r\n" ) > 0 ) {
00925                 //find end of line and copy pointer to pos
00926                 content += (size_t) strcspn( content, "\r\n" );
00927                 pos = content;
00928 
00929                 if ( ( strspn( content, "\r" ) == 1 && strspn( content, "\r\n" ) >= 2 ) ||
00930                      ( strspn( content, "\n" ) == 1 && strspn( content, "\r\n" ) >= 2 ) )
00931                         content += (size_t) 2;
00932                 else
00933                         content += (size_t) 1;
00934         }
00935         // advance content pointer behind the newline
00936         content += (size_t) strspn( content, "\r\n" );
00937 
00938         // null-terminate header at pos
00939         header[pos - header] = 0;
00940 
00941         // copy final header to struct member
00942         page->header = header;
00943 
00944 
00945         // copy remaining content to body
00946         page->body = content;
00947 
00948         if ( verbose ) {
00949                 printf( "STATUS: %s\n", page->status );
00950                 printf( "HEADER: \n%s\n", page->header );
00951                 printf( "BODY: \n%s\n", page->body );
00952         }
00953 
00954         return STATE_OK;
00955 }
00956 
00957 
00958 int
00959 check_http_response( struct pageref *page )
00960 {
00961         char *msg = "";
00962 
00963         /* check response time befor anything else */
00964         if ( use_critical_interval && ( elapsed_time > critical_interval ) ) {
00965                 asprintf( &msg, RESULT_TEMPLATE_RESPONSE_TIME, 
00966                                 protocol_text( use_ssl ),
00967                                 state_text( STATE_CRITICAL ),
00968                                 elapsed_time,
00969                                 elapsed_time ); 
00970                 terminate( STATE_CRITICAL, msg );
00971         }
00972         if ( use_warning_interval  && ( elapsed_time > warning_interval ) ) {
00973                 asprintf( &msg, RESULT_TEMPLATE_RESPONSE_TIME, 
00974                                 protocol_text( use_ssl ),
00975                                 state_text( STATE_WARNING ),
00976                                 elapsed_time,
00977                                 elapsed_time );
00978                 terminate( STATE_WARNING, msg );
00979         }
00980 
00981 
00982         /* make sure the status line matches the response we are looking for */
00983         if ( strstr( page->status, http_expect ) ) {
00984                 /* The result is only checked against the expected HTTP status line,
00985                    so exit immediately after this check */
00986                 if ( use_http_expect_only ) {
00987                         if ( ( server_port == HTTP_PORT ) 
00988 #ifdef HAVE_SSL
00989                              || ( server_port == HTTPS_PORT ) )
00990 #else
00991                            )
00992 #endif 
00993                                 asprintf( &msg, "Expected HTTP response received from host\n" );
00994                         else
00995                                 asprintf( &msg, "Expected HTTP response received from host on port %d\n", server_port );
00996                         terminate( STATE_OK, msg );
00997                 }
00998         } else {
00999                 if ( ( server_port == HTTP_PORT )
01000 #ifdef HAVE_SSL
01001                      || ( server_port == HTTPS_PORT ) )
01002 #else
01003                    )
01004 #endif
01005                         asprintf( &msg, "Invalid HTTP response received from host\n" );
01006                 else
01007                         asprintf( &msg, "Invalid HTTP response received from host on port %d\n", server_port );
01008                 terminate( STATE_CRITICAL, msg );
01009         }
01010 
01011         /* check the return code */
01012         /* server errors result in a critical state */
01013         if ( strstr( page->status, "500" ) ||
01014              strstr( page->status, "501" ) ||
01015              strstr( page->status, "502" ) ||
01016              strstr( page->status, "503" ) ||
01017              strstr( page->status, "504" ) ||
01018              strstr( page->status, "505" )) {
01019                 asprintf( &msg, RESULT_TEMPLATE_STATUS_RESPONSE_TIME,
01020                                 protocol_text( use_ssl ),
01021                                 state_text( http_client_error_state ),
01022                                 page->status,
01023                                 elapsed_time,
01024                                 elapsed_time );
01025                 terminate( STATE_CRITICAL, msg );
01026         }
01027 
01028         /* client errors result in a warning state */
01029         if ( strstr( page->status, "400" ) ||
01030              strstr( page->status, "401" ) ||
01031              strstr( page->status, "402" ) ||
01032              strstr( page->status, "403" ) ||
01033              strstr( page->status, "404" ) ||
01034              strstr( page->status, "405" ) ||
01035              strstr( page->status, "406" ) ||
01036              strstr( page->status, "407" ) ||
01037              strstr( page->status, "408" ) ||
01038              strstr( page->status, "409" ) ||
01039              strstr( page->status, "410" ) ||
01040              strstr( page->status, "411" ) ||
01041              strstr( page->status, "412" ) ||
01042              strstr( page->status, "413" ) ||
01043              strstr( page->status, "414" ) ||
01044              strstr( page->status, "415" ) ||
01045              strstr( page->status, "416" ) ||
01046              strstr( page->status, "417" ) ) {
01047                 asprintf( &msg, RESULT_TEMPLATE_STATUS_RESPONSE_TIME,
01048                                 protocol_text( use_ssl ),
01049                                 state_text( http_client_error_state ),
01050                                 page->status,
01051                                 elapsed_time,
01052                                 elapsed_time );
01053                 terminate( http_client_error_state, msg );
01054         }
01055 
01056         /* check redirected page if specified */
01057         if (strstr( page->status, "300" ) ||
01058             strstr( page->status, "301" ) ||
01059             strstr( page->status, "302" ) ||
01060             strstr( page->status, "303" ) ||
01061             strstr( page->status, "304" ) ||
01062             strstr( page->status, "305" ) ||
01063             strstr( page->status, "306" ) ||
01064             strstr( page->status, "307" ) ) {
01065                 if ( http_redirect_state == STATE_DEPENDENT ) {
01066                                 /* returning STATE_DEPENDENT means follow redirect */
01067                                 return STATE_DEPENDENT;
01068                 } else {
01069                         asprintf( &msg, RESULT_TEMPLATE_STATUS_RESPONSE_TIME,
01070                                         protocol_text( use_ssl ),
01071                                         state_text( http_redirect_state ),
01072                                         page->status,
01073                                         elapsed_time,
01074                                         elapsed_time );
01075                         terminate( http_redirect_state, msg );
01076                 }
01077         }
01078 
01079         return STATE_OK;
01080 }
01081 
01082 int
01083 check_http_content( struct pageref *page )
01084 {
01085         char *msg = "";
01086 
01087         /* check for string in content */
01088         if ( check_content_string ) {
01089                 if ( strstr( page->content, content_string ) ) {
01090                         asprintf( &msg, RESULT_TEMPLATE_STATUS_RESPONSE_TIME,
01091                                         protocol_text( use_ssl ),
01092                                         state_text( STATE_OK ),
01093                                         page->status,
01094                                         elapsed_time,
01095                                         elapsed_time );
01096                         terminate( STATE_OK, msg );
01097                 } else {
01098                         asprintf( &msg, RESULT_TEMPLATE_STATUS_RESPONSE_TIME,
01099                                         protocol_text( use_ssl ),
01100                                         state_text( STATE_CRITICAL ),
01101                                         page->status,
01102                                         elapsed_time,
01103                                         elapsed_time );
01104                         terminate( STATE_CRITICAL, msg );
01105                 }
01106         }
01107 
01108 #ifdef HAVE_REGEX_H
01109         /* check for regex in content */
01110         if ( check_content_regex ) {
01111                 regex_error = regexec( &regex_preg, page->content, REGEX_REGS, regex_pmatch, 0);
01112                 if ( regex_error == 0 ) {
01113                         asprintf( &msg, RESULT_TEMPLATE_STATUS_RESPONSE_TIME,
01114                                         protocol_text( use_ssl ),
01115                                         state_text( STATE_OK ),
01116                                         page->status,
01117                                         elapsed_time,
01118                                         elapsed_time );
01119                         terminate( STATE_OK, msg );
01120                 } else {
01121                         if ( regex_error == REG_NOMATCH ) {
01122                                 asprintf( &msg, "%s, %s: regex pattern not found\n",
01123                                                 protocol_text( use_ssl) ,
01124                                                 state_text( STATE_CRITICAL ) );
01125                                 terminate( STATE_CRITICAL, msg );
01126                         } else {
01127                                 regerror( regex_error, &regex_preg, regex_error_buffer, MAX_INPUT_BUFFER);
01128                                 asprintf( &msg, "%s %s: Regex execute Error: %s\n",
01129                                                 protocol_text( use_ssl) ,
01130                                                 state_text( STATE_CRITICAL ),
01131                                                 regex_error_buffer );
01132                                 terminate( STATE_CRITICAL, msg );
01133                         }
01134                 }
01135         }
01136 #endif
01137 
01138         return STATE_OK;
01139 }
01140 
01141 
01142 int
01143 prepare_follow_redirect( struct pageref *page )
01144 {
01145         char *header = NULL;
01146         char *msg = "";
01147         char protocol[6];
01148         char hostname[MAX_IPV4_HOSTLENGTH];
01149         char port[6];
01150         char *url_path = NULL;
01151         char *orig_url_path = NULL;
01152         char *orig_url_dirname = NULL;
01153         size_t len = 0;
01154 
01155         asprintf( &header, "%s", page->header );
01156 
01157 
01158         /* restore some default values */
01159         use_http_post_data = FALSE;
01160         asprintf( &http_method, "%s", DEFAULT_HTTP_METHOD );
01161 
01162         /* copy url of original request, maybe we need it to compose 
01163            absolute url from relative Location: header */
01164         asprintf( &orig_url_path, "%s", http_url_path );
01165 
01166         while ( strcspn( header, "\r\n" ) > (size_t) 0 ) {
01167                 url_path = realloc( url_path, (size_t) strcspn( header, "\r\n" ) );
01168                 if ( url_path == NULL )
01169                         terminate( STATE_UNKNOWN, "HTTP UNKNOWN: could not reallocate url_path" );
01170 
01171 
01172                 /* Try to find a Location header combination of METHOD HOSTNAME PORT and PATH */
01173                 /* 1. scan for Location: http[s]://hostname:port/path */
01174                 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 ) {
01175                         asprintf( &server_hostname, "%s", hostname );
01176                         asprintf( &server_host, "%s", hostname );
01177                         use_ssl = chk_protocol(protocol);
01178                         server_port = atoi( port );
01179                         asprintf( &http_url_path, "%s", url_path );
01180                         return STATE_DEPENDENT;
01181                 } 
01182                 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) {
01183                         asprintf( &server_hostname, "%s", hostname );
01184                         asprintf( &server_host, "%s", hostname );
01185                         use_ssl = chk_protocol(protocol);
01186                         server_port = protocol_std_port(use_ssl);
01187                         asprintf( &http_url_path, "%s", url_path );
01188                         return STATE_DEPENDENT;
01189                 }
01190                 else if ( sscanf ( header, HTTP_HEADER_LOCATION_MATCH HTTP_HEADER_PROTOCOL_MATCH HTTP_HEADER_HOSTNAME_MATCH HTTP_HEADER_PORT_MATCH, &protocol, &hostname, &port ) == 3) {
01191                         asprintf( &server_hostname, "%s", hostname );
01192                         asprintf( &server_host, "%s", hostname );
01193                         use_ssl = chk_protocol(protocol);
01194                         server_port = atoi( port );
01195                         asprintf( &http_url_path, "%s", DEFAULT_HTTP_URL_PATH );
01196                         return STATE_DEPENDENT;
01197                 }
01198                 else if ( sscanf ( header, HTTP_HEADER_LOCATION_MATCH HTTP_HEADER_PROTOCOL_MATCH HTTP_HEADER_HOSTNAME_MATCH, protocol, hostname ) == 2 ) {
01199                         asprintf( &server_hostname, "%s", hostname );
01200                         asprintf( &server_host, "%s", hostname );
01201                         use_ssl = chk_protocol(protocol);
01202                         server_port = protocol_std_port(use_ssl);
01203                         asprintf( &http_url_path, "%s", DEFAULT_HTTP_URL_PATH );
01204                 }
01205                 else if ( sscanf ( header, HTTP_HEADER_LOCATION_MATCH HTTP_HEADER_URL_PATH_MATCH, url_path ) == 1 ) {
01206                         /* check for relative url and prepend path if necessary */
01207                         if ( ( url_path[0] != '/' ) && ( orig_url_dirname = strrchr( orig_url_path, '/' ) ) ) {
01208                                 *orig_url_dirname = '\0';
01209                                 asprintf( &http_url_path, "%s%s", orig_url_path, url_path );
01210                         } else {
01211                                 asprintf( &http_url_path, "%s", url_path );
01212                         }
01213                         return STATE_DEPENDENT;
01214                 }
01215                 header += (size_t) strcspn( header, "\r\n" );
01216                 header += (size_t) strspn( header, "\r\n" );
01217         } /* end while (header) */
01218         
01219 
01220         /* default return value is STATE_DEPENDENT to continue looping in main() */
01221         asprintf( &msg, "% %: % - Could not find redirect Location",
01222                         protocol_text( use_ssl ),
01223                         state_text( STATE_UNKNOWN ),
01224                         page->status );
01225         terminate( STATE_UNKNOWN, msg );
01226 }
01227 
01228 #ifdef HAVE_SSL
01229 int
01230 https_request( SSL_CTX *ctx, SSL *ssl, struct pageref *page )
01231 {
01232         char *buffer = "";
01233         char recvbuff[MAX_INPUT_BUFFER] = "";
01234         int buffer_len = 0;
01235         int content_len = 0;
01236         size_t sendsize = 0;
01237         size_t recvsize = 0;
01238         char *content = "";
01239         size_t size = 0;
01240         char *basic_auth_encoded = NULL;
01241 
01242         asprintf( &buffer, HTTP_TEMPLATE_REQUEST, buffer, http_method, http_url_path );
01243 
01244         asprintf( &buffer, HTTP_TEMPLATE_HEADER_USERAGENT, buffer, progname, REVISION, PACKAGE_VERSION );
01245         
01246         if ( use_server_hostname ) {
01247                 asprintf( &buffer, HTTP_TEMPLATE_HEADER_HOST, buffer, server_hostname );
01248         }
01249 
01250         if ( use_basic_auth ) {
01251                 basic_auth_encoded = base64( basic_auth, strlen( basic_auth ) );
01252                 asprintf( &buffer, HTTP_TEMPLATE_HEADER_AUTH, buffer, basic_auth_encoded );
01253         }
01254 
01255         /* either send http POST data */
01256         if ( use_http_post_data ) {
01257                 asprintf( &buffer, "%sContent-Type: application/x-www-form-urlencoded\r\n", buffer );
01258                 asprintf( &buffer, "%sContent-Length: %i\r\n\r\n", buffer, content_len );
01259                 asprintf( &buffer, "%s%s%s", buffer, http_post_data, "\r\n" );
01260                 sendsize = SSL_write( ssl, buffer, strlen( buffer ) );
01261                 switch ( SSL_get_error( ssl, sendsize ) ) {
01262                         case SSL_ERROR_NONE:
01263                                 if ( sendsize < strlen( buffer ) )
01264                                         ssl_terminate( STATE_CRITICAL, "SSL ERROR: Incomplete write.\n" );
01265                                 break;
01266                         default:
01267                                 ssl_terminate( STATE_CRITICAL, "SSL ERROR: Write problem.\n" );
01268                                 break;
01269                 }
01270         /* or just a newline */
01271         } else {
01272 
01273                 asprintf( &buffer, "%s\r\n", buffer );
01274                 sendsize = SSL_write( ssl, buffer, strlen( buffer ) );
01275                 switch ( SSL_get_error( ssl, sendsize ) ) {
01276                         case SSL_ERROR_NONE:
01277                                 if ( sendsize < strlen( buffer ) )
01278                                         ssl_terminate( STATE_CRITICAL, "SSL ERROR: Incomplete write.\n" );
01279                                 break;
01280                         default:
01281                                 ssl_terminate( STATE_CRITICAL, "SSL ERROR: Write problem.\n" );
01282                                 break;
01283                 }
01284         }
01285 
01286 
01287         /* read server's response */
01288 
01289         do {
01290                 recvsize = SSL_read( ssl, recvbuff, MAX_INPUT_BUFFER - 1 );
01291 
01292                 switch ( SSL_get_error( ssl, recvsize ) ) {
01293                         case SSL_ERROR_NONE:
01294                                 if ( recvsize > (size_t) 0 ) {
01295                                         recvbuff[recvsize] = '\0';
01296                                         asprintf( &content, "%s%s", content, recvbuff );
01297                                         size += recvsize;
01298                                 }
01299                                 break;
01300                         case SSL_ERROR_WANT_READ:
01301                                 if ( use_client_certificate ) {
01302                                         continue;
01303                                 } else {
01304                                         // workaround while we don't have anonymous client certificates: return OK
01305                                         //ssl_terminate( STATE_WARNING, "HTTPS WARNING - Client Certificate required.\n" );
01306                                         ssl_terminate( STATE_OK, "HTTPS WARNING - Client Certificate required.\n" );
01307                                 }
01308                                 break;
01309                         case SSL_ERROR_ZERO_RETURN:
01310                                 break;
01311                         case SSL_ERROR_SYSCALL:
01312                                 ssl_terminate( STATE_CRITICAL, "SSL ERROR: Premature close.\n" );
01313                                 break;
01314                         default:
01315                                 ssl_terminate( STATE_CRITICAL, "SSL ERROR: Read problem.\n" );
01316                                 break;
01317                 }
01318         } while ( recvsize > (size_t) 0 );
01319 
01320         asprintf( &page->content, "%s", content );
01321         page->size = size;
01322 
01323         /* return a CRITICAL status if we couldn't read any data */
01324         if ( size == (size_t) 0)
01325                 ssl_terminate( STATE_CRITICAL, "No data received" );
01326 
01327         return STATE_OK;
01328 }
01329 #endif
01330 
01331 
01332 #ifdef HAVE_SSL
01333 int 
01334 ssl_terminate(int state, char *string ) {
01335         ERR_print_errors( bio_err );
01336         terminate( state, string );
01337 }
01338 #endif
01339 
01340 #ifdef HAVE_SSL
01341 static int 
01342 password_cb( char *buf, int num, int rwflag, void *userdata )
01343 {
01344         if ( num < strlen( client_certificate_passphrase ) + 1 )
01345                 return( 0 );
01346 
01347         strcpy( buf, client_certificate_passphrase );
01348         return( strlen( client_certificate_passphrase ) );
01349 }
01350 #endif
01351 
01352 #ifdef HAVE_SSL
01353 static void 
01354 sigpipe_handle( int x ) {
01355 }
01356 #endif
01357 
01358 #ifdef HAVE_SSL
01359 SSL_CTX *
01360 initialize_ssl_ctx( void )
01361 {
01362         SSL_METHOD *meth;
01363         SSL_CTX *ctx;
01364 
01365         if ( !bio_err ) {
01366                 /* Global system initialization */
01367                 SSL_library_init();
01368                 SSL_load_error_strings();
01369 
01370                 /* An error write context */
01371                 bio_err=BIO_new_fp( stderr, BIO_NOCLOSE );
01372         }
01373 
01374         /* set up as SIGPIPE handler */
01375         signal( SIGPIPE, sigpipe_handle );
01376 
01377         /* create our context */
01378         meth=SSLv3_method();
01379         ctx=SSL_CTX_new( meth );
01380 
01381         /* load client certificate and key */
01382         if ( use_client_certificate ) {
01383                 if ( !(SSL_CTX_use_certificate_chain_file( ctx, client_certificate_file )) ) 
01384                         ssl_terminate( STATE_CRITICAL, "check_http: can't read client certificate file" );
01385 
01386                 /* set client certificate key passphrase */
01387                 if ( use_client_certificate_passphrase ) {
01388                         SSL_CTX_set_default_passwd_cb( ctx, password_cb );
01389                 }
01390 
01391                 if ( !(SSL_CTX_use_PrivateKey_file( ctx, client_certificate_file, SSL_FILETYPE_PEM )) )
01392                         ssl_terminate( STATE_CRITICAL, "check_http: can't read client certificate key file" );
01393         }
01394 
01395         /* load the CAs we trust */
01396         if ( use_ca_certificate ) {
01397                 if ( !(SSL_CTX_load_verify_locations( ctx, ca_certificate_file, 0 )) )
01398                         ssl_terminate( STATE_CRITICAL, "check_http: can't read CA certificate file" );
01399 
01400 #if (OPENSSL_VERSION_NUMBER < 0x00905100L)
01401                 SSL_CTX_set_verify_depth( ctx, 1 );
01402 #endif
01403         }
01404 
01405         return ctx;
01406 }
01407 #endif
01408 
01409 #ifdef HAVE_SSL
01410 void destroy_ssl_ctx( SSL_CTX *ctx )
01411 {
01412         SSL_CTX_free( ctx );
01413 }
01414 #endif
01415 
01416 #ifdef HAVE_SSL
01417 int
01418 fetch_server_certificate( SSL *ssl )
01419 {
01420         server_certificate = SSL_get_peer_certificate( ssl );
01421         if ( server_certificate == NULL )
01422                 ssl_terminate( STATE_CRITICAL,  "SSL ERROR: Cannot retrieve server certificate.\n" );
01423 
01424         return STATE_OK;
01425 }
01426 #endif
01427 
01428 
01429 #ifdef HAVE_SSL
01430 int
01431 check_server_certificate_chain( SSL *ssl )
01432 {
01433         if ( SSL_get_verify_result( ssl ) != X509_V_OK )
01434                 ssl_terminate( STATE_CRITICAL, "SSL ERROR: Cannot verify server certificate chain.\n" );
01435 
01436         return STATE_OK;
01437 }
01438 #endif
01439 
01440 
01441 #ifdef HAVE_SSL
01442 int
01443 check_server_certificate_hostname( )
01444 {
01445         char server_CN[256];
01446         char *msg = NULL;
01447         X509_NAME_get_text_by_NID( X509_get_subject_name( server_certificate ), NID_commonName, server_CN, 256 );
01448         if ( strcasecmp( server_CN, server_hostname ) ) {
01449                 asprintf( &msg, "SSL ERROR: Server Certificate does not match Hostname %s.\n", server_hostname );
01450                 ssl_terminate( STATE_WARNING, msg );
01451         }
01452 
01453         return STATE_OK;
01454 }
01455 #endif
01456 
01457 #ifdef HAVE_SSL
01458 int
01459 check_server_certificate_expires( )
01460 {
01461         ASN1_STRING *tm;
01462         int offset;
01463         struct tm stamp;
01464         int days_left;
01465         char timestamp[17] = "";
01466         char *msg = NULL;
01467 
01468         /* Retrieve timestamp of certificate */
01469         tm = X509_get_notAfter( server_certificate );
01470 
01471         /* Generate tm structure to process timestamp */
01472         if ( tm->type == V_ASN1_UTCTIME ) {
01473                 if ( tm->length < 10 ) {
01474                         ssl_terminate( STATE_CRITICAL, "ERROR: Wrong time format in certificate.\n" );
01475                 } else {
01476                         stamp.tm_year = ( tm->data[0] - '0' ) * 10 + ( tm->data[1] - '0' );
01477                         if ( stamp.tm_year < 50 )
01478                                 stamp.tm_year += 100;
01479                         offset = 0;
01480                 }
01481         } else {
01482                 if ( tm->length < 12 ) {
01483                         ssl_terminate( STATE_CRITICAL, "ERROR: Wrong time format in certificate.\n" );
01484                 } else {
01485                         stamp.tm_year =
01486                                 ( tm->data[0] - '0' ) * 1000 + ( tm->data[1] - '0' ) * 100 +
01487                                 ( tm->data[2] - '0' ) * 10 + ( tm->data[3] - '0' );
01488                         stamp.tm_year -= 1900;
01489                         offset = 2;
01490                 }
01491         }
01492         stamp.tm_mon =
01493                 ( tm->data[2 + offset] - '0' ) * 10 + ( tm->data[3 + offset] - '0' ) - 1;
01494         stamp.tm_mday =
01495                 ( tm->data[4 + offset] - '0' ) * 10 + ( tm->data[5 + offset] - '0' );
01496         stamp.tm_hour =
01497                 ( tm->data[6 + offset] - '0' ) * 10 + ( tm->data[7 + offset] - '0' );
01498         stamp.tm_min =
01499                 ( tm->data[8 + offset] - '0' ) * 10 + ( tm->data[9 + offset] - '0' );
01500         stamp.tm_sec = 0;
01501         stamp.tm_isdst = -1;
01502 
01503         days_left = ( mktime( &stamp ) - time( NULL ) ) / 86400;
01504         snprintf
01505                 ( timestamp, 17, "%02d.%02d.%04d %02d:%02d",
01506                  stamp.tm_mday, stamp.tm_mon +1, stamp.tm_year + 1900, 
01507                  stamp.tm_hour, stamp.tm_min );
01508 
01509         if ( ( days_left > 0 ) && ( days_left <= server_certificate_min_days_valid ) ) {
01510                 asprintf( &msg, "Certificate expires in %d day(s) (%s).\n", days_left, timestamp );
01511                 ssl_terminate( STATE_WARNING, msg );
01512         }
01513         if ( days_left < 0 ) {
01514                 asprintf( &msg, "Certificate expired on %s.\n", timestamp );
01515                 ssl_terminate( STATE_CRITICAL, msg );
01516         }
01517 
01518         if (days_left == 0) {
01519                 asprintf( &msg, "Certificate expires today (%s).\n", timestamp );
01520                 ssl_terminate( STATE_WARNING, msg );
01521         }
01522 
01523         asprintf( &msg, "Certificate will expire on %s.\n", timestamp );
01524         ssl_terminate( STATE_OK, msg );
01525 }
01526 #endif
01527 
01528 /* written by lauri alanko */
01529 static char *
01530 base64 (char *bin, int len)
01531 {
01532 
01533         char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
01534         int i = 0, j = 0;
01535 
01536         char BASE64_END = '=';
01537         char base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
01538 
01539         while (j < len - 2) {
01540                 buf[i++] = base64_table[bin[j] >> 2];
01541                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
01542                 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
01543                 buf[i++] = base64_table[bin[j + 2] & 63];
01544                 j += 3;
01545         }
01546 
01547         switch (len - j) {
01548         case 1:
01549                 buf[i++] = base64_table[bin[j] >> 2];
01550                 buf[i++] = base64_table[(bin[j] & 3) << 4];
01551                 buf[i++] = BASE64_END;
01552                 buf[i++] = BASE64_END;
01553                 break;
01554         case 2:
01555                 buf[i++] = base64_table[bin[j] >> 2];
01556                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
01557                 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
01558                 buf[i++] = BASE64_END;
01559                 break;
01560         case 0:
01561                 break;
01562         }
01563 
01564         buf[i] = '\0';
01565         return buf;
01566 }
01567