This is an automated email from the ASF dual-hosted git repository.

gmurthy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/qpid-dispatch.git


The following commit(s) were added to refs/heads/master by this push:
     new 7bcd40a  DISPATCH-1440 - Deprecated passwordFile attribute in 
sslProfile and modified the password field to accept openssl style prefixes. 
This closes #582.
7bcd40a is described below

commit 7bcd40aec8bb059e1ea200c7b126635ba0903139
Author: Ganesh Murthy <gmur...@apache.org>
AuthorDate: Fri Oct 4 14:30:17 2019 -0400

    DISPATCH-1440 - Deprecated passwordFile attribute in sslProfile and 
modified the password field to accept openssl style prefixes. This closes #582.
---
 docs/books/user-guide/configuration-reference.adoc |   4 +-
 docs/books/user-guide/configuration-security.adoc  |   2 +-
 ...ecting-using-mutual-ssl-tls-authentication.adoc |   5 +-
 .../modules/enabling-ssl-tls-encryption.adoc       |   7 +-
 .../securing-connections-between-routers.adoc      |   7 +-
 python/qpid_dispatch/management/qdrouter.json      |   6 +-
 src/connection_manager.c                           | 145 ++++++++++++++-------
 tests/system_tests_user_id.py                      |  22 +++-
 8 files changed, 134 insertions(+), 64 deletions(-)

diff --git a/docs/books/user-guide/configuration-reference.adoc 
b/docs/books/user-guide/configuration-reference.adoc
index c25cd2c..5d09083 100644
--- a/docs/books/user-guide/configuration-reference.adoc
+++ b/docs/books/user-guide/configuration-reference.adoc
@@ -76,8 +76,8 @@ Attributes for setting SSL/TLS configuration for connections.
 * *_caCertFile_* (path) : The absolute path to the database that contains the 
public certificates of trusted certificate authorities (CA).
 * *_certFile_* (path) : The absolute path to the file containing the 
PEM-formatted public certificate to be used on the local end of any connections 
using this profile.
 * *_privateKeyFile_* (path) : The absolute path to the file containing the 
PEM-formatted private key for the above certificate.
-* *_passwordFile_* (path) : If the above private key is password protected, 
this is the absolute path to a file containing the password that unlocks the 
certificate key.
-* *_password_* (string) : An alternative to storing the password in a file 
referenced by passwordFile is to supply the password right here in the 
configuration file. This option can be used by supplying the password in the 
‘password’ option. Don’t use both password and passwordFile in the same profile.
+* *_passwordFile_* (path) : (DEPRECATED) If the above private key is password 
protected, this is the absolute path to the file containing the password that 
unlocks the certificate key. This file should be permission protected to limit 
access. This has been deprecated. Use the file: prefix in the password field to 
specify the absolute path of the file containing the password. If both password 
and passwordFile are provided, the passwordFile is ignored.
+* *_password_* (string) : Password that unlocks the certificate key. Supports 
three prefixes namely - env:, file: pass:. Also supports the legacy literal: 
prefix. env:var obtains the password from the environment variable var. Since 
the environment of other processes is visible on certain platforms (e.g. ps 
under certain Unix OSes) this option should be used with caution. 
file:absolutepath obtains password from the absolute path of the file 
containing the password. This option is the saf [...]
 * *_uidFormat_* (string) : A list of x509 client certificate fields that will 
be used to build a string that will uniquely identify the client certificate 
owner. For example, a value of ‘cou’ indicates that the uid will consist of c - 
common name concatenated with o - organization-company name concatenated with u 
- organization unit; or a value of ‘oF’ indicates that the uid will consist of 
o (organization name) concatenated with F (the sha256 fingerprint of the entire 
certificate) . All [...]
 * *_uidNameMappingFile_* (string) : The absolute path to the file containing 
the unique id to display name mapping.
 * *_name_* (string) : The name of the profile used for referencing it from 
