ソースを参照

Merge branch 'ssl_changes' into nrpe-2-16-RC1. See README.SSL.md
for detailed information about these changes to SSL/TLS processing.

John C. Frickson 10 年 前
コミット
09946ed574
11 ファイル変更884 行追加340 行削除
  1. 0 34
      README.SSL
  2. 120 0
      README.SSL.md
  3. 70 97
      README.md
  4. 29 47
      SECURITY.md
  5. 1 1
      configure
  6. 1 1
      configure.ac
  7. 2 5
      include/utils.h
  8. 59 0
      sample-config/nrpe.cfg.in
  9. 263 108
      src/check_nrpe.c
  10. 293 47
      src/nrpe.c
  11. 46 0
      src/utils.c

+ 0 - 34
README.SSL

@@ -1,34 +0,0 @@
-NRPE With SSL/TLS
-
-NRPE now has the option for Encrypting Network traffic using
-SSL/TLS from openssl. 
-
-The Encryption is done using a set encryption routine of 
-AES-256 Bit Encryption using SHA and Anon-DH. This encrypts
-all traffic using the NRPE sockets from the client to the server.
-
-Since we are using Anon-DH this allows for an encrypted 
-SSL/TLS Connection without using pre-generated keys or 
-certificates. The key generation information used by the 
-program to dynaically create keys on daemon startup can be found
-in the dh.h file in the nrpe src directory. This file was created
-using the command:
-
-openssl dhparam -C 512 
-
-which outputs the C code in dh.h. For your own security you can replace
-that file with your own dhparam generated code.
-
-As of this time you will need to have the latest greatest version of
-OpenSSL (tested against version 0.9.7a) since not all versions have
-the AES algorythm in them.
-
-I am not aware that at this time this code is restricted under export 
-restrictions but I leave that verification process up to you.
-
-Thoughts and suggestions are welcome and I can be reached on the
-Nagios and NagiosPlug Mailing Lists.
-
-	- Derrick
-
-

+ 120 - 0
README.SSL.md

@@ -0,0 +1,120 @@
+NRPE With SSL/TLS
+=================
+
+NRPE has had basic support for SSL/TLS for some time now, but it was
+severely lacking. It only allowed anonymous Diffie Hellman (ADH) key
+exchange, it used a fixed 512-bit key (generated at `./configure`
+time and extremely insecure) and originally allowed SSLv2. In 2004,
+SSLv2 and SSLv3 support was disabled.
+
+nrpe and check_nrpe have been updated to offer much more secure
+encryption and more options. And the updates are done in a backward-
+compatible way, allowing you to migrate to the newer versions
+without having to do it all at once, and possibly miss updating some
+machines, causing lost reporting.
+
+The changes to the NRPE daemon are outlined below first, followed by
+the changes to the check_nrpe client.
+
+
+------------------------------------------
+####CHANGES IN THE CURRENT VERSION OF NRPE
+------------------------------------------
+
+Running `./configure` will now create a 2048-bit DH key instead
+of the old 512-bit key. The most current versions of openSSL will
+still not allow it. In my testing, openSSL 1.0.1e allowed DH keys
+of 512 bits, and 1.0.1k would not allow 2048 bit keys. In addition
+we now call `SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE)` so a
+new key is generated on each connection, based on the 2048-bit
+key generated.
+
+The NRPE configuration file has added new SSL/TLS options. The
+defaults currently will allow old check_nrpe clients to continue to
+connect to the nrpe daemon, but can report on "old style" 
+connections, or enforce more secure communication as your migration
+progresses. The new options are in the "SSL/TLS OPTIONS" section of
+nrpe.cfg, about two-thirds of the way down.
+
+The `ssl_version` directive lets you set which versions of SSL/TLS
+you want to allow. SSLv2, SSLv3, TLSv1, TLSv1.1 and TLSv1.2 are
+allowed, or those litereals with a `+` after them (as in TLSv1.1+).
+Without the `+`, that version _only_ will be used. With the `+`,
+that version _or above_ will be used. openSSL will always negotiate
+the highest available allowed version available on both ends. This
+directive currently defaults to `TLSv1+`.
+
+The `ssl_use_adh` directive is **DEPRECATED**, even though it is new.
+Possible values are `0` to not allow ADH at all, `1` to allow ADH,
+and `2` to require ADH. The `2` should never be required, but it's
+there just in case it's needed, for whatever reason. `1` is currently
+the default, which allows older check_nrpe clients to connect using
+ADH. When all the clients are migrated to the newer version, it
+should be set to `0`. In an upcoming version of NRPE, ADH will no
+longer be allowed at all. Note that if you use a `2` here, NRPE will
+override any `ssl_cipher_list` entries (below) to *only* allow ADH.
+
+The `ssl_cipher_list` directive lets you specify which ciphers you
+want to allow. It currently defaults to `ALL:!MD5:@STRENGTH` but can
+take any value allowed by openSSL. In an upcoming version of NRPE, it
+will be changed to something more secure, something like
+`ALL:!aNULL:!eNULL:!SSLv2:!LOW:!EXP:!RC4:!MD5:@STRENGTH`. Note that
+if you have `ssl_use_adh=2`, this string will be overridden with
+`ADH` which only only allow ADH.
+
+The `ssl_cacert_file`, `ssl_cert_file` and `ssl_privatekey_file`
+directives are used to specify which *.pem files are to be used for
+Public-Key Encryption (PKE). Setting these will allow clients to use
+PKE to communicate with the server, similar to how the HTTPS
+protocol works.
+
+The `ssl_client_certs` directive specifies whether or not a client
+certificate will be requested when a client tries to connect. A value
+of `0` means the nrpe daemon will not ask for or require a client
+certificate. A `1` will cause it to ask for a client certificate, but
+not require one. A `2` will require the client to present a valid
+certificate. This currently defaults to `0`. If you want to use
+client certificates and are upgrading the clients over time, you can
+set this to `1` once many have been upgraded, then set to `2` to
+force the use of client certs. Note that the client certs _must_ be
+signed by the CA cert specified in the `ssl_cacert_file` directive.
+
+The `ssl_logging` directive allows you to log some additional data
+to syslog. OR (or add) values together to have more than one option
+enabled. Values are `0` for no additional logging (the default),
+`1` to log startup SSL/TLS parameters from the nrpe.cfg file, `2` to
+log the SSL/TLS version of connections, `4` to log which cipher is
+being used for the connection, `8` to log if client has a cert, and
+`16` to log details of client's certificate. `-1` will enable all.
+This can be especially helpful during client migration, so you can
+tell which clients have certificates, what SSL/TLS version is being
+used, and which ciphers are being used.
+
+
+------------------------------------------------
+####CHANGES IN THE CURRENT VERSION OF CHECK_NRPE
+------------------------------------------------
+
+The check_nrpe client has also been updated to provide more secure
+encryption and allow the use of client certificates. The command line
+has several new options, which are outlined below. Both the long and
+short arguments are presented.
+
+`--no-adh` or `-d` will disable the use of ADH. This option is **DEPRECATED**,
+even though it's new. It will be removed in a future version.
+
+`--ssl-version=<ver>` or `-S <ver>` specifies minimum SSL/TLS version
+to use. See the `ssl_version` directive above for possible values.
+
+`--cipher-list=<value.` or `-L <value>` determines which ciphers will
+and won't be allowed. See the `ssl_cipher_list` directive above.
+
+`--client-cert=<path>` or `-C <path>` specifies an optional client
+certificate to use. If this value is entered, the next one below is
+required.
+
+`--key-file=<path>` or `-K <path>` specifies the client certificate
+key file to use. This goes along with `--client-cert` above.
+
+`--ca-cert-file=<path>` or `-A <path>` specifies the CA certificate
+to use in order to validate the nrpe daemon's public key.

+ 70 - 97
README → README.md

@@ -1,15 +1,9 @@
------------
 NRPE README
------------
-
-
-** UPDATED DOCUMENTATION!
+===========
 
 For installation instructions and information on the design overview
 of the NRPE addon, please read the PDF documentation that is found in 
-this directory: NRPE.pdf
-
-
+this directory: `docs/NRPE.pdf`
 
 
 Purpose
@@ -23,46 +17,45 @@ Contents
 
 There are two pieces to this addon:
 
-  1) NRPE       - This program runs as a background process on the 
-                  remote host and processes command execution requests
-	          from the check_nrpe plugin on the Nagios host.
-		  Upon receiving a plugin request from an authorized
-                  host, it will execute the command line associated
-                  with the command name it received and send the
-                  program output and return code back to the 
-                  check_nrpe plugin
-
-  2) check_nrpe - This is a plugin that is run on the Nagios host
-                  and is used to contact the NRPE process on remote
-	          hosts.  The plugin requests that a plugin be
-                  executed on the remote host and wait for the NRPE
-                  process to execute the plugin and return the result.
-                  The plugin then uses the output and return code
-                  from the plugin execution on the remote host for
-                  its own output and return code.
+  1) **NRPE**       - This program runs as a background process on the 
+                      remote host and processes command execution requests
+                      from the check_nrpe plugin on the Nagios host.
+                      Upon receiving a plugin request from an authorized
+                      host, it will execute the command line associated
+                      with the command name it received and send the
+                      program output and return code back to the 
+                      check_nrpe plugin
+
+  2) **check_nrpe** - This is a plugin that is run on the Nagios host
+                      and is used to contact the NRPE process on remote
+                      hosts.  The plugin requests that a plugin be
+                      executed on the remote host and wait for the NRPE
+                      process to execute the plugin and return the result.
+                      The plugin then uses the output and return code
+                      from the plugin execution on the remote host for
+                      its own output and return code.
 
 
 Compiling
 ---------
 
 The code is very basic and may not work on your particular
