check_http-with-client-certificate.c 60 KB

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