_listener_ and _connector_ sections.
diff --git a/docs/books/user-guide/configuration-security.adoc 
b/docs/books/user-guide/configuration-security.adoc
index d0e4018..8008da8 100644
--- a/docs/books/user-guide/configuration-security.adoc
+++ b/docs/books/user-guide/configuration-security.adoc
@@ -133,7 +133,7 @@ For example:
 privateKeyFile: /qdrouterd/ssl_certs/router-key-pwd.pem
 ----
 
-`passwordFile` or `password`:: If the private key is password-protected, you 
must provide the password by either specifying the absolute path to a file 
containing the password that unlocks the certificate key, or entering the 
password directly in the configuration file.
+`passwordFile` or `password`:: If the private key is password-protected, you 
must provide the password by either specifying the absolute path to a file 
containing the password that unlocks the certificate key, or entering the 
password directly in the configuration file. Entering the password directly in 
the configuration file is unsafe. passwordFile has been deprecated. Use 
password.
 +
 For example:
 +
diff --git 
a/docs/books/user-guide/modules/connecting-using-mutual-ssl-tls-authentication.adoc
 
b/docs/books/user-guide/modules/connecting-using-mutual-ssl-tls-authentication.adoc
index 6bc9e05..cf773e6 100644
--- 
a/docs/books/user-guide/modules/connecting-using-mutual-ssl-tls-authentication.adoc
+++ 
b/docs/books/user-guide/modules/connecting-using-mutual-ssl-tls-authentication.adoc
@@ -48,6 +48,7 @@ sslProfile {
     certFile: /etc/qpid-dispatch-certs/tls.crt
     privateKeyFile: /etc/qpid-dispatch-certs/tls.key
     caCertFile: /etc/qpid-dispatch-certs/ca.crt
+    password: file:/etc/qpid-dispatch-certs/
     ...
 }
 ----
