From b600dffdc895f29003a8bb801a5afb44c702d7c0 Mon Sep 17 00:00:00 2001 From: Bram Matthys Date: Mon, 15 Oct 2012 21:25:38 +0200 Subject: [PATCH] - Added auth method 'sslclientcertfp' which provides an alternative method to authenticate users with SSL client certificates based on SHA256 fingerprints. This can be used instead of the already existing 'sslclientcert' so you don't have to use an external file. One way to get the SHA256 fingerprint would be: openssl x509 -in name-of-pem-file.pem -sha256 -noout -fingerprint Suggested and patch supplied by Jobe (#4019). - Added documentation on the new sslclientcertfp - Moved documentation on authentication types to one place and refer to it from each section (oper::password, vhost::password, link::password-receive, etc). --- Changes | 11 ++++ doc/unreal32docs.html | 137 +++++++++++++++++++++++++++--------------- include/auth.h | 2 + src/auth.c | 49 ++++++++++++++- 4 files changed, 150 insertions(+), 49 deletions(-) diff --git a/Changes b/Changes index 6e27e2d88..1abd81c9c 100644 --- a/Changes +++ b/Changes @@ -2489,3 +2489,14 @@ building new versions of zlib, openssl, and curl. - Added set::options::disable-cap, which can be used to disable the new CAP support (#4104). +- Added auth method 'sslclientcertfp' which provides an alternative + method to authenticate users with SSL client certificates based + on SHA256 fingerprints. This can be used instead of the already + existing 'sslclientcert' so you don't have to use an external file. + One way to get the SHA256 fingerprint would be: + openssl x509 -in name-of-pem-file.pem -sha256 -noout -fingerprint + Suggested and patch supplied by Jobe (#4019). +- Added documentation on the new sslclientcertfp +- Moved documentation on authentication types to one place and refer + to it from each section (oper::password, vhost::password, + link::password-receive, etc). diff --git a/doc/unreal32docs.html b/doc/unreal32docs.html index 69aa8a0ab..a8c5fddf5 100644 --- a/doc/unreal32docs.html +++ b/doc/unreal32docs.html @@ -69,7 +69,8 @@ English | -- 3.16. Nick Character Sets
-- 3.17. CGI:IRC Support
-- 3.18. Time Synchronization
- -- 3.19. Other features
+ -- 3.19. Authentication Types
+ -- 3.20. Other features
4. Configuring your unrealircd.conf file
---4.1. Configuration file explained
@@ -709,7 +710,79 @@ the IRCd will continue to boot regardlessly (should rarely happen).

the set documentation for more information.

-

3.19 - Other features

+

3.19 - Authentication Types

+

At various places in the configuration file, for example the oper block, allow block and +link block, you can authenticate clients by password or other +means.
+ +You can specify the password as plaintext, but you can also specify an +authentication type.
+The following auth-types are available:
+ + + + + + + + +
Auth-type:Description:Dependencies:How to generate:
cryptUNIX cryptWindows: OpenSSL required/MKPASSWD crypt :password
md5MD5 with saltAlways available/MKPASSWD md5 :password
sha1SHA1 with saltOpenSSL required/MKPASSWD sha1 :password
ripemd160RIPEMD160 with saltOpenSSL required/MKPASSWD ripemd160 :password
sslclientcertSSL Client certificateOpenSSL requiredPath to public certifcate .pem file.
sslclientcertfpSSL Client certificate fingerprintOpenSSL requiredopenssl x509 -in name-of-pem-file.pem -sha256 -noout -fingerprint
+The /MKPASSWD command can be used online as IRCOp. Alternatively, you can +use the command line interface from the shell: ./unreal mkpasswd hashtype +password.
+Not all authentication types are available on all systems, see the +dependencies (prerequisites) in the table of above.
+
+Example: MD5 hashed password in vhost block
+1. Say, you want to use the password test and want to use md5 hashed +passwords.
+ If you are an IRCOp then you can simply type /MKPASSWD md5 :test.
+Alternatively, instead of IRC you can run the following command on the +shell: ./unreal mkpasswd md5 test.
+Using either method, the outputted hashed password will look like $NIV0bSfG$UTMvI/KdMwe4cZqmT/23qw== +(the exact string will vary!)
+2. Now, put this string in your vhost block and let UnrealIRCd know it's an +md5 hash. Example: +

vhost {
+    vhost I.love.Tux;
+    from { userhost *@*; };
+    login Tux;
+    password "$NIV0bSfG$UTMvI/KdMwe4cZqmT/23qw==" { md5; };
+};
+3. To use this vhost, type /VHOST Tux test
+
+Example: Authentication by SSL Client certificates
+sslclientcert and sslclientcertfp are exceptional +auth-types which can be used to authenticate SSL users by their client +certificate.
+With these authentication methods you can be sure the user is using SSL and +is using the specified client certificate.
+Here's an example of how to use it for the oper block:
+1. Create an SSL client certificate if you don't have one already (search + the web for 'create ssl certificate' if you don't know how)
+2. Grab the SHA256 hash of the certificate by running: +
openssl x509 -in name-of-pem-file.pem -sha256 -noout -fingerprint
+3. In the configuration file, replace the original password (test in our example) with the hash +and specify the sslclientcertfp auth-type. Here's an example:
+
oper test {
+     password "E7:4D:46:F1:9F:F4:68:F5:E8:E3:49:CC:28:5D:F9:65:85:BA:4F:16:B6:49:02:E3:34:E6:E7:6A:FE:76:A7:98" { sslclientcertfp; };
+     flags { global; can_override; };
+     class clients;
+   };
+4. Rehash your server (/REHASH).
+5. Now, connect with your SSL client and make sure it uses your SSL client +certificate of step #2.
+6. You can now oper up with /oper test x. The password must still +be specified but may be anything (x in this example), it is ignored +as the SSL client certificate is used for authentication.
+7. Congratulations, you are now using the most secure authentication method +available in UnrealIRCd.
+
+Another very useful place to use sslclientcertfp is in link::password-receive +

