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).
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: |
| crypt | UNIX crypt | Windows: OpenSSL required | /MKPASSWD crypt :password |
| md5 | MD5 with salt | Always available | /MKPASSWD md5 :password |
| sha1 | SHA1 with salt | OpenSSL required | /MKPASSWD sha1 :password |
| ripemd160 | RIPEMD160 with salt | OpenSSL required | /MKPASSWD ripemd160 :password |
| sslclientcert | SSL Client certificate | OpenSSL required | Path to public certifcate .pem file. |
| sslclientcertfp | SSL Client certificate fingerprint | OpenSSL required | openssl x509 -in name-of-pem-file.pem -sha256 -noout -fingerprint |
vhost {
+ vhost I.love.Tux;
+ from { userhost *@*; };
+ login Tux;
+ password "$NIV0bSfG$UTMvI/KdMwe4cZqmT/23qw==" { md5; };
+};
+3. To use this vhost, type /VHOST Tux testopenssl 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).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 -
/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;