-system without some tweaking.  I just haven't put a lot of effort
-into this addond.  Most Linux users should be able to compile
-NRPE and the check_nrpe plugin with the following commands...
+system without some tweaking. If you are having any problems
+compiling on your system, please let us know, hopefully with
+fixes. Most users should be able to compile NRPE and the
+check_nrpe plugin with the following commands...
 
-./configure
-make all
+	./configure
+	make all
 
-The binaries will be located in the src/ directory after you
-run 'make all' and will have to be installed manually somewhere
+The binaries will be located in the `src/` directory after you
+run `make all` and will have to be installed manually somewhere
 on your system.
 
-
-NOTE: Since the check_nrpe plugin and nrpe daemon run on different
+_NOTE: Since the check_nrpe plugin and nrpe daemon run on different
       machines (the plugin runs on the Nagios host and the daemon
       runs on the remote host), you will have to compile the nrpe
-      daemon on the target machine.
-
+      daemon on the target machine._
 
 
 Installing
@@ -70,21 +63,19 @@ Installing
 
 The check_nrpe plugin should be placed on the Nagios host along
 with your other plugins.  In most cases, this will be in the
-/usr/local/nagios/libexec directory.
+`/usr/local/nagios/libexec` directory.
 
-The nrpe program and the configuration file (nrpe.cfg) should
+The nrpe program and the configuration file `nrpe.cfg` should
 be placed somewhere on the remote host.  Note that you will also
 have to install some plugins on the remote host if you want to
 make much use of this addon.
 
 
-
 Configuring
 -----------
 
 Sample config files for the NRPE daemon are located in the
-sample-config/ subdirectory.
-
+`sample-config/` subdirectory.
 
 
 Running Under INETD or XINETD
@@ -93,49 +84,46 @@ Running Under INETD or XINETD
 If you plan on running nrpe under inetd or xinetd and making use
 of TCP wrappers, you need to do the following things:
 
-
-
-1) Add a line to your /etc/services file as follows (modify the port
+1) Add a line to your `/etc/services` file as follows (modify the port
    number as you see fit)
 
 	nrpe            5666/tcp	# NRPE
 
-
-
 2) Add entries for the NRPE daemon to either your inetd or xinetd
    configuration files.  Which one your use will depend on which
    superserver is installed on your system.  Both methods are described
-   below.  NOTE: If you run nrpe under inetd or xinetd, the server_port
+   below.
+
+   _NOTE: If you run nrpe under inetd or xinetd, the server_port
    and allowed_hosts variables in the nrpe configuration file are
-   ignored.
+   ignored._
 
 
-   ***** INETD *****
-   If your system uses the inetd superserver WITH tcpwrappers, add an
-   entry to /etc/inetd.conf as follows:
+   #### INETD ####
+   If your system uses the inetd superserver **with** tcpwrappers, add an
+   entry to `/etc/inetd.conf` as follows:
 
 	nrpe 	stream 	tcp 	nowait 	<user> /usr/sbin/tcpd <nrpebin> -c <nrpecfg> --inetd
 
-   If your system uses the inetd superserver WITHOUT tcpwrappers, add an
-   entry to /etc/inetd.conf as follows:
+   If your system uses the inetd superserver **without** tcpwrappers,
+   add an entry to `/etc/inetd.conf` as follows:
 
 	nrpe 	stream 	tcp 	nowait 	<user> <nrpebin> -c <nrpecfg> --inetd
 
 
-   - Replace <user> with the name of the user that the nrpe server should run as.
-     	Example: nagios
-   - Replace <nrpebin> with the path to the nrpe binary on your system.
-	Example: /usr/local/nagios/nrpe
-   - Replace <nrpecfg> with the path to the nrpe config file on your system.
-	Example: /usr/local/nagios/nrpe.cfg
+   - Replace `<user>` with the name of the user that the nrpe server should run as.
+     Example: `nagios`
+   - Replace `<nrpebin>` with the path to the nrpe binary on your system.
+     Example: `/usr/local/nagios/nrpe`
+   - Replace `<nrpecfg>` with the path to the nrpe config file on your system.
+     Example: `/usr/local/nagios/nrpe.cfg`
 
 
-   ***** XINETD *****
+   #### XINETD ####
    If your system uses xinetd instead of inetd, you'll probably
-   want to create a file called 'nrpe' in your /etc/xinetd.d
+   want to create a file called `nrpe` in your `/etc/xinetd.d`
    directory that contains the following entries:
 
-
 	# default: on
 	# description: NRPE
 	service nrpe
@@ -151,16 +139,13 @@ of TCP wrappers, you need to do the following things:
 		only_from       = <ipaddress1> <ipaddress2> ...
 	}
 
-
-   - Replace <user> with the name of the user that the nrpe server should run as.
-   - Replace <nrpebin> with the path to the nrpe binary on your system.
-   - Replace <nrpecfg> with the path to the nrpe config file on your system.
-   - Replace the <ipaddress> fields with the IP addresses of hosts which
+   - Replace `<user>` with the name of the user that the nrpe server should run as.
+   - Replace `<nrpebin>` with the path to the nrpe binary on your system.
+   - Replace `<nrpecfg>` with the path to the nrpe config file on your system.
+   - Replace the `<ipaddress>` fields with the IP addresses of hosts which
      are allowed to connect to the NRPE daemon.  This only works if xinetd was
      compiled with support for tcpwrappers.
 
-
-
 3) Restart inetd or xinetd will the following command (pick the
    on that is appropriate for your system:
 
@@ -172,50 +157,44 @@ of TCP wrappers, you need to do the following things:
 
 	kill -HUP `cat /var/run/inet.pid`
 
-
-
-4) Add entries to your /etc/hosts.allow and /etc/hosts.deny
+4) Add entries to your `/etc/hosts.allow` and `/etc/hosts.deny`
    file to enable TCP wrapper protection for the nrpe service.
    This is optional, although highly recommended.
 
 
-
-
 Configuring Things On The Nagios Host
 ---------------------------------------
 
 Examples for configuring the nrpe daemon are found in the sample
-nrpe.cfg file included in this distribution.  That config file
+`nrpe.cfg` file included in this distribution.  That config file
 resides on the remote host(s) along with the nrpe daemon.  The
 check_nrpe plugin gets installed on the Nagios host.  In order
-to use the check_nrpe plugin from within Nagios, you'll have
+to use the check_nrpe plugin from within Nagios, you will have
 to define a few things in the host config file.  An example
 command definition for the check_nrpe plugin would look like this:
 
-define command{
-	command_name	check_nrpe
-	command_line	/usr/local/nagios/libexec/check_nrpe -H $HOSTADDRESS$ -c $ARG1$
-	}
+    define command{
+        command_name	check_nrpe
+        command_line	/usr/local/nagios/libexec/check_nrpe -H $HOSTADDRESS$ -c $ARG1$
+        }
 
 In any service definitions that use the nrpe plugin/daemon to
 get their results, you would set the service check command portion
 of the definition to something like this (sample service definition
 is simplified for this example):
 
-define service{
-	host_name		someremotehost
-	service_description	someremoteservice
-	check_command		check_nrpe!yourcommand
-	... etc ...
-	}
+    define service{
+        host_name		someremotehost
+        service_description	someremoteservice
+        check_command		check_nrpe!yourcommand
+        ... etc ...
+        }
 
-where "yourcommand" is a name of a command that you define in 
+where `yourcommand` is a name of a command that you define in 
 your nrpe.cfg file on the remote host (see the docs in the 
 sample nrpe.cfg file for more information).
 
 
-
-
 Questions?
 ----------
 
@@ -224,11 +203,5 @@ working, first try searching the nagios-users mailing list archives.
 Details on searching the list archives can be found at 
 http://www.nagios.org
 
-If all else fails, you can email me and I'll try and respond as
-soon as I get a chance.
-
-	-- Ethan Galstad (nagios@nagios.org)
-
-
-
-
+If you don't find an answer there, post a message in the Nagios
+Plugin Development forum at https://support.nagios.com/forum/viewforum.php?f=35

+ 29 - 47
SECURITY → SECURITY.md

@@ -1,25 +1,20 @@
-********************
 NRPE SECURITY README
-********************
+====================
 
+---
 
-TCP WRAPPER SUPPORT
-===================
+### TCP WRAPPER SUPPORT ###
 
-NRPE 2.x includes native support for TCP wrappers.  The older
-host access list directive was removed from the config file.
-Make sure your system supports TCP wrappers before running NRPE.
-Once you compile NRPE you can check to see if it has wrapper
-support built in by running the daemon from the command line
-without any arguments like this:
-
-	./nrpe --help
 
+NRPE 2.x includes native support for TCP wrappers. Once you
+compile NRPE you can check to see if it has wrapper support
+built in by running the daemon from the command line without
+any arguments like this:
 