+ +

3.20 - Other features

UnrealIRCd has a lot of features so not everything is covered here... You'll find that out by yourself.

@@ -1023,33 +1096,8 @@ listen *:6601 {

The oper::password:: is the password the oper must specify. oper::password::auth-type allows you to specify an authentication method for this password. Don't specify oper::password::auth-type for plaintext password. - Valid auth-types are crypt, md5, sha1, ripemd160, - and sslclientcert. Specifying any one of these types means that the - value of oper::password:: is a hash generated with - mkpasswd. -

-

- sslclientcert is an exceptional auth-type. When this is - chosen as the auth-type, oper::password:: should be a file - path (relative to UnrealIRCd's installation directory). The file - should be a PEM-encoded SSL certificate (the public certificate, not - a key). This specifies that only an IRC client that -

    -
  1. is connected using SSL
  2. -
  3. presented the matching client certficate when connecting
  4. -
  5. has access to the private key associated with the certficiate file
  6. -
- may connect. Of course, this feature requires that UnrealIRCd be - compiled with SSL support. Also, as any SSL certificate's associated - key is much longer than a normal human's password and much more - random, this is the securest authentication option. To oper - up when you have specified a client SSL certificate as the oper's - password, just ensure that the client is set up correctly and issue -
/oper <name> :
. The sslclientcert - auth-type may also be used - for link::password-receive - to secure link blocks. - + Valid auth-types, as was as an example on how to use them with oper + blocks, can be found under Authentication Types.

Please note that BOTH the password and login name are case sensitive.

The oper::class directive specifies the name of a preexisting (appears before @@ -1388,9 +1436,8 @@ drpass { die <die-password> { <auth-type>; }; };

This block sets the /restart and /die passwords with drpass::restart and drpass::die - respectively. The drpass::restart:: and drpass::die:: allow you to specify the - type of authentication used by this item. The currently supported authentication - types are crypt, md5, and sha1, ripemd-160.

+ respectively. + Instead of a plaintext password, you can also use other Authentication Types.

Example:

 drpass {
@@ -1926,8 +1973,10 @@ vhost {
   to be eligible for the vhost. You may specify more than one hostmask. The vhost::login 
   in the login name the user must enter and vhost::password is the password that 
   must be entered. The vhost::password:: allows you to specify the type of 
-  authentication used by this item. The currently supported authentication types 
-  are crypt, md5, and sha1, ripemd-160. Lastly vhost::swhois allows you to add an extra 
+  authentication used by this item. See Authentication Types for a list of available
+  types.
+  Lastly vhost::swhois allows you to add an extra 
   line to a users whois, exactly as it does in the Oper Block oper::swhois.

Example:

@@ -2048,12 +2097,9 @@ link <server-name> {
   The password used for connecting to the remote server, must be plain-text.
  

password-receive
The password used for validating - incoming links. This may be encrypted in the same manner - as oper::password. It may, - notably, be set up to use - the sslclientcert - auth-type. In fact, this is strongly recommended as it is infinitely - more resilient against brute-force attacks. + incoming links. It is strongly recommended to use hashed passwords or + the sslclientcertfp auth-type. See Authentication + Types for more information.

hub vs leaf
A hub has multiple servers linked to it, a leaf has only one link... to you. @@ -3526,14 +3572,9 @@ to get more information on a command.

mkpasswd <auth-type> <password>
- Will encrypt <password> using the <auth-type> hashing method. Available hash methods: -
    -
  • crypt [Windows support requires SSL]
  • -
  • md5
  • -
  • sha1 [requires SSL]
  • -
  • ripemd160 [requires SSL]
  • -
-
+ Will encrypt <password> using the <auth-type> hashing method. + See Authentication Types for available + hash methods. IRCop diff --git a/include/auth.h b/include/auth.h index fa1c19bfe..ed4576bc4 100644 --- a/include/auth.h +++ b/include/auth.h @@ -30,6 +30,7 @@ typedef struct { #define AUTHTYPE_SHA1 3 #define AUTHTYPE_SSL_CLIENTCERT 4 #define AUTHTYPE_RIPEMD160 5 +#define AUTHTYPE_SSL_CLIENTCERTFP 6 /* md5 is always available and enabled as of Unreal3.2.1 */ #define AUTHENABLE_MD5 @@ -37,6 +38,7 @@ typedef struct { #define AUTHENABLE_SHA1 #define AUTHENABLE_SSL_CLIENTCERT #define AUTHENABLE_RIPEMD160 +#define AUTHENABLE_SSL_CLIENTCERTFP /* OpenSSL provides a crypt() */ #ifndef AUTHENABLE_UNIXCRYPT #define AUTHENABLE_UNIXCRYPT diff --git a/src/auth.c b/src/auth.c index 9fe3932cd..ebd9fe1c9 100644 --- a/src/auth.c +++ b/src/auth.c @@ -57,6 +57,9 @@ anAuthStruct MODVAR AuthTypes[] = { {"ripemd160", AUTHTYPE_RIPEMD160}, /* sure, this is ugly, but it's our fault. -- Syzop */ {"ripemd-160", AUTHTYPE_RIPEMD160}, +#endif +#ifdef AUTHENABLE_SSL_CLIENTCERTFP + {"sslclientcertfp", AUTHTYPE_SSL_CLIENTCERTFP}, #endif {NULL, 0} }; @@ -467,11 +470,24 @@ int Auth_Check(aClient *cptr, anAuthStruct *as, char *para) extern char *crypt(); #endif -#ifdef AUTHENABLE_SSL_CLIENTCERT +#if defined(AUTHENABLE_SSL_CLIENTCERT) || defined(AUTHENABLE_SSL_CLIENTCERTFP) X509 *x509_clientcert = NULL; +#endif +#ifdef AUTHENABLE_SSL_CLIENTCERT X509 *x509_filecert = NULL; FILE *x509_f = NULL; #endif +#ifdef AUTHENABLE_SSL_CLIENTCERTFP + unsigned int n; + unsigned int i; + unsigned int j; + unsigned int k; + unsigned char md[EVP_MAX_MD_SIZE]; + char hex[EVP_MAX_MD_SIZE * 2 + 1]; + char hexc[EVP_MAX_MD_SIZE * 3 + 1]; + char hexchars[16] = "0123456789abcdef"; + const EVP_MD *digest = EVP_sha256(); +#endif if (!as) return 1; @@ -542,6 +558,37 @@ int Auth_Check(aClient *cptr, anAuthStruct *as, char *para) X509_free(x509_clientcert); X509_free(x509_filecert); return 2; +#endif +#ifdef AUTHENABLE_SSL_CLIENTCERTFP + case AUTHTYPE_SSL_CLIENTCERTFP: + if (!para) + return -1; + if (!cptr->ssl) + return -1; + x509_clientcert = SSL_get_peer_certificate((SSL *)cptr->ssl); + if (!x509_clientcert) + return -1; + if (!X509_digest(x509_clientcert, digest, md, &n)) { + X509_free(x509_clientcert); + return -1; + } + j = 0; + k = 0; + for (i=0; i> 4) & 0xF]; + hex[j++] = hexchars[md[i] & 0xF]; + hexc[k++] = hexchars[(md[i] >> 4) & 0xF]; + hexc[k++] = hexchars[md[i] & 0xF]; + hexc[k++] = ':'; + } + hex[j] = '\0'; + hexc[--k] = '\0'; + if (strcasecmp(as->data, hex) && strcasecmp(as->data, hexc)) { + X509_free(x509_clientcert); + return -1; + } + X509_free(x509_clientcert); + return 2; #endif } return -1;