@@ -55,9 +56,11 @@ sslProfile {
 
 `certFile`:: The absolute path to the file containing the public certificate 
for this router.
 
+`caCertFile`:: The absolute path to the CA certificate that was used to sign 
the router's certificate.
+
 `privateKeyFile`:: The absolute path to the file containing the private key 
for this router's public certificate.
 
-`caCertFile`:: The absolute path to the CA certificate that was used to sign 
the router's certificate.
+`password`:: The password to unlock the certificate key. Not specified if 
certificate key has no password. The prefix file: is used to specify the 
absolute path of the file containing the password.
 --
 
 . Configure the `connector` for this connection to use the `sslProfile` that 
you created.
diff --git a/docs/books/user-guide/modules/enabling-ssl-tls-encryption.adoc 
b/docs/books/user-guide/modules/enabling-ssl-tls-encryption.adoc
index 5720f6c..ba32dec 100644
--- a/docs/books/user-guide/modules/enabling-ssl-tls-encryption.adoc
+++ b/docs/books/user-guide/modules/enabling-ssl-tls-encryption.adoc
@@ -46,8 +46,9 @@ This `sslProfile` contains the locations of the private key 
and certificates tha
 sslProfile {
     name: service-tls
     certFile: /etc/qpid-dispatch-certs/normal/tls.crt
-    privateKeyFile: /etc/qpid-dispatch-certs/normal/tls.key
     caCertFile: /etc/qpid-dispatch-certs/client-ca/ca.crt
+    privateKeyFile: /etc/qpid-dispatch-certs/normal/tls.key
+    password: file:/etc/qpid-dispatch-certs/inter-router/password.txt
     ...
 }
 ----
@@ -55,9 +56,11 @@ sslProfile {
 
 `certFile`:: The absolute path to the file containing the public certificate 
for this router.
 
+`caCertFile`:: The absolute path to the CA certificate that was used to sign 
the router's certificate.
+
 `privateKeyFile`:: The absolute path to the file containing the private key 
for this router's public certificate.
 
-`caCertFile`:: The absolute path to the CA certificate that was used to sign 
the router's certificate.
+`password`:: The password to unlock the certificate key. Not specified if 
certificate key has no password. The prefix file: is used to specify the 
absolute path of the file containing the password.
 --
 
 . Configure the `listener` for this connection to use SSL/TLS to encrypt the 
connection.
diff --git 
a/docs/books/user-guide/modules/securing-connections-between-routers.adoc 
b/docs/books/user-guide/modules/securing-connections-between-routers.adoc
index 10e1460..f69c2f8 100644
--- a/docs/books/user-guide/modules/securing-connections-between-routers.adoc
+++ b/docs/books/user-guide/modules/securing-connections-between-routers.adoc
@@ -56,8 +56,9 @@ This `sslProfile` contains the locations of the private key 
and certificates tha
 sslProfile {
     name: inter-router-tls
     certFile: /etc/qpid-dispatch-certs/inter-router/tls.crt
-    privateKeyFile: /etc/qpid-dispatch-certs/inter-router/tls.key
     caCertFile: /etc/qpid-dispatch-certs/inter-router/ca.crt
+    privateKeyFile: /etc/qpid-dispatch-certs/inter-router/tls.key
+    password: file:/etc/qpid-dispatch-certs/inter-router/password.txt
     ...
 }
 ----
@@ -65,9 +66,11 @@ sslProfile {
 
 `certFile`:: The absolute path to the file containing the public certificate 
for this router.
 
+`caCertFile`:: The absolute path to the CA certificate that was used to sign 
the router's certificate.
+
 `privateKeyFile`:: The absolute path to the file containing the private key 
for this router's public certificate.
 
-`caCertFile`:: The absolute path to the CA certificate that was used to sign 
the router's certificate.
+`password`:: The password to unlock the certificate key. Not specified if 
certificate key has no password. The prefix file: is used to specify the 
absolute path of the file containing the password.
 --
 
 .. Configure the inter-router `connector` for this connection to use the 
`sslProfile` that you created.
diff --git a/python/qpid_dispatch/management/qdrouter.json 
b/python/qpid_dispatch/management/qdrouter.json
index 0b8262c..3cd092f 100644
--- a/python/qpid_dispatch/management/qdrouter.json
+++ b/python/qpid_dispatch/management/qdrouter.json
@@ -639,14 +639,14 @@
                 },
                 "passwordFile": {
                     "type": "path",
-                    "description": "If the above private key is password 
protected, this is the absolute path to a file containing the password that 
unlocks the certificate key. This file should be permission protected to limit 
access",
+                    "description": "(DEPRECATED) If the above private key is 
password protected, this is the absolute path to the file containing the 
password that unlocks the certificate key. This file should be permission 
protected to limit access. This has been deprecated. Use the file: prefix in 
the password field to specify the absolute path of the file containing the 
password. If both password and passwordFile are provided, the passwordFile is 
ignored",
+                    "deprecated": true,
                     "create": true
 
                 },
                 "password": {
                     "type": "string",
-                    "description": "(DEPRECATED) An alternative to storing the 
password in a file referenced by passwordFile is to supply the password right 
here in the configuration file.  This takes precedence over the passwordFile if 
both are specified. This attribute has been deprecated because it is unsafe to 
store plain text passwords in config files. Use the passwordFile instead",
-                    "deprecated": true,
+                    "description": "Password that unlocks the certificate key. 
Supports three prefixes namely - env:, file: pass:. Also supports the legacy 
literal: prefix. env:var obtains the password from the environment variable 
var. Since the environment of other processes is visible on certain platforms 
(e.g. ps under certain Unix OSes) this option should be used with caution. 
file:absolutepath obtains the password from the  absolute path of the file 
containing the password. This op [...]
                     "create": true
 
                 },
diff --git a/src/connection_manager.c b/src/connection_manager.c
index b571555..9593198 100644
--- a/src/connection_manager.c
+++ b/src/connection_manager.c
@@ -32,6 +32,7 @@
 #include <string.h>
 #include <stdio.h>
 #include <inttypes.h>
+#include <errno.h>
 
 struct qd_config_ssl_profile_t {
     DEQ_LINKS(qd_config_ssl_profile_t);
@@ -104,6 +105,44 @@ static qd_config_ssl_profile_t 
*qd_find_ssl_profile(qd_connection_manager_t *cm,
 }
 
 /**
+ * Read the file from the password_file location on the file system and 
populate password_field with the
+ * contents of the file.
+ */
+static void qd_set_password_from_file(char *password_file, char 
**password_field, qd_log_source_t *log_source)
+{
+    if (password_file) {
+        FILE *file = fopen(password_file, "r");
+
+        if (file == NULL) {
+            //
+            // The global variable errno (found in <errno.h>) contains 
information about what went wrong; you can use perror() to print that 
information as a readable string
+            //
+            qd_log(log_source, QD_LOG_ERROR, "Unable to open password file %s, 
error: %s", password_file, strerror(errno));
+            return;
+        }
+
+        char buffer[200];
+
+        int c;
+        int i=0;
+
+        while (i < 200 - 1) {
+            c = fgetc(file);
+            if (c == EOF || c == '\n')
+                break;
+            buffer[i++] = c;
+        }
+
+        if (i != 0) {
+            buffer[i] = '\0';
+            free(*password_field);
+            *password_field = strdup(buffer);
+        }
+        fclose(file);
+    }
+}
+
+/**
  * Search the list of config_sasl_plugins for an sasl-profile that matches the 
passed in name
  */
 static qd_config_sasl_plugin_t *qd_find_sasl_plugin(qd_connection_manager_t 
*cm, char *name)
@@ -210,9 +249,8 @@ static void set_config_host(qd_server_config_t *config, 
qd_entity_t* entity)
     snprintf(config->host_port, hplen, "%s:%s", config->host, config->port);
 }
 
-static void qd_config_ssl_profile_process_password(qd_config_ssl_profile_t* 
ssl_profile)
+static void qd_config_process_password(char **actual_val, char *pw, bool 
*is_file, qd_log_source_t *log_source)
 {
-    char *pw = ssl_profile->ssl_password;
     if (!pw)
         return;
 
@@ -230,29 +268,52 @@ static void 
qd_config_ssl_profile_process_password(qd_config_ssl_profile_t* ssl_
             //
             // Replace the allocated directive with the looked-up password
             //
-            free(ssl_profile->ssl_password);
-            ssl_profile->ssl_password = strdup(passwd);
+            *actual_val = strdup(passwd);
         } else {
             qd_error(QD_ERROR_NOT_FOUND, "Failed to find a password in the 
environment variable");
         }
     }
 
     //
-    // If the "password" starts with "literal:" then
+    // If the "password" starts with "literal:" or "pass:" then
     // the remaining text is the password and the heading should be
     // stripped off
     //
-    else if (strncmp(pw, "literal:", 8) == 0) {
-        // skip the "literal:" header
-        pw += 8;
+    else if (strncmp(pw, "literal:", 8) == 0 || strncmp(pw, "pass:", 5) == 0) {
+        qd_log(log_source, QD_LOG_WARNING, "It is unsafe to provide plain text 
passwords in the config file");
+
+        if (strncmp(pw, "l", 1) == 0) {
+            // skip the "literal:" header
+            pw += 8;
+        }
+        else {
+            // skip the "pass:" header
+            pw += 5;
+        }
 
-        // skip the whitespace if it is there
-        while (*pw == ' ') ++pw;
+        //
+        // Replace the password with a copy of the string after "literal: or 
"pass:"
+        //
+        char *copy = strdup(pw);
+        *actual_val = copy;
+    }
+    //
+    // If the password starts with a file: literal set the is_file to true.
+    //
+    else if (strncmp(pw, "file:", 5) == 0) {
+        pw += 5;
 
-        // Replace the password with a copy of the string after "literal:"
+        // Replace the password with a copy of the string after "file:"
         char *copy = strdup(pw);
-        free(ssl_profile->ssl_password);
-        ssl_profile->ssl_password = copy;
+        *actual_val = copy;
+        *is_file = true;
+    }
+    else {
+        //
+        // THe password field does not have any prefixes. Use it as plain text
+        //
+        qd_log(log_source, QD_LOG_WARNING, "It is unsafe to provide plain text 
passwords in the config file");
+
     }
 }
 
@@ -508,42 +569,33 @@ qd_config_ssl_profile_t 
*qd_dispatch_configure_ssl_profile(qd_dispatch_t *qd, qd
     ssl_profile->ssl_certificate_file       = qd_entity_opt_string(entity, 
"certFile", 0); CHECK();
     ssl_profile->ssl_private_key_file       = qd_entity_opt_string(entity, 
"privateKeyFile", 0); CHECK();
     ssl_profile->ssl_password               = qd_entity_opt_string(entity, 
"password", 0); CHECK();
+    char *password_file                     = qd_entity_opt_string(entity, 
"passwordFile", 0); CHECK();
 
     if (ssl_profile->ssl_password) {
-        qd_log(cm->log_source, QD_LOG_WARNING, "Attribute password of entity 
sslProfile has been deprecated. Use passwordFile instead.");
+        //
+        // Process the password to handle any modifications or lookups needed
+        //
+        char *actual_pass = 0;
+        bool is_file_path = 0;
+        qd_config_process_password(&actual_pass, ssl_profile->ssl_password, 
&is_file_path, cm->log_source); CHECK();
+        if (actual_pass && is_file_path) {
+            qd_set_password_from_file(actual_pass, &ssl_profile->ssl_password, 
cm->log_source);
+        }
+        else if (actual_pass) {
+            free(ssl_profile->ssl_password);
+            ssl_profile->ssl_password = actual_pass;
+        }
+    }
+    else if (password_file) {
+        //
+        // Warn the user that the passwordFile attribute has been deprecated.
+        //
+        qd_log(cm->log_source, QD_LOG_WARNING, "Attribute passwordFile of 
entity sslProfile has been deprecated. Use password field with the file: prefix 
instead.");
+        qd_set_password_from_file(password_file, &ssl_profile->ssl_password, 
cm->log_source);
     }
 
+    free(password_file);
 
-    if (!ssl_profile->ssl_password) {
-        // SSL password not provided. Check if passwordFile property is 
specified.
-        char *password_file = qd_entity_opt_string(entity, "passwordFile", 0); 
CHECK();
-
-        if (password_file) {
-            FILE *file = fopen(password_file, "r");
-
-            if (file) {
-                char buffer[200];
-
-                int c;
-                int i=0;
-
-                while (i < 200 - 1) {
-                    c = fgetc(file);
-                    if (c == EOF || c == '\n')
-                        break;
-                    buffer[i++] = c;
-                }
-
-                if (i != 0) {
-                    buffer[i] = '\0';
-                    free(ssl_profile->ssl_password);
-                    ssl_profile->ssl_password = strdup(buffer);
-                }
-                fclose(file);
-            }
-        }
-        free(password_file);
-    }
     ssl_profile->ssl_ciphers   = qd_entity_opt_string(entity, "ciphers", 0);   
                CHECK();
     ssl_profile->ssl_protocols = qd_entity_opt_string(entity, "protocols", 0); 
                CHECK();
     ssl_profile->ssl_trusted_certificate_db = qd_entity_opt_string(entity, 
"caCertFile", 0);   CHECK();
@@ -551,11 +603,6 @@ qd_config_ssl_profile_t 
*qd_dispatch_configure_ssl_profile(qd_dispatch_t *qd, qd
     ssl_profile->ssl_uid_format             = qd_entity_opt_string(entity, 
"uidFormat", 0);          CHECK();
     ssl_profile->uid_name_mapping_file      = qd_entity_opt_string(entity, 
"uidNameMappingFile", 0); CHECK();
 
-    //
-    // Process the password to handle any modifications or lookups needed
-    //
-    qd_config_ssl_profile_process_password(ssl_profile); CHECK();
-
     qd_log(cm->log_source, QD_LOG_INFO, "Created SSL Profile with name %s ", 
ssl_profile->name);
     return ssl_profile;
 
diff --git a/tests/system_tests_user_id.py b/tests/system_tests_user_id.py
index 827cd0f..7798c68 100644
--- a/tests/system_tests_user_id.py
+++ b/tests/system_tests_user_id.py
@@ -38,6 +38,8 @@ class QdSSLUseridTest(TestCase):
     def setUpClass(cls):
         super(QdSSLUseridTest, cls).setUpClass()
 
+        os.environ["TLS_SERVER_PASSWORD"] = "server-password"
+
         ssl_profile1_json = os.path.join(DIR, 'displayname_files', 
'profile_names1.json')
         ssl_profile2_json = os.path.join(DIR, 'displayname_files', 
'profile_names2.json')
 
@@ -90,7 +92,9 @@ class QdSSLUseridTest(TestCase):
                              'certFile': 
cls.ssl_file('server-certificate.pem'),
                              'privateKeyFile': 
cls.ssl_file('server-private-key.pem'),
                              'uidFormat': 'cs5',
-                             'password': 'server-password'}),
+                            # Use the env: prefix TLS_SERVER_PASSWORD. The 
TLS_SERVER_PASSWORD
+                            # is set to 'server-password'
+                             'password': 'env:TLS_SERVER_PASSWORD'}),
 
             # no fingerprint field
             ('sslProfile', {'name': 'server-ssl7',
@@ -113,7 +117,9 @@ class QdSSLUseridTest(TestCase):
                              'caCertFile': cls.ssl_file('ca-certificate.pem'),
                              'certFile': 
cls.ssl_file('server-certificate.pem'),
                              'privateKeyFile': 
cls.ssl_file('server-private-key.pem'),
-                             'password': 'server-password'}),
+                            # Use the prefix 'file:' for the password. This 
should read the file and
+                            # use the password from the file.
+                             'password': 'file:' + 
cls.ssl_file('server-password-file.txt')}),
 
             # one component of uidFormat is invalid (x), this will result in 
an error in the fingerprint calculation.
             # The user_id will fall back to proton's pn_transport_get_user
@@ -131,7 +137,9 @@ class QdSSLUseridTest(TestCase):
                              'certFile': 
cls.ssl_file('server-certificate.pem'),
                              'privateKeyFile': 
cls.ssl_file('server-private-key.pem'),
                              'uidFormat': 'abxd',
-                             'password': 'server-password'}),
+                            # Use the prefix 'literal:'. This makes sure we 
maintain
+                            #backward compatability
+                             'password': 'literal:server-password'}),
 
             ('sslProfile', {'name': 'server-ssl12',
                              'caCertFile': cls.ssl_file('ca-certificate.pem'),
@@ -139,7 +147,8 @@ class QdSSLUseridTest(TestCase):
                              'privateKeyFile': 
cls.ssl_file('server-private-key.pem'),
                              'uidFormat': '1',
                              'uidNameMappingFile': ssl_profile1_json,
-                             'password': 'server-password'}),
+                            # Use the pass: followed by the actual password
+                             'password': 'pass:server-password'}),
 
             # should translate a display name
             # specifying both passwordFile and password, password takes 
precedence.
@@ -150,6 +159,9 @@ class QdSSLUseridTest(TestCase):
                             'uidFormat': '2',
                             'uidNameMappingFile': ssl_profile2_json,
                             'password': 'server-password',
+                            # If both passwordFile and password are provided, 
the passwordFile is ignored.
+                            # Here we specify a bad password in the 
passwordFile and
+                            # it is ignored.
                             'passwordFile': 
cls.ssl_file('server-password-file-bad.txt')}),
 
             ('sslProfile', {'name': 'server-ssl14',
@@ -158,6 +170,8 @@ class QdSSLUseridTest(TestCase):
                             'privateKeyFile': 
cls.ssl_file('server-private-key.pem'),
                             'uidFormat': '1',
                             'uidNameMappingFile': ssl_profile1_json,
+                            # Use just the deprecated passwordFile entity to 
make sure it is backward
+                            # compatible.
                             'passwordFile': 
cls.ssl_file('server-password-file.txt')}),
 
             ('listener', {'port': cls.tester.get_port(), 'sslProfile': 
'server-ssl1', 'authenticatePeer': 'yes',


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@qpid.apache.org
For additional commands, e-mail: commits-h...@qpid.apache.org

Reply via email to