+	./nrpe --help
 
 
-COMMAND ARGUMENTS
-=================
+#### COMMAND ARGUMENTS ####
 
 NRPE 2.0 includes the ability for clients to supply arguments to
 commands which should be run.  Please note that this feature
@@ -27,60 +22,55 @@ should be considered a security risk, and you should only use
 it if you know what you're doing!
 
 
-BASH COMMAND SUBSTITUTION
--------------------------
+#### BASH COMMAND SUBSTITUTION ####
 
 Even with the metacharacter restrictions below, if command arguments 
 are enabled, it is still possible to send bash command substitions 
-in the form $(...) as an agrument. This is explicity disabled by 
+in the form `$(...)` as an agrument. This is explicity disabled by 
 default, but can be enabled by a configure-time option and a
-configuration file option. Enabling this option is VERY RISKY and 
-its use is HIGHLY DISCOURAGED.
+configuration file option. Enabling this option is **VERY RISKY**
+and its use is **HIGHLY DISCOURAGED**.
 
 
-ENABLING ARGUMENTS
-------------------
+#### ENABLING ARGUMENTS ####
 
 To enable support for command argument in the daemon, you must
 do two things:
 
-   1.  Run the configure script with the --enable-command-args 
+   1.  Run the configure script with the `--enable-command-args`
        option
 
-   2.  Set the 'dont_blame_nrpe' directive in the NRPE config
-       file to 1.
+   2.  Set the `dont_blame_nrpe` directive in the NRPE config
+       file to `1`.
 
 
-ENABLING BASH COMMAND SUBSTITUTION
-----------------------------------
+#### ENABLING BASH COMMAND SUBSTITUTION ####
 
 To enable support for arguments containing bash command substitions, 
 you must do two things:
 
    1.  Enable arguments as described above
 
-   2.  Include the --enable-bash-command-substitution configure
+   2.  Include the `--enable-bash-command-substitution` configure
        option when running the configure script
 
-   3.  Set the 'allow_bash_command_substitutions' directive in the 
-       NRPE config file to 1.
+   3.  Set the `allow_bash_command_substitutions` directive in the 
+       NRPE config file to `1`.
 
 
-ILLEGAL METACHARS
------------------
+#### ILLEGAL METACHARS ####
 
 To help prevent some nasty things from being done by evil 
 clients, the following metacharacters are not allowed
 in client command arguments:
 
-   | ` & > < ' " \ [ ] { } ; !
+	| ` & > < ' " \ [ ] { } ; !
 
-Any client request which contains the abovementioned metachars
+Any client request which contains the above mentioned metachars
 is discarded.
 
 
-USER/GROUP RESTRICTIONS
------------------------
+#### USER/GROUP RESTRICTIONS ####
 
 The NRPE daemon cannot be run with (effective) root user/group
 privileges.  You must run the daemon with an account that does
@@ -89,21 +79,19 @@ directives in the config file to specify which user/group the
 daemon should run as.
 
 
