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;