-ENCRYPTION
-----------
+#### ENCRYPTION ####
 
 If you do enable support for command arguments in the NRPE daemon,
 make sure that you encrypt communications either by using:
 
    1.  Stunnel (see http://www.stunnel.org for more info)
-   2.  Native SSL support
+   2.  Native SSL support (See the `README.SSL.md` file for more info)
 
-Do NOT assume that just because the daemon is behind a firewall
+*Do NOT* assume that just because the daemon is behind a firewall
 that you are safe!  Always encrypt NRPE traffic!
 
 
-USING ARGUMENTS
----------------
+#### USING ARGUMENTS ####
 
 How do you use command arguments?  Well, lets say you define a
 command in the NRPE config file that looks like this:
@@ -123,9 +111,3 @@ like this:
 
 You can supply up to 16 arguments to be passed to the command
 for substitution in $ARG$ macros ($ARG1$ - $ARG16$).
-
-
-
-
-    -- Ethan Galstad (nagios@nagios.org)
-

+ 1 - 1
configure

@@ -4945,7 +4945,7 @@ _ACEOF
 				sslbin=$ssldir/bin/openssl
 			fi
 			# awk to strip off meta data at bottom of dhparam output
-			$sslbin dhparam -C 512 | awk '/^-----/ {exit} {print}' > include/dh.h
+			$sslbin dhparam -C 2048 | awk '/^-----/ {exit} {print}' > include/dh.h
 		fi
 	fi
 

+ 1 - 1
configure.ac

@@ -345,7 +345,7 @@ if test x$check_for_ssl = xyes; then
 				sslbin=$ssldir/bin/openssl
 			fi
 			# awk to strip off meta data at bottom of dhparam output
-			$sslbin dhparam -C 512 | awk '/^-----/ {exit} {print}' > include/dh.h
+			$sslbin dhparam -C 2048 | awk '/^-----/ {exit} {print}' > include/dh.h
 		fi
 	fi
 

+ 2 - 5
include/utils.h

@@ -53,11 +53,8 @@ int recvall(int,char *,int *,int);
 
 char *my_strsep(char **,const char *);
 
+int b64_decode(unsigned char *encoded);
+
 void display_license(void);
 
 #endif
-
-
-
-
-

+ 59 - 0
sample-config/nrpe.cfg.in

@@ -186,6 +186,65 @@ connection_timeout=300
 
 
 
+# SSL/TLS OPTIONS
+# These directives allow you to specify how to use SSL/TLS.
+
+# SSL VERSION
+# This can be any of: SSLv2 (only use SSLv2), SSLv2+ (use any version),
+#        SSLv3 (only use SSLv3), SSLv3+ (use SSLv3 or above), TLSv1 (only use
+#        TLSv1), TLSv1+ (use TLSv1 or above), TLSv1.1 (only use TLSv1.1),
+#        TLSv1.1+ (use TLSv1.1 or above), TLSv1.2 (only use TLSv1.2),
+#        TLSv1.2+ (use TLSv1.2 or above)
+# If an "or above" version is used, the best will be negotiated. So if both
+# ends are able to do TLSv1.2 and use specify SSLv2, you will get TLSv1.2.
+
+#ssl_version=TLSv1+
+
+# SSL USE ADH
+# This is for backward compatibility and is DEPRECATED. Set to 1 to enable
+# ADH or 2 to require ADH. 1 is currently the default but will be changed
+# in a later version.
+
+#ssl_use_adh=1
+
+# SSL CIPHER LIST
+# This lists which ciphers can be used. For backward compatibility, this
+# defaults to 'ssl_cipher_list=ALL:!MD5:@STRENGTH' in this version but
+# will be changed to something something else in a later version of NRPE.
+
+#ssl_cipher_list=ALL:!MD5:@STRENGTH
+
+# SSL Certificate and Private Key Files
+
+#ssl_cacert_file=@sharedstatedir@/ssl/ca-cert.pem
+#ssl_cert_file=@sharedstatedir@/ssl/nagios-cert.pem
+#ssl_privatekey_file=@sharedstatedir@/ssl/nagios-key.pem
+
+
+# SSL CLIENT CERTS
+# This options determines client certificate usage.
+# Values: 0 = Don't ask for or require client certificates
+#         1 = Ask for client certificates
+#         2 = Require client certificates
+
+#ssl_client_certs=0
+
+# SSL LOGGING
+# This option determines which SSL messages are send to syslog. OR values
+# together to specify multiple options.
+
+# Values: 0  = No additional logging (default)
+#         1  = Log startup SSL/TLS parameters
+#         2  = Log SSL/TLS version of connections
+#         4  = Log which cipher is being used for the connection
+#         8  = Log if client has a certificate
+#         16 = Log details of client's certificate if it has one
+#         -1 = All of the above
+
+#ssl_logging=0
+
+
+
 # INCLUDE CONFIG FILE
 # This directive allows you to include definitions from an external config file.
 

+ 263 - 108
src/check_nrpe.c

@@ -53,6 +53,23 @@ int use_ssl=TRUE;
 int use_ssl=FALSE;
 #endif
 
+/* SSL/TLS parameters */
+typedef enum _SSL_VER { SSLv2 = 1, SSLv2_plus, SSLv3, SSLv3_plus, TLSv1,
+					TLSv1_plus, TLSv1_1, TLSv1_1_plus, TLSv1_2, TLSv1_2_plus
+				} SslVer;
+typedef enum _CLNT_CERTS {
+					Ask_For_Cert = 1, Require_Cert = 2, Log_Certs = 4
+				} ClntCerts;
+struct _SSL_PARMS {
+	char	*cert_file;
+	char	*cacert_file;
+	char	*privatekey_file;
+	char    cipher_list[MAX_FILENAME_LENGTH];
+	SslVer	ssl_min_ver;
+	int		allowDH;
+	int		client_certs;
+} sslprm = { NULL, NULL, NULL, "ALL:!MD5:@STRENGTH", TLSv1_plus, TRUE, 0 };
+
 
 int process_arguments(int,char **);
 void alarm_handler(int);
@@ -65,7 +82,7 @@ int main(int argc, char **argv){
         u_int32_t packet_crc32;
         u_int32_t calculated_crc32;
 	int16_t result;
-	int rc;
+	int rc, ssl_opts = SSL_OP_ALL, vrfy;
 	packet send_packet;
 	packet receive_packet;
 	int bytes_to_send;
@@ -87,28 +104,44 @@ int main(int argc, char **argv){
 		printf("Last Modified: %s\n",MODIFICATION_DATE);
 		printf("License: GPL v2 with exemptions (-l for more info)\n");
 #ifdef HAVE_SSL
-		printf("SSL/TLS Available: Anonymous DH Mode, OpenSSL 0.9.6 or higher required\n");
+		printf("SSL/TLS Available: OpenSSL 0.9.6 or higher required\n");
 #endif
 		printf("\n");
 	        }
 
 	if(result!=OK || show_help==TRUE){
 
-		printf("Usage: check_nrpe -H <host> [ -b <bindaddr> ] [-4] [-6] [-n] [-u] [-p <port>] [-t <timeout>] [-c <command>] [-a <arglist...>]\n");
+		printf("Usage: check_nrpe -H <host> [-4] [-6] [-n] [-u] [-V] [-l] [-d]\n"
+			"       [-S <ssl version>  [-L <cipherlist>] [-C <clientcert>]\n"
+			"       [-K <key>] [-A <ca-certificate>] [-b <bindaddr>] [-p <port>]\n"
+			"       [-t <timeout>] [-c <command>] [-a <arglist...>]\n");
 		printf("\n");
 		printf("Options:\n");
-		printf(" -n         = Do no use SSL\n");
-		printf(" -u         = Make socket timeouts return an UNKNOWN state instead of CRITICAL\n");
-		printf(" <host>     = The address of the host running the NRPE daemon\n");
-		printf(" <bindaddr> = bind to local address\n");
-		printf(" -4         = bind to ipv4 only\n");
-		printf(" -6         = bind to ipv6 only\n");
-		printf(" [port]     = The port on which the daemon is running (default=%d)\n",DEFAULT_SERVER_PORT);
-		printf(" [timeout]  = Number of seconds before connection times out (default=%d)\n",DEFAULT_SOCKET_TIMEOUT);
-		printf(" [command]  = The name of the command that the remote daemon should run\n");
-		printf(" [arglist]  = Optional arguments that should be passed to the command.  Multiple\n");
-		printf("              arguments should be separated by a space.  If provided, this must be\n");
-		printf("              the last option supplied on the command line.\n");
+		printf(" <host>       = The address of the host running the NRPE daemon\n");
+		printf(" -4           = bind to ipv4 only\n");
+		printf(" -6           = bind to ipv6 only\n");
+		printf(" -n           = Do no use SSL\n");
+		printf(" -u           = Make socket timeouts return UNKNOWN state instead of CRITICAL\n");
+		printf(" -V           = Show version\n");
+		printf(" -l           = Show license\n");
+		printf(" -d           = Don't use Anonymous Diffie Hellman\n");
+		printf("                (This will be the default in a future release.)\n");
+		printf(" <bindaddr>   = bind to local address\n");
+		printf(" <ssl ver>    = The SSL/TLS version to use. Can be any one of: SSLv2 (only),\n");
+		printf("                SSLv2+ (or above), SSLv3 (only), SSLv3+ (or above),\n");
+		printf("                TLSv1 (only), TLSv1+ (or above DEFAULT), TLSv1.1 (only),\n");
+		printf("                TLSv1.1+ (or above), TLSv1.2 (only), TLSv1.2+ (or above)\n");
+		printf(" <cipherlist> = The list of SSL ciphers to use (currently defaults\n");
+		printf("                to \"ALL:!MD5:@STRENGTH\". WILL change in a future release.)\n");
+		printf(" <clientcert> = The client certificate to use for PKI\n");
+		printf(" <key>        = The private key to use with the client certificate\n");
+		printf(" <ca-cert>    = The CA certificate to use for PKI\n");
+		printf(" [port]       = The port on which the daemon is running (default=%d)\n",DEFAULT_SERVER_PORT);
+		printf(" [timeout]    = Number of seconds before connection times out (default=%d)\n",DEFAULT_SOCKET_TIMEOUT);
+		printf(" [command]    = The name of the command that the remote daemon should run\n");
+		printf(" [arglist]    = Optional arguments that should be passed to the command,\n");
+		printf("                separated by a space.  If provided, this must be the last\n");
+		printf("                option supplied on the command line.\n");
 		printf("\n");
 		printf("Note:\n");
 		printf("This plugin requires that you have the NRPE daemon running on the remote host.\n");
@@ -133,20 +166,72 @@ int main(int argc, char **argv){
 
 #ifdef HAVE_SSL
 	/* initialize SSL */
-	if(use_ssl==TRUE){
-		SSL_library_init();
-		SSLeay_add_ssl_algorithms();
-		meth=SSLv23_client_method();
+	if(use_ssl==TRUE) {
 		SSL_load_error_strings();
-		if((ctx=SSL_CTX_new(meth))==NULL){
+		SSL_library_init();
+		meth = SSLv23_client_method();
+
+#ifndef OPENSSL_NO_SSL2
+		if (sslprm.ssl_min_ver == SSLv2)
+			meth = SSLv2_server_method();
+#endif
+#ifndef OPENSSL_NO_SSL3
+		if (sslprm.ssl_min_ver == SSLv3)
+			meth = SSLv3_server_method();
+#endif
+		if (sslprm.ssl_min_ver == TLSv1)
+			meth = TLSv1_server_method();
+		if (sslprm.ssl_min_ver == TLSv1_1)
+			meth = TLSv1_1_server_method();
+		if (sslprm.ssl_min_ver == TLSv1_2)
+			meth = TLSv1_2_server_method();
+
+		if ((ctx = SSL_CTX_new(meth)) == NULL) {
 			printf("CHECK_NRPE: Error - could not create SSL context.\n");
 			exit(STATE_CRITICAL);
-		        }
+        }
+
+		if (sslprm.ssl_min_ver >= SSLv3) {
+			ssl_opts |= SSL_OP_NO_SSLv2;
+			if (sslprm.ssl_min_ver >= TLSv1)
+				ssl_opts |= SSL_OP_NO_SSLv3;
+		}
+		SSL_CTX_set_options(ctx, ssl_opts);
 
-		/* ADDED 01/19/2004 */
-		/* use only TLSv1 protocol */
-		SSL_CTX_set_options(ctx,SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
-                }
+		if (sslprm.cert_file != NULL && sslprm.privatekey_file != NULL) {
+			if (!SSL_CTX_use_certificate_file(ctx, sslprm.cert_file, SSL_FILETYPE_PEM)) {
+				SSL_CTX_free(ctx);
+				syslog(LOG_ERR, "Error: could not use certificate file '%s'.\n", sslprm.cert_file);
+				exit(STATE_CRITICAL);
+			}
+			if (!SSL_CTX_use_PrivateKey_file(ctx, sslprm.privatekey_file, SSL_FILETYPE_PEM)) {
+				SSL_CTX_free(ctx);
+				syslog(LOG_ERR, "Error: could not use private key file '%s'.\n", sslprm.privatekey_file);
+				exit(STATE_CRITICAL);
+			}
+		}
+
+		if (sslprm.cacert_file != NULL) {
+			if (!SSL_CTX_load_verify_locations(ctx, sslprm.cacert_file, NULL)) {
+				SSL_CTX_free(ctx);
+				syslog(LOG_ERR, "Error: could not use CA certificate '%s'.\n", sslprm.cacert_file);
+				exit(STATE_CRITICAL);
+			}
+			vrfy = SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+			SSL_CTX_set_verify(ctx, vrfy, NULL);
+		}
+
+		if (!sslprm.allowDH) {
+			if (strlen(sslprm.cipher_list) < sizeof(sslprm.cipher_list) - 6)
+				strcat(sslprm.cipher_list, ":!ADH");
+		}
+
+		if (SSL_CTX_set_cipher_list(ctx, sslprm.cipher_list) == 0) {
+			SSL_CTX_free(ctx);
+			syslog(LOG_ERR, "Error: Could not set SSL/TLS cipher list: %s", sslprm.cipher_list);
+			exit(STATE_CRITICAL);
+		}
+	}
 #endif
 
 	/* initialize alarm signal handling */
@@ -174,44 +259,47 @@ int main(int argc, char **argv){
 
 #ifdef HAVE_SSL
 	/* do SSL handshake */
-	if(result==STATE_OK && use_ssl==TRUE){
-		if((ssl=SSL_new(ctx))!=NULL){
-			SSL_CTX_set_cipher_list(ctx,"ADH");
-			SSL_set_fd(ssl,sd);
-			if((rc=SSL_connect(ssl))!=1){
+	if (result == STATE_OK && use_ssl==TRUE) {
+		if ((ssl = SSL_new(ctx)) != NULL) {
+			X509	*peer;
+			char	peer_cn[256];
+
+			SSL_set_fd(ssl, sd);
+			if ((rc = SSL_connect(ssl)) != 1) {
 				printf("CHECK_NRPE: Error - Could not complete SSL handshake.\n");
 #ifdef DEBUG
-				printf("SSL_connect=%d\n",rc);
+				printf("SSL_connect=%d\n", rc);
 				/*
-				rc=SSL_get_error(ssl,rc);
-				printf("SSL_get_error=%d\n",rc);
-				printf("ERR_get_error=%lu\n",ERR_get_error());
-				printf("%s\n",ERR_error_string(rc,NULL));
+				rc = SSL_get_error(ssl, rc);
+				printf("SSL_get_error=%d\n", rc);
+				printf("ERR_get_error=%lu\n", ERR_get_error());
+				printf("%s\n",ERR_error_string(rc, NULL));
 				*/
 				ERR_print_errors_fp(stdout);
 #endif
 				result=STATE_CRITICAL;
-			        }
-		        }
-		else{
+			}
+
+		} else {
+
 			printf("CHECK_NRPE: Error - Could not create SSL connection structure.\n");
 			result=STATE_CRITICAL;
-		        }
+		}
 
 		/* bail if we had errors */
-		if(result!=STATE_OK){
+		if (result != STATE_OK) {
 			SSL_CTX_free(ctx);
 			close(sd);
 			exit(result);
-		        }
-	        }
+        }
+	}
 #endif
 
 	/* we're connected and ready to go */
 	if(result==STATE_OK){
 
 		/* clear the packet buffer */
-		bzero(&send_packet,sizeof(send_packet));
+		memset(&send_packet, 0, sizeof(send_packet));
 
 		/* fill the packet with semi-random data */
 		randomize_buffer((char *)&send_packet,sizeof(send_packet));
@@ -271,22 +359,22 @@ int main(int argc, char **argv){
 		graceful_close(sd,1000);
 
 		/* recv() error */
-		if(rc<0){
+		if (rc < 0) {
 			printf("CHECK_NRPE: Error receiving data from daemon.\n");
 			return STATE_UNKNOWN;
-		        }
+		}
 
 		/* server disconnected */
-		else if(rc==0){
+		else if (rc == 0) {
 			printf("CHECK_NRPE: Received 0 bytes from daemon.  Check the remote server logs for error messages.\n");
 			return STATE_UNKNOWN;
-		        }
+		}
 
 		/* receive underflow */
-		else if(bytes_to_recv<sizeof(receive_packet)){
-			printf("CHECK_NRPE: Receive underflow - only %d bytes received (%d expected).\n",bytes_to_recv,sizeof(receive_packet));
+		else if(bytes_to_recv<sizeof(receive_packet)) {
+			printf("CHECK_NRPE: Receive underflow - only %d bytes received (%ld expected).\n", bytes_to_recv, sizeof(receive_packet));
 			return STATE_UNKNOWN;
-		        }
+		}
 
 		
 		/***** DECRYPT RESPONSE *****/
@@ -337,125 +425,192 @@ int main(int argc, char **argv){
 
 
 /* process command line arguments */
-int process_arguments(int argc, char **argv){
+int process_arguments(int argc, char **argv)
+{
 	char optchars[MAX_INPUT_BUFFER];
-	int argindex=0;
-	int c=1;
-	int i=1;
+	int argindex = 0;
+	int c = 1;
+	int i = 1;
+	int pskfd;
+	struct stat st;
 
 #ifdef HAVE_GETOPT_LONG
-	int option_index=0;
-	static struct option long_options[]={
-		{"host", required_argument, 0, 'H'},
-		{"bind", required_argument, 0, 'b'},
-		{"command", required_argument, 0, 'c'},
-		{"args", required_argument, 0, 'a'},
-		{"no-ssl", no_argument, 0, 'n'},
-		{"unknown-timeout", no_argument, 0, 'u'},
-		{"ipv4", no_argument, 0, '4'},
-		{"ipv6", no_argument, 0, '6'},
-		{"timeout", required_argument, 0, 't'},
-		{"port", required_argument, 0, 'p'},
-		{"help", no_argument, 0, 'h'},
-		{"license", no_argument, 0, 'l'},
-		{0, 0, 0, 0}
-                };
+	int option_index = 0;
+	static struct option long_options[] = {
+		{ "host",			required_argument,	0, 'H'},
+		{ "bind",			required_argument,	0, 'b'},
+		{ "command",		required_argument,	0, 'c'},
+		{ "args",			required_argument,	0, 'a'},
+		{ "no-ssl",			no_argument,		0, 'n'},
+		{ "unknown-timeout",no_argument,		0, 'u'},
+		{ "ipv4",			no_argument,		0, '4'},
+		{ "ipv6",			no_argument,		0, '6'},
+		{ "no-adh",			no_argument,		0, 'd'},
+		{ "ssl-version",	required_argument,	0, 'S'},
+		{ "cipher-list",	required_argument,	0, 'L'},
+		{ "client-cert",	required_argument,	0, 'C'},
+		{ "key-file",		required_argument,	0, 'K'},
+		{ "ca-cert-file",	required_argument,	0, 'A'},
+		{ "timeout",		required_argument,	0, 't'},
+		{ "port",			required_argument,	0, 'p'},
+		{ "help",			no_argument,		0, 'h'},
+		{ "license",		no_argument,		0, 'l'},
+		{ 0, 0, 0, 0}
+	};
 #endif
 
 	/* no options were supplied */
-	if(argc<2)
+	if (argc < 2)
 		return ERROR;
 
-	snprintf(optchars,MAX_INPUT_BUFFER,"H:b:c:a:t:p:nu46hl");
+	snprintf(optchars, MAX_INPUT_BUFFER, "H:b:c:a:t:p:S:L:C:K:A:46dhlnuV");
 
-	while(1){
+	while(1) {
 #ifdef HAVE_GETOPT_LONG
-		c=getopt_long(argc,argv,optchars,long_options,&option_index);
+		c = getopt_long(argc, argv, optchars, long_options, &option_index);
 #else
-		c=getopt(argc,argv,optchars);
+		c = getopt(argc, argv, optchars);
 #endif
-		if(c==-1 || c==EOF || argindex > 0)
+		if (c == -1 || c == EOF || argindex > 0)
 			break;
 
 		/* process all arguments */
-		switch(c){
+		switch(c) {
 
 		case '?':
 		case 'h':
-			show_help=TRUE;
+			show_help = TRUE;
 			break;
+
 		case 'b':
-			bind_address=strdup(optarg);
+			bind_address = strdup(optarg);
 			break;
+
 		case 'V':
-			show_version=TRUE;
+			show_version = TRUE;
 			break;
+
 		case 'l':
-			show_license=TRUE;
+			show_license = TRUE;
 			break;
+
 		case 't':
-			socket_timeout=atoi(optarg);
-			if(socket_timeout<=0)
+			socket_timeout = atoi(optarg);
+			if(socket_timeout <= 0)
 				return ERROR;
 			break;
+
 		case 'p':
-			server_port=atoi(optarg);
-			if(server_port<=0)
+			server_port = atoi(optarg);
+			if(server_port <= 0)
 				return ERROR;
 			break;
+
 		case 'H':
-			server_name=strdup(optarg);
+			server_name = strdup(optarg);
 			break;
+
 		case 'c':
-			command_name=strdup(optarg);
+			command_name = strdup(optarg);
 			break;
+
 		case 'a':
-			argindex=optind;
+			argindex = optind;
 			break;
+
 		case 'n':
-			use_ssl=FALSE;
+			use_ssl = FALSE;
 			break;
+
 		case 'u':
-			timeout_return_code=STATE_UNKNOWN;
+			timeout_return_code = STATE_UNKNOWN;
 			break;
+
 		case '4':
-			address_family=AF_INET;
+			address_family = AF_INET;
 			break;
+
 		case '6':
-			address_family=AF_INET6;
+			address_family = AF_INET6;
+			break;
+
+		case 'd':
+			sslprm.allowDH = FALSE;
+			break;
+
+		case 'A':
+			sslprm.cacert_file = strdup(optarg);
+			break;
+
+		case 'C':
+			sslprm.cert_file = strdup(optarg);
+			break;
+
+		case 'K':
+			sslprm.privatekey_file = strdup(optarg);
+			break;
+
+		case 'S':
+			if (!strcmp(optarg, "SSLv2"))
+				sslprm.ssl_min_ver = SSLv2;
+			else if (!strcmp(optarg, "SSLv2+"))
+				sslprm.ssl_min_ver = SSLv2_plus;
+			else if (!strcmp(optarg, "SSLv3"))
+				sslprm.ssl_min_ver = SSLv3;
+			else if (!strcmp(optarg, "SSLv3+"))
+				sslprm.ssl_min_ver = SSLv3_plus;
+			else if (!strcmp(optarg, "TLSv1"))
+				sslprm.ssl_min_ver = TLSv1;
+			else if (!strcmp(optarg, "TLSv1+"))
+				sslprm.ssl_min_ver = TLSv1_plus;
+			else if (!strcmp(optarg, "TLSv1.1"))
+				sslprm.ssl_min_ver = TLSv1_1;
+			else if (!strcmp(optarg, "TLSv1.1+"))
+				sslprm.ssl_min_ver = TLSv1_1_plus;
+			else if (!strcmp(optarg, "TLSv1.2"))
+				sslprm.ssl_min_ver = TLSv1_2;
+			else if (!strcmp(optarg, "TLSv1.2+"))
+				sslprm.ssl_min_ver = TLSv1_2_plus;
+			else
+				return ERROR;
+			break;
+
+		case 'L':
+			strncpy(sslprm.cipher_list, optarg, sizeof(sslprm.cipher_list) - 1);
+			sslprm.cipher_list[sizeof(sslprm.cipher_list)-1]='\0';
 			break;
+
 		default:
 			return ERROR;
 			break;
-		        }
-	        }
+		}
+	}
 
 	/* determine (base) command query */
-	snprintf(query,sizeof(query),"%s",(command_name==NULL)?DEFAULT_NRPE_COMMAND:command_name);
-	query[sizeof(query)-1]='\x0';
+	snprintf(query, sizeof(query), "%s", (command_name == NULL) ? DEFAULT_NRPE_COMMAND : command_name);
+	query[sizeof(query)-1] = '\x0';
 
 	/* get the command args */
-	if(argindex>0){
+	if (argindex > 0) {
 
-		for(c=argindex-1;c<argc;c++){
+		for (c = argindex - 1; c < argc; c++) {
 
-			i=sizeof(query)-strlen(query)-2;
-			if(i<=0)
+			i = sizeof(query) - strlen(query) - 2;
+			if (i <= 0)
 				break;
 
-			strcat(query,"!");
-			strncat(query,argv[c],i);
-			query[sizeof(query)-1]='\x0';
-		        }
-	        }
+			strcat(query, "!");
+			strncat(query, argv[c], i);
+			query[sizeof(query) - 1] = '\x0';
+		}
+	}
 
 	/* make sure required args were supplied */
-	if(server_name==NULL && show_help==FALSE && show_version==FALSE  && show_license==FALSE)
+	if (server_name == NULL && show_help == FALSE && show_version == FALSE  && show_license == FALSE)
 		return ERROR;
 
-
 	return OK;
-        }
+}
 
 
 

+ 293 - 47
src/nrpe.c

@@ -31,6 +31,7 @@
 #include "acl.h"
 
 #ifdef HAVE_SSL
+#include <ssl.h>
 #include "../include/dh.h"
 #endif
 
@@ -77,6 +78,29 @@ int     connection_timeout=DEFAULT_CONNECTION_TIMEOUT;
 int     ssl_shutdown_timeout=DEFAULT_SSL_SHUTDOWN_TIMEOUT;
 char    *command_prefix=NULL;
 
+/* SSL/TLS parameters */
+typedef enum _SSL_VER { SSLv2 = 1, SSLv2_plus, SSLv3, SSLv3_plus, TLSv1,
+    	TLSv1_plus, TLSv1_1, TLSv1_1_plus, TLSv1_2, TLSv1_2_plus
+				} SslVer;
+typedef enum _CLNT_CERTS {
+		Ask_For_Cert = 1, Require_Cert = 2
+				} ClntCerts;
+typedef enum _SSL_LOGGING { SSL_NoLogging, SSL_LogStartup, SSL_LogIpAddr,
+        SSL_LogVersion, SSL_LogCipher, SSL_LogIfClientCert, SSL_LogCertDetails
+				} SslLogging;
+struct _SSL_PARMS {
+	char		*cert_file;
+	char		*cacert_file;
+	char		*privatekey_file;
+	char		cipher_list[MAX_FILENAME_LENGTH];
+	SslVer		ssl_min_ver;
+	int			allowDH;
+	ClntCerts	client_certs;
+	SslLogging	log_opts;
+} sslprm = { NULL, NULL, NULL, "ALL:!MD5:@STRENGTH", TLSv1_plus, TRUE, 0, SSL_NoLogging };
+
+char	remote_host[MAX_HOST_ADDRESS_LENGTH];
+
 command *command_list=NULL;
 
 char    *nrpe_user=NULL;
@@ -104,12 +128,13 @@ int     use_src=FALSE; /* Define parameter for SRC option */
 int		listen_queue_size=DEFAULT_LISTEN_QUEUE_SIZE;
 
 #ifdef HAVE_SSL
-void complete_SSL_shutdown( SSL *);
+static void complete_SSL_shutdown( SSL *);
+static int verify_callback(int ok, X509_STORE_CTX *ctx);
 #endif
 
 int main(int argc, char **argv){
 	int result=OK;
-	int x;
+	int x, ssl_opts = SSL_OP_ALL|SSL_OP_SINGLE_DH_USE, vrfy;
 	char buffer[MAX_INPUT_BUFFER];
 	char *env_string=NULL;
 #ifdef HAVE_SSL
@@ -197,7 +222,7 @@ int main(int argc, char **argv){
 	/* open a connection to the syslog facility */
 	/* facility name may be overridden later */
 	get_log_facility(NRPE_LOG_FACILITY);
-        openlog("nrpe",LOG_PID,log_facility); 
+	openlog("nrpe",LOG_PID,log_facility); 
 
 	/* make sure the config file uses an absolute path */
 	if(config_file[0]!='/'){
@@ -236,12 +261,41 @@ int main(int argc, char **argv){
 		macro_argv[x]=NULL;
 
 #ifdef HAVE_SSL
+	if (sslprm.log_opts & SSL_LogStartup) {
+		syslog(LOG_INFO, "SSL Certificate File: %s", sslprm.cert_file);
+		syslog(LOG_INFO, "SSL Private Key File: %s", sslprm.privatekey_file);
+		syslog(LOG_INFO, "SSL CA Certificate File: %s", sslprm.cacert_file);
+		if (sslprm.allowDH < 2)
+			syslog(LOG_INFO, "SSL Cipher List: %s", sslprm.cipher_list);
+		else
+			syslog(LOG_INFO, "SSL Cipher List: ADH");
+		syslog(LOG_INFO, "SSL Allow ADH: %s",
+				sslprm.allowDH == 0 ? "No" : (sslprm.allowDH == 1 ? "Allow" : "Require"));
+		syslog(LOG_INFO, "SSL Client Certs: %s",
+				sslprm.client_certs == 0 ? "Don't Ask" : (sslprm.client_certs == 1 ? "Accept" : "Require"));
+		syslog(LOG_INFO, "SSL Log Options: 0x%02x", sslprm.log_opts);
+		switch (sslprm.ssl_min_ver) {
+			case SSLv2:			env_string = "SSLv2";					break;
+			case SSLv2_plus:	env_string = "SSLv2 And Above";			break;
+			case SSLv3:			env_string = "SSLv3";					break;
+			case SSLv3_plus:	env_string = "SSLv3_plus And Above";	break;
+			case TLSv1:			env_string = "TLSv1";					break;
+			case TLSv1_plus:	env_string = "TLSv1_plus And Above";	break;
+			case TLSv1_1:		env_string = "TLSv1_1";					break;
+			case TLSv1_1_plus:	env_string = "TLSv1_1_plus And Above";	break;
+			case TLSv1_2:		env_string = "TLSv1_2";					break;
+			case TLSv1_2_plus:	env_string = "TLSv1_2_plus And Above";	break;
+			defualt:			env_string = "INVALID VALUE!";			break;
+		}
+		syslog(LOG_INFO, "SSL Version: %s", env_string);
+	}
+
 	/* initialize SSL */
 	if(use_ssl==TRUE){
-		SSL_library_init();
-		SSLeay_add_ssl_algorithms();
-		meth=SSLv23_server_method();
 		SSL_load_error_strings();
+		SSL_library_init();
+
+		meth = SSLv23_server_method();
 
 		/* use week random seed if necessary */
 		if(allow_weak_random_seed && (RAND_status()==0)){
@@ -262,22 +316,78 @@ int main(int argc, char **argv){
 				}
 			}
 
-		if((ctx=SSL_CTX_new(meth))==NULL){
-			syslog(LOG_ERR,"Error: could not create SSL context.\n");
+#ifndef OPENSSL_NO_SSL2
+		if (sslprm.ssl_min_ver == SSLv2)
+			meth = SSLv2_server_method();
+#endif
+#ifndef OPENSSL_NO_SSL3
+		if (sslprm.ssl_min_ver == SSLv3)
+			meth = SSLv3_server_method();
+#endif
+		if (sslprm.ssl_min_ver == TLSv1)
+			meth = TLSv1_server_method();
+		if (sslprm.ssl_min_ver == TLSv1_1)
+			meth = TLSv1_1_server_method();
+		if (sslprm.ssl_min_ver == TLSv1_2)
+			meth = TLSv1_2_server_method();
+
+		ctx = SSL_CTX_new(meth);
+		if (ctx == NULL) {
+			syslog(LOG_ERR,"Error: could not create SSL context");
+			SSL_CTX_free(ctx);
 			exit(STATE_CRITICAL);
-		        }
+		}
 
-		/* ADDED 01/19/2004 */
-		/* use only TLSv1 protocol */
-		SSL_CTX_set_options(ctx,SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+		if (sslprm.ssl_min_ver >= SSLv3) {
+			ssl_opts |= SSL_OP_NO_SSLv2;
+			if (sslprm.ssl_min_ver >= TLSv1)
+				ssl_opts |= SSL_OP_NO_SSLv3;
+		}
+		SSL_CTX_set_options(ctx, ssl_opts);
 
-		/* use anonymous DH ciphers */
-		SSL_CTX_set_cipher_list(ctx,"ADH");
-		dh=get_dh512();
-		SSL_CTX_set_tmp_dh(ctx,dh);
-		DH_free(dh);
+		if (sslprm.cert_file != NULL) {
+			if (!SSL_CTX_use_certificate_file(ctx, sslprm.cert_file, SSL_FILETYPE_PEM)) {
+				SSL_CTX_free(ctx);
+				syslog(LOG_ERR, "Error: could not use certificate file '%s'", sslprm.cert_file);
+				exit(STATE_CRITICAL);
+			}
+			if (!SSL_CTX_use_PrivateKey_file(ctx, sslprm.privatekey_file, SSL_FILETYPE_PEM)) {
+				SSL_CTX_free(ctx);
+				syslog(LOG_ERR, "Error: could not use private key file '%s'", sslprm.privatekey_file);
+				exit(STATE_CRITICAL);
+			}
+		}
+
+		if (sslprm.client_certs != 0) {
+			vrfy = SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE;
+			if ((sslprm.client_certs & Require_Cert) != 0)
+				vrfy |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+			SSL_CTX_set_verify(ctx, vrfy, verify_callback);
+			if (!SSL_CTX_load_verify_locations(ctx, sslprm.cacert_file, NULL)) {
+				SSL_CTX_free(ctx);
+				syslog(LOG_ERR, "Error: could not use CA certificate '%s'", sslprm.cacert_file);
+				exit(STATE_CRITICAL);
+			}
+		}
+
+		if (SSL_CTX_set_cipher_list(ctx, sslprm.cipher_list) == 0) {
+			SSL_CTX_free(ctx);
+			syslog(LOG_ERR, "Error: Could not set SSL/TLS cipher list");
+			exit(STATE_CRITICAL);
+		}
+
+		if (sslprm.allowDH) {
+			/* use anonymous DH ciphers */
+			if (sslprm.allowDH == 2)
+				SSL_CTX_set_cipher_list(ctx, "ADH");
+			dh = get_dh2048();
+			SSL_CTX_set_tmp_dh(ctx, dh);
+			DH_free(dh);
+		}
+		
 		if(debug==TRUE)
 			syslog(LOG_INFO,"INFO: SSL/TLS initialized. All network traffic will be encrypted.");
+
 	        }
 	else{
 		if(debug==TRUE)
@@ -467,6 +577,32 @@ int main(int argc, char **argv){
 	}
 
 
+int verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
+{
+	char		name[256], issuer[256];
+	X509		*err_cert;
+	int			err;
+	SSL			*ssl;
+
+	if (preverify_ok || (sslprm.log_opts & SSL_LogCertDetails == 0))
+		return preverify_ok;
+
+	err_cert = X509_STORE_CTX_get_current_cert(ctx);
+	err = X509_STORE_CTX_get_error(ctx);
+
+	/* Get the pointer to the SSL of the current connection */
+	ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+
+	X509_NAME_oneline(X509_get_subject_name(err_cert), name, 256);
+	X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), issuer, 256);
+
+	if (!preverify_ok && (sslprm.log_opts & SSL_LogCertDetails)) {
+		syslog(LOG_ERR, "SSL Client has an invalid certificate: %s (issuer=%s) err=%d:%s",
+				name, issuer, err, X509_verify_cert_error_string(err));
+	}
+
+	return preverify_ok;
+}
 
 
 /* read in the configuration file */
@@ -481,6 +617,8 @@ int read_config_file(char *filename){
 	int line=0;
 	int len=0;
 	int x=0;
+	int pskfd;
+	struct stat st;
 
 
 	/* open the config file for reading */
@@ -641,6 +779,69 @@ int read_config_file(char *filename){
 				}
 			}
 
+		else if (!strcmp(varname, "ssl_version")) {
+			if (!strcmp(varvalue, "SSLv2"))
+				sslprm.ssl_min_ver = SSLv2;
+			else if (!strcmp(varvalue, "SSLv2+"))
+				sslprm.ssl_min_ver = SSLv2_plus;
+			else if (!strcmp(varvalue, "SSLv3"))
+				sslprm.ssl_min_ver = SSLv3;
+			else if (!strcmp(varvalue, "SSLv3+"))
+				sslprm.ssl_min_ver = SSLv3_plus;
+			else if (!strcmp(varvalue, "TLSv1"))
+				sslprm.ssl_min_ver = TLSv1;
+			else if (!strcmp(varvalue, "TLSv1+"))
+				sslprm.ssl_min_ver = TLSv1_plus;
+			else if (!strcmp(varvalue, "TLSv1.1"))
+				sslprm.ssl_min_ver = TLSv1_1;
+			else if (!strcmp(varvalue, "TLSv1.1+"))
+				sslprm.ssl_min_ver = TLSv1_1_plus;
+			else if (!strcmp(varvalue, "TLSv1.2"))
+				sslprm.ssl_min_ver = TLSv1_2;
+			else if (!strcmp(varvalue, "TLSv1.2+"))
+				sslprm.ssl_min_ver = TLSv1_2_plus;
+			else {
+				syslog(LOG_ERR, "Invalid ssl version specified in config file '%s' - Line %d", filename, line);
+				return ERROR;
+			}
+		}
+
+		else if (!strcmp(varname, "ssl_use_adh")) {
+			sslprm.allowDH = atoi(varvalue);
+			if (sslprm.allowDH < 0 || sslprm.allowDH > 2) {
+				syslog(LOG_ERR, "Invalid use adh value specified in config file '%s' - Line %d", filename, line);
+				return ERROR;
+			}
+        }
+
+		else if (!strcmp(varname, "ssl_logging"))
+			sslprm.log_opts = strtol(varvalue, NULL, 0);
+
+		else if (!strcmp(varname, "ssl_cipher_list")) {
+			strncpy(sslprm.cipher_list, varvalue, sizeof(sslprm.cipher_list) - 1);
+			sslprm.cipher_list[sizeof(sslprm.cipher_list)-1]='\0';
+		}
+
+		else if(!strcmp(varname, "ssl_cert_file"))
+			sslprm.cert_file = strdup(varvalue);
+		
+		else if(!strcmp(varname, "ssl_cacert_file"))
+			sslprm.cacert_file = strdup(varvalue);
+
+		else if(!strcmp(varname, "ssl_privatekey_file"))
+			sslprm.privatekey_file = strdup(varvalue);
+
+		else if (!strcmp(varname, "ssl_client_certs")) {
+			sslprm.client_certs = atoi(varvalue);
+			if (sslprm.client_certs < 0 || sslprm.client_certs > 7) {
+				syslog(LOG_ERR, "Invalid client certs value specified in config file '%s' - Line %d", filename, line);
+				return ERROR;
+			}
+			/* if requiring or logging client certs, make sure "Ask" is turned on */
+			if (sslprm.client_certs & Require_Cert)
+				sslprm.client_certs |= Ask_For_Cert;
+		}
+
 		else if(!strcmp(varname,"log_facility")){
 			if((get_log_facility(varvalue))==OK){
 				/* re-open log using new facility */
@@ -927,7 +1128,7 @@ void wait_for_connections(void){
 	struct sockaddr_in6 *nptr6;
 	struct sockaddr_storage addr;
 	int rc;
-	int sock, new_sd;
+	int sock, new_sd=0;
 	socklen_t addrlen;
 	pid_t pid;
 	fd_set fdread;
@@ -967,7 +1168,7 @@ void wait_for_connections(void){
 #endif
 #endif
 
-	syslog(LOG_INFO,"Listening for connections on port %d\n",htons(myname.sin_port));
+	syslog(LOG_INFO, "Listening for connections on port %d", server_port);
 
 	if(allowed_hosts)
 		syslog(LOG_INFO,"Allowing connections from: %s\n",allowed_hosts);
@@ -1088,11 +1289,12 @@ void wait_for_connections(void){
 							nptr = (struct sockaddr_in *)&addr;
 
 							/* log info to syslog facility */
-							if(debug==TRUE) {
+							strncpy(remote_host, inet_ntoa(nptr->sin_addr), sizeof(remote_host) - 1);
+							remote_host[MAX_HOST_ADDRESS_LENGTH - 1] = '\0';
+							if (debug == TRUE || (sslprm.log_opts & SSL_LogIpAddr)) {
 								syslog(LOG_DEBUG, "Connection from %s port %d",
-										inet_ntoa(nptr->sin_addr), 
-										nptr->sin_port);
-								}
+										remote_host,  nptr->sin_port);
+							}
 							if(!is_an_allowed_host(AF_INET,
 									(void *)&(nptr->sin_addr))) {
 								/* log error to syslog facility */
@@ -1129,10 +1331,11 @@ void wait_for_connections(void){
 								} 
 
 							/* log info to syslog facility */
-							if(debug==TRUE) {
+							strcpy(remote_host, ipstr);
+							if (debug == TRUE || (sslprm.log_opts & SSL_LogIpAddr)) {
 								syslog(LOG_DEBUG, "Connection from %s port %d",
 										ipstr, nptr6->sin6_port);
-								}
+							}
 							if(!is_an_allowed_host(AF_INET6, 
 									(void *)&(nptr6->sin6_addr))) {
 								/* log error to syslog facility */
@@ -1251,6 +1454,8 @@ void handle_connection(int sock){
 #endif
 #ifdef HAVE_SSL
 	SSL *ssl=NULL;
+	const SSL_CIPHER *c;
+	X509 *peer;
 #endif
 #ifdef HAVE_SIGACTION
 	struct sigaction sig_action;
@@ -1280,33 +1485,73 @@ void handle_connection(int sock){
 
 #ifdef HAVE_SSL
 	/* do SSL handshake */
-	if(result==STATE_OK && use_ssl==TRUE){
-		if((ssl=SSL_new(ctx))!=NULL){
+	if (result == STATE_OK && use_ssl == TRUE) {
+		if ((ssl = SSL_new(ctx)) != NULL) {
 			SSL_set_fd(ssl,sock);
-
+ 
 			/* keep attempting the request if needed */
-                        while(((rc=SSL_accept(ssl))!=1) && (SSL_get_error(ssl,rc)==SSL_ERROR_WANT_READ));
-
-			if(rc!=1){
-				syslog(LOG_ERR,"Error: Could not complete SSL handshake. %d\n",SSL_get_error(ssl,rc));
+			while (((rc = SSL_accept(ssl)) != 1) && (SSL_get_error(ssl, rc) == SSL_ERROR_WANT_READ));
+
+			if (rc != 1) {
+				if (sslprm.log_opts & (SSL_LogCertDetails|SSL_LogIfClientCert)) {
+					int	nerrs = 0;
+					rc = 0;
+					while ((x = ERR_get_error_line_data(NULL, NULL, NULL, NULL)) != 0) {
+						syslog(LOG_ERR, "Error: Could not complete SSL handshake with %s: %s",
+								remote_host, ERR_reason_error_string(x));
+						++nerrs;
+					}
+					if (nerrs == 0)
+						syslog(LOG_ERR, "Error: Could not complete SSL handshake with %s: %d",
+								remote_host, SSL_get_error(ssl,rc));
+				} else
+					syslog(LOG_ERR, "Error: Could not complete SSL handshake with %s: %d",
+							remote_host, SSL_get_error(ssl,rc));
 #ifdef DEBUG
-				errfp=fopen("/tmp/err.log","w");
+				errfp = fopen("/tmp/err.log", "a");
 				ERR_print_errors_fp(errfp);
 				fclose(errfp);
 #endif
 				return;
-			        }
-		        }
-		else{
-			syslog(LOG_ERR,"Error: Could not create SSL connection structure.\n");
+			}
+			if (sslprm.log_opts & SSL_LogVersion)
+				syslog(LOG_NOTICE, "Remote %s - SSL Version: %s",
+						remote_host, SSL_get_version(ssl));
+			if (sslprm.log_opts & SSL_LogCipher) {
+				c = SSL_get_current_cipher(ssl);
+				syslog(LOG_NOTICE, "Remote %s - %s, Cipher is %s", remote_host,
+					   SSL_CIPHER_get_version(c), SSL_CIPHER_get_name(c));
+			}
+			if ((sslprm.log_opts & SSL_LogIfClientCert) || (sslprm.log_opts & SSL_LogCertDetails)) {
+				peer = SSL_get_peer_certificate(ssl);
+				if (peer) {
+					if (sslprm.log_opts & SSL_LogIfClientCert)
+						syslog(LOG_NOTICE, "SSL Client %s has %s certificate",
+								remote_host, peer->valid ? "a valid" : "an invalid");
+					if (sslprm.log_opts & SSL_LogCertDetails) {
+						syslog(LOG_NOTICE, "SSL Client %s Cert Name: %s",
+								remote_host, peer->name);
+						X509_NAME_oneline(X509_get_issuer_name(peer), buffer, sizeof(buffer));
+						syslog(LOG_NOTICE, "SSL Client %s Cert Issuer: %s",
+								remote_host, buffer);
+					}
+				} else if (sslprm.client_certs == 0)
+					syslog(LOG_NOTICE, "SSL Not asking for client certification");
+				else
+					syslog(LOG_NOTICE, "SSL Client %s did not present a certificate",
+							remote_host);
+			}
+		}
+		else {
+			syslog(LOG_ERR, "Error: Could not create SSL connection structure.");
 #ifdef DEBUG
-			errfp=fopen("/tmp/err.log","w");
+			errfp = fopen("/tmp/err.log", "a");
 			ERR_print_errors_fp(errfp);
 			fclose(errfp);
 #endif
 			return;
-		        }
-	        }
+		}
+	}
 #endif
 
 	bytes_to_recv=sizeof(receive_packet);
@@ -1314,7 +1559,7 @@ void handle_connection(int sock){
 		rc=recvall(sock,(char *)&receive_packet,&bytes_to_recv,socket_timeout);
 #ifdef HAVE_SSL
 	else{
-                while(((rc=SSL_read(ssl,&receive_packet,bytes_to_recv))<=0) && (SSL_get_error(ssl,rc)==SSL_ERROR_WANT_READ));
+	        while(((rc=SSL_read(ssl,&receive_packet,bytes_to_recv))<=0) && (SSL_get_error(ssl,rc)==SSL_ERROR_WANT_READ));
 		}
 #endif
 
@@ -1322,7 +1567,7 @@ void handle_connection(int sock){
 	if(rc<=0){
 
 		/* log error to syslog facility */
-		syslog(LOG_ERR,"Could not read request from client, bailing out...");
+		syslog(LOG_ERR,"Could not read request from client %s, bailing out...", remote_host);
 
 #ifdef HAVE_SSL
 		if(ssl){
@@ -1339,7 +1584,7 @@ void handle_connection(int sock){
 	else if(bytes_to_recv!=sizeof(receive_packet)){
 
 		/* log error to syslog facility */
-		syslog(LOG_ERR,"Data packet from client was too short, bailing out...");
+		syslog(LOG_ERR,"Data packet from client (%s) was too short, bailing out...", remote_host);
 
 #ifdef HAVE_SSL
 		if(ssl){
@@ -1363,7 +1608,7 @@ void handle_connection(int sock){
 	if(validate_request(&receive_packet)==ERROR){
 
 		/* log an error */
-		syslog(LOG_ERR,"Client request was invalid, bailing out...");
+		syslog(LOG_ERR,"Client request from %s was invalid, bailing out...", remote_host);
 
 		/* free memory */
 		free(command_name);
@@ -1385,7 +1630,8 @@ void handle_connection(int sock){
 
 	/* log info to syslog facility */
 	if(debug==TRUE)
-		syslog(LOG_DEBUG,"Host is asking for command '%s' to be run...",receive_packet.buffer);
+		syslog(LOG_DEBUG,"Host %s is asking for command '%s' to be run...",
+				remote_host, receive_packet.buffer);
 
 	/* disable connection alarm - a new alarm will be setup during my_system */
 	alarm(0);
@@ -1398,7 +1644,7 @@ void handle_connection(int sock){
 
 		/* log info to syslog facility */
 		if(debug==TRUE)
-			syslog(LOG_DEBUG,"Response: %s",buffer);
+			syslog(LOG_DEBUG,"Response to %s: %s", remote_host, buffer);
 
 		result=STATE_OK;
 	        }
@@ -1472,7 +1718,7 @@ void handle_connection(int sock){
 		buffer[strlen(buffer)-1]='\x0';
 
 	/* clear the response packet buffer */
-	bzero(&send_packet,sizeof(send_packet));
+	memset(&send_packet, 0, sizeof(send_packet));
 
 	/* fill the packet with semi-random data */
 	randomize_buffer((char *)&send_packet,sizeof(send_packet));

+ 46 - 0
src/utils.c

@@ -364,6 +364,52 @@ char *my_strsep (char **stringp, const char *delim){
 	return begin;
 	}
 
+int b64_decode(unsigned char *encoded)
+{
+	static const char *b64 = { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" };
+	int		i, j, l, padding = 0;
+	unsigned char	c[4], *outp = encoded;
+	union {
+		unsigned c3;
+		struct {
+			unsigned f1:6;
+			unsigned f2:6;
+			unsigned f3:6;
+			unsigned f4:6;
+		} fields;
+	} enc;
+
+	enc.c3 = 0;
+	l = strlen((char*)encoded);
+	for (i = 0; i < l; i += 4) {
+		for (j = 0; j < 4; ++j) {
+			if (encoded[i+j] == '=') {
+				c[j] = 0;
+				++padding;
+			} else if (encoded[i+j] >= 'A' && encoded[i+j] <= 'Z')
+				c[j] = encoded[i+j] - 'A';
+			else if (encoded[i+j] >= 'a' && encoded[i+j] <= 'z')
+				c[j] = encoded[i+j] - 'a' + 26;
+			else if (encoded[i+j] >= '0' && encoded[i+j] <= '9')
+				c[j] = encoded[i+j] - '0' + 52;
+			else if (encoded[i+j] == '+')
+				c[j] = encoded[i+j] - '+' + 62;
+			else
+				c[j] = encoded[i+j] - '/' + 63;
+		}
+		enc.fields.f1 = c[3];
+		enc.fields.f2 = c[2];
+		enc.fields.f3 = c[1];
+		enc.fields.f4 = c[0];
+		*outp++ = (enc.c3 >> 16) & 0xff;
+		*outp++ = (enc.c3 >> 8) & 0xff;
+		*outp++ = (enc.c3) & 0xff;
+	}
+	*outp = '\0';
+
+	return outp - encoded - padding;
+}
+
 
 /* show license */
 void display_license(void){