unrealircd

- supernets unrealircd source & configuration
git clone git://git.acid.vegas/unrealircd.git
Log | Files | Refs | Archive | README | LICENSE

auth.c (15525B)

      1 /*
      2  *   Unreal Internet Relay Chat Daemon, src/auth.c
      3  *   (C) 2001 Carsten V. Munk (stskeeps@tspre.org)
      4  *   (C) 2003-2019 Bram Matthys (syzop@vulnscan.org) and the UnrealIRCd team
      5  *
      6  *   This program is free software; you can redistribute it and/or modify
      7  *   it under the terms of the GNU General Public License as published by
      8  *   the Free Software Foundation; either version 1, or (at your option)
      9  *   any later version.
     10  *
     11  *   This program is distributed in the hope that it will be useful,
     12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14  *   GNU General Public License for more details.
     15  *
     16  *   You should have received a copy of the GNU General Public License
     17  *   along with this program; if not, write to the Free Software
     18  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     19  */
     20 
     21 #include "unrealircd.h"
     22 #include "crypt_blowfish.h"
     23 
     24 typedef struct AuthTypeList AuthTypeList;
     25 struct AuthTypeList {
     26 	char			*name;
     27 	AuthenticationType	type;
     28 };
     29 
     30 /** The list of authentication types that we support. */
     31 AuthTypeList MODVAR AuthTypeLists[] = {
     32 	{"plain",           AUTHTYPE_PLAINTEXT},
     33 	{"plaintext",       AUTHTYPE_PLAINTEXT},
     34 	{"crypt",           AUTHTYPE_UNIXCRYPT},
     35 	{"unixcrypt",       AUTHTYPE_UNIXCRYPT},
     36 	{"bcrypt",          AUTHTYPE_BCRYPT},
     37 	{"cert",            AUTHTYPE_TLS_CLIENTCERT},
     38 	{"sslclientcert",   AUTHTYPE_TLS_CLIENTCERT},
     39 	{"tlsclientcert",   AUTHTYPE_TLS_CLIENTCERT},
     40 	{"certfp",          AUTHTYPE_TLS_CLIENTCERTFP},
     41 	{"sslclientcertfp", AUTHTYPE_TLS_CLIENTCERTFP},
     42 	{"tlsclientcertfp", AUTHTYPE_TLS_CLIENTCERTFP},
     43 	{"spkifp",          AUTHTYPE_SPKIFP},
     44 	{"argon2",          AUTHTYPE_ARGON2},
     45 	{NULL,              0}
     46 };
     47 
     48 /* Helper function for Auth_AutoDetectHashType() */
     49 static int parsepass(const char *str, char **salt, char **hash)
     50 {
     51 	static char saltbuf[512], hashbuf[512];
     52 	const char *p;
     53 	int max;
     54 
     55 	/* Syntax: $<salt>$<hash> */
     56 	if (*str != '$')
     57 		return 0;
     58 	p = strchr(str+1, '$');
     59 	if (!p || (p == str+1) || !p[1])
     60 		return 0;
     61 
     62 	max = p - str;
     63 	if (max > sizeof(saltbuf))
     64 		max = sizeof(saltbuf);
     65 	strlcpy(saltbuf, str+1, max);
     66 	strlcpy(hashbuf, p+1, sizeof(hashbuf));
     67 	*salt = saltbuf;
     68 	*hash = hashbuf;
     69 	return 1;
     70 }
     71 
     72 /** Auto detect hash type for input hash 'hash'.
     73  * Will fallback to AUTHTYPE_PLAINTEXT when not found (or invalid).
     74  */
     75 int Auth_AutoDetectHashType(const char *hash)
     76 {
     77 	static char hashbuf[256];
     78 	char *saltstr, *hashstr;
     79 	int bits;
     80 
     81 	if (!strchr(hash, '$'))
     82 	{
     83 		/* SHA256 certificate fingerprint perhaps?
     84 		 * These are exactly 64 bytes (00112233..etc..) or 95 bytes (00:11:22:33:etc) in size.
     85 		 */
     86 		if ((strlen(hash) == 64) || (strlen(hash) == 95))
     87 		{
     88 			const char *p;
     89 			char *hexchars = "0123456789abcdefABCDEF";
     90 			for (p = hash; *p; p++)
     91 				if ((*p != ':') && !strchr(hexchars, *p))
     92 					return AUTHTYPE_PLAINTEXT; /* not hex and not colon */
     93 
     94 			return AUTHTYPE_TLS_CLIENTCERTFP;
     95 		}
     96 
     97 		if (strlen(hash) == 44)
     98 		{
     99 			const char *p;
    100 			char *b64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    101 			for (p = hash; *p; p++)
    102 				if (!strchr(b64chars, *p))
    103 					return AUTHTYPE_PLAINTEXT; /* not base64 */
    104 
    105 			return AUTHTYPE_SPKIFP;
    106 		}
    107 	}
    108 
    109 	if ((*hash != '$') || !strchr(hash+1, '$'))
    110 		return AUTHTYPE_PLAINTEXT;
    111 
    112 	if (!strncmp(hash, "$2a$", 4) || !strncmp(hash, "$2b$", 4) || !strncmp(hash, "$2y$", 4))
    113 		return AUTHTYPE_BCRYPT;
    114 
    115 	if (!strncmp(hash, "$argon2", 7))
    116 		return AUTHTYPE_ARGON2;
    117 
    118 	/* Now handle UnrealIRCd-style password hashes.. */
    119 	if (parsepass(hash, &saltstr, &hashstr) == 0)
    120 		return AUTHTYPE_PLAINTEXT; /* old method (pre-3.2.1) or could not detect, fallback. */
    121 
    122 	bits = b64_decode(hashstr, hashbuf, sizeof(hashbuf)) * 8;
    123 	if (bits <= 0)
    124 		return AUTHTYPE_UNIXCRYPT; /* decode failed. likely some other crypt() type. */
    125 
    126 	/* else it's likely some other crypt() type */
    127 	return AUTHTYPE_UNIXCRYPT;
    128 }
    129 
    130 
    131 /** Find authentication type for 'hash' and explicit type 'type'.
    132  * @param hash   The password hash (may be NULL if you are creating a password)
    133  * @param type   An explicit type. In that case we will search by this type, rather
    134  *               than trying to determine the type on the 'hash' parameter.
    135  *               Or leave NULL, then we use hash autodetection.
    136  */
    137 AuthenticationType Auth_FindType(const char *hash, const char *type)
    138 {
    139 	if (type)
    140 	{
    141 		AuthTypeList *e = AuthTypeLists;
    142 		while (e->name)
    143 		{
    144 			if (!mycmp(e->name, type))
    145 				return e->type;
    146 			e++;
    147 		}
    148 		return AUTHTYPE_INVALID; /* Not found */
    149 	}
    150 
    151 	if (hash)
    152 		return Auth_AutoDetectHashType(hash);
    153 
    154 	return AUTHTYPE_INVALID; /* both 'hash' and 'type' are NULL */
    155 }
    156 
    157 /** Check the syntax of an authentication block.
    158  * This is a block like: password "data" { type; };
    159  * in the configuration file.
    160 */
    161 int Auth_CheckError(ConfigEntry *ce)
    162 {
    163 	AuthenticationType type = AUTHTYPE_PLAINTEXT;
    164 	X509 *x509_filecert = NULL;
    165 	FILE *x509_f = NULL;
    166 	if (!ce->value)
    167 	{
    168 		config_error("%s:%i: authentication module failure: missing parameter",
    169 			ce->file->filename, ce->line_number);
    170 		return -1;
    171 	}
    172 	if (ce->items && ce->items->next)
    173 	{
    174 		config_error("%s:%i: you may not have multiple authentication methods",
    175 			ce->file->filename, ce->line_number);
    176 		return -1;
    177 	}
    178 
    179 	type = Auth_FindType(ce->value, ce->items ? ce->items->name : NULL);
    180 	if (type == -1)
    181 	{
    182 		config_error("%s:%i: authentication module failure: %s is not an implemented/enabled authentication method",
    183 			ce->file->filename, ce->line_number,
    184 			ce->items->name);
    185 		return -1;
    186 	}
    187 
    188 	switch (type)
    189 	{
    190 		case AUTHTYPE_UNIXCRYPT:
    191 			/* If our data is like 1 or none, we just let em through .. */
    192 			if (strlen(ce->value) < 2)
    193 			{
    194 				config_error("%s:%i: authentication module failure: AUTHTYPE_UNIXCRYPT: no salt (crypt strings will always be >2 in length)",
    195 					ce->file->filename, ce->line_number);
    196 				return -1;
    197 			}
    198 			break;
    199 		case AUTHTYPE_TLS_CLIENTCERT:
    200 			convert_to_absolute_path(&ce->value, CONFDIR);
    201 			if (!(x509_f = fopen(ce->value, "r")))
    202 			{
    203 				config_error("%s:%i: authentication module failure: AUTHTYPE_TLS_CLIENTCERT: error opening file %s: %s",
    204 					ce->file->filename, ce->line_number, ce->value, strerror(errno));
    205 				return -1;
    206 			}
    207 			x509_filecert = PEM_read_X509(x509_f, NULL, NULL, NULL);
    208 			fclose(x509_f);
    209 			if (!x509_filecert)
    210 			{
    211 				config_error("%s:%i: authentication module failure: AUTHTYPE_TLS_CLIENTCERT: PEM_read_X509 errored in file %s (format error?)",
    212 					ce->file->filename, ce->line_number, ce->value);
    213 				return -1;
    214 			}
    215 			X509_free(x509_filecert);
    216 			break;
    217 		default: ;
    218 	}
    219 
    220 	/* Unix crypt is a bit more complicated: most types are outright 'bad',
    221 	 * while other types have reasonable security similar to 'bcrypt'.
    222 	 * To be honest these people should probably use 'argon2' since it's
    223 	 * a lot better. Then again, warning about this when it's still such
    224 	 * a common hashing method (now, in 2018) may be a bit overzealous.
    225 	 * So: not warning about crypt types $5/$6 which use SHA256/SHA512
    226 	 * with normally at least 5000 rounds (unless deliberately weakened
    227 	 * by the user).
    228 	 */
    229 	if ((type == AUTHTYPE_UNIXCRYPT) && strncmp(ce->value, "$5", 2) &&
    230 	    strncmp(ce->value, "$6", 2) && !strstr(ce->value, "$rounds"))
    231 	{
    232 		config_warn("%s:%i: Using simple crypt for authentication is not recommended. "
    233 		            "Consider using the more secure auth-type 'argon2' instead. "
    234 		            "See https://www.unrealircd.org/docs/Authentication_types for the complete list.",
    235                             ce->file->filename, ce->line_number);
    236 		/* do not return, not an error. */
    237 	}
    238 	if ((type == AUTHTYPE_PLAINTEXT) && (strlen(ce->value) > PASSWDLEN))
    239 	{
    240 		config_error("%s:%i: passwords length may not exceed %d",
    241 			ce->file->filename, ce->line_number, PASSWDLEN);
    242 		return -1;
    243 	}
    244 	return 1;
    245 }
    246 
    247 /** Convert an authentication block from the configuration file
    248  * into an AuthConfig structure so it can be used at runtime.
    249  */
    250 AuthConfig *AuthBlockToAuthConfig(ConfigEntry *ce)
    251 {
    252 	AuthenticationType type = AUTHTYPE_PLAINTEXT;
    253 	AuthConfig *as = NULL;
    254 
    255 	type = Auth_FindType(ce->value, ce->items ? ce->items->name : NULL);
    256 	if (type == AUTHTYPE_INVALID)
    257 		type = AUTHTYPE_PLAINTEXT;
    258 
    259 	as = safe_alloc(sizeof(AuthConfig));
    260 	safe_strdup(as->data, ce->value);
    261 	as->type = type;
    262 	return as;
    263 }
    264 
    265 /** Free an AuthConfig struct */
    266 void Auth_FreeAuthConfig(AuthConfig *as)
    267 {
    268 	if (as)
    269 	{
    270 		safe_free(as->data);
    271 		safe_free(as);
    272 	}
    273 }
    274 
    275 /* RAW salt length (before b64_encode) to use in /MKPASSWD
    276  * and REAL salt length (after b64_encode, including terminating nul),
    277  * used for reserving memory.
    278  */
    279 #define RAWSALTLEN		6
    280 #define REALSALTLEN		12
    281 
    282 static int authcheck_argon2(Client *client, AuthConfig *as, const char *para)
    283 {
    284 	argon2_type hashtype;
    285 
    286 	if (!para)
    287 		return 0;
    288 
    289 	/* Find out the hashtype. Why do we need to do this, why is this
    290 	 * not in the library or irrelevant by using some generic function?
    291 	 */
    292 	if (!strncmp(as->data, "$argon2id", 9))
    293 		hashtype = Argon2_id;
    294 	else if (!strncmp(as->data, "$argon2i", 8))
    295 		hashtype = Argon2_i;
    296 	else if (!strncmp(as->data, "$argon2d", 8))
    297 		hashtype = Argon2_d;
    298 	else
    299 		return 0; /* unknown argon2 type */
    300 
    301 	if (argon2_verify(as->data, para, strlen(para), hashtype) == ARGON2_OK)
    302 		return 1; /* MATCH */
    303 
    304 	return 0; /* NO MATCH or error */
    305 }
    306 
    307 static int authcheck_bcrypt(Client *client, AuthConfig *as, const char *para)
    308 {
    309 	char data[512]; /* NOTE: only 64 required by BF_crypt() */
    310 	char *str;
    311 
    312 	if (!para)
    313 		return 0;
    314 
    315 	memset(data, 0, sizeof(data));
    316 	str = _crypt_blowfish_rn(para, as->data, data, sizeof(data));
    317 
    318 	if (!str)
    319 		return 0; /* ERROR / INVALID HASH */
    320 
    321 	if (!strcmp(str, as->data))
    322 		return 1; /* MATCH */
    323 
    324 	return 0; /* NO MATCH */
    325 }
    326 
    327 static int authcheck_tls_clientcert(Client *client, AuthConfig *as, const char *para)
    328 {
    329 	X509 *x509_clientcert = NULL;
    330 	X509 *x509_filecert = NULL;
    331 	FILE *x509_f = NULL;
    332 
    333 	if (!client->local->ssl)
    334 		return 0;
    335 	x509_clientcert = SSL_get_peer_certificate(client->local->ssl);
    336 	if (!x509_clientcert)
    337 		return 0;
    338 	if (!(x509_f = fopen(as->data, "r")))
    339 	{
    340 		X509_free(x509_clientcert);
    341 		return 0;
    342 	}
    343 	x509_filecert = PEM_read_X509(x509_f, NULL, NULL, NULL);
    344 	fclose(x509_f);
    345 	if (!x509_filecert)
    346 	{
    347 		X509_free(x509_clientcert);
    348 		return 0;
    349 	}
    350 	if (X509_cmp(x509_filecert, x509_clientcert) != 0)
    351 	{
    352 		X509_free(x509_clientcert);
    353 		X509_free(x509_filecert);
    354 		return 0;
    355 	}
    356 	X509_free(x509_clientcert);
    357 	X509_free(x509_filecert);
    358 	return 1;
    359 }
    360 
    361 static int authcheck_tls_clientcert_fingerprint(Client *client, AuthConfig *as, const char *para)
    362 {
    363 	int i, k;
    364 	char hexcolon[EVP_MAX_MD_SIZE * 3 + 1];
    365 	const char *fp;
    366 
    367 	if (!client->local->ssl)
    368 		return 0;
    369 
    370 	fp = moddata_client_get(client, "certfp");
    371 	if (!fp)
    372 		return 0;
    373 
    374 	/* Make a colon version so that we keep in line with
    375 	 * previous versions, based on Nath's patch -dboyz
    376 	 */
    377 	k=0;
    378 	for (i=0; i<strlen(fp); i++)
    379 	{
    380 		if (i != 0 && i % 2 == 0)
    381 			hexcolon[k++] = ':';
    382 		hexcolon[k++] = fp[i];
    383 	}
    384 	hexcolon[k] = '\0';
    385 
    386 	if (strcasecmp(as->data, hexcolon) && strcasecmp(as->data, fp))
    387 		return 0;
    388 
    389 	return 1;
    390 }
    391 
    392 static int authcheck_spkifp(Client *client, AuthConfig *as, const char *para)
    393 {
    394 	const char *fp = spki_fingerprint(client);
    395 
    396 	if (!fp)
    397 		return 0; /* auth failed: not TLS or some other failure */
    398 
    399 	if (strcasecmp(as->data, fp))
    400 		return 0; /* auth failed: mismatch */
    401 
    402 	return 1; /* SUCCESS */
    403 }
    404 
    405 
    406 /*
    407  * client MUST be a local client
    408  * as is what it will be compared with
    409  * para will used in coordination with the auth type	
    410 */
    411 
    412 /** Check authentication, such as a password against the
    413  * provided AuthConfig (which was parsed from the configuration
    414  * file earlier).
    415  * @param client    The client.
    416  * @param as      The authentication config.
    417  * @param para    The provided parameter (NULL allowed)
    418  * @returns 1 if passed, 0 if incorrect (eg: invalid password)
    419  * @note
    420  * - The return value was different in versions before UnrealIRCd 5.0.0!
    421  * - In older versions a NULL 'as' was treated as an allow, now it's deny.
    422  */
    423 int Auth_Check(Client *client, AuthConfig *as, const char *para)
    424 {
    425 	extern char *crypt();
    426 	char *res;
    427 
    428 	if (!as || !as->data)
    429 		return 0; /* Should not happen, but better be safe.. */
    430 
    431 	switch (as->type)
    432 	{
    433 		case AUTHTYPE_PLAINTEXT:
    434 			if (!para)
    435 				return 0;
    436 			if (!strcmp(as->data, "changemeplease") && !strcmp(para, as->data))
    437 			{
    438 				unreal_log(ULOG_INFO, "auth", "AUTH_REJECT_DEFAULT_PASSWORD", client,
    439 				           "Rejecting default password 'changemeplease'. "
    440 				           "Please change the password in the configuration file.");
    441 				return 0;
    442 			}
    443 			/* plain text compare */
    444 			if (!strcmp(para, as->data))
    445 				return 1;
    446 			return 0;
    447 
    448 		case AUTHTYPE_ARGON2:
    449 			return authcheck_argon2(client, as, para);
    450 
    451 		case AUTHTYPE_BCRYPT:
    452 			return authcheck_bcrypt(client, as, para);
    453 
    454 		case AUTHTYPE_UNIXCRYPT:
    455 			if (!para)
    456 				return 0;
    457 			res = crypt(para, as->data);
    458 			if (res && !strcmp(res, as->data))
    459 				return 1;
    460 			return 0;
    461 
    462 		case AUTHTYPE_TLS_CLIENTCERT:
    463 			return authcheck_tls_clientcert(client, as, para);
    464 
    465 		case AUTHTYPE_TLS_CLIENTCERTFP:
    466 			return authcheck_tls_clientcert_fingerprint(client, as, para);
    467 
    468 		case AUTHTYPE_SPKIFP:
    469 			return authcheck_spkifp(client, as, para);
    470 
    471 		case AUTHTYPE_INVALID:
    472 			return 0; /* Should never happen */
    473 	}
    474 	return 0;
    475 }
    476 
    477 #define UNREALIRCD_ARGON2_DEFAULT_TIME_COST             3
    478 #define UNREALIRCD_ARGON2_DEFAULT_MEMORY_COST           8192
    479 #define UNREALIRCD_ARGON2_DEFAULT_PARALLELISM_COST      2
    480 #define UNREALIRCD_ARGON2_DEFAULT_HASH_LENGTH           32
    481 #define UNREALIRCD_ARGON2_DEFAULT_SALT_LENGTH           (128/8)
    482 
    483 static char *mkpass_argon2(const char *para)
    484 {
    485 	static char buf[512];
    486 	char salt[UNREALIRCD_ARGON2_DEFAULT_SALT_LENGTH];
    487 	int ret, i;
    488 
    489 	if (!para)
    490 		return NULL;
    491 
    492 	/* Initialize salt */
    493 	for (i=0; i < sizeof(salt); i++)
    494 		salt[i] = getrandom8();
    495 
    496 	*buf = '\0';
    497 
    498 	ret = argon2id_hash_encoded(UNREALIRCD_ARGON2_DEFAULT_TIME_COST,
    499 	                            UNREALIRCD_ARGON2_DEFAULT_MEMORY_COST,
    500 	                            UNREALIRCD_ARGON2_DEFAULT_PARALLELISM_COST,
    501 	                            para,
    502 	                            strlen(para),
    503 	                            salt,
    504 	                            sizeof(salt),
    505 	                            UNREALIRCD_ARGON2_DEFAULT_HASH_LENGTH,
    506 	                            buf,
    507 	                            sizeof(buf));
    508 
    509 	if (ret != ARGON2_OK)
    510 		return NULL; /* internal error */
    511 
    512 	return buf;
    513 }
    514 
    515 static char *mkpass_bcrypt(const char *para)
    516 {
    517 	static char buf[128];
    518 	char data[512]; /* NOTE: only 64 required by BF_crypt() */
    519 	char salt[64];
    520 	char random_data[32];
    521 	char *str;
    522 	char *saltstr;
    523 	int i;
    524 
    525 	if (!para)
    526 		return NULL;
    527 
    528 	memset(data, 0, sizeof(data));
    529 
    530 	for (i=0; i<sizeof(random_data); i++)
    531 		random_data[i] = getrandom8();
    532 
    533 	saltstr = _crypt_gensalt_blowfish_rn("$2y", 9, random_data, sizeof(random_data), salt, sizeof(salt));
    534 	if (!saltstr)
    535 		return NULL;
    536 
    537 	str = _crypt_blowfish_rn(para, saltstr, data, sizeof(data));
    538 
    539 	if (!str)
    540 		return NULL; /* INTERNAL ERROR */
    541 
    542 	strlcpy(buf, str, sizeof(buf));
    543 	return buf;
    544 }
    545 
    546 /** Create a hashed password for the specified string.
    547  * @param type  One of AUTHTYPE_*, eg AUTHTYPE_ARGON2.
    548  * @param text  The password in plaintext.
    549  * @returns The hashed password.
    550  */
    551 const char *Auth_Hash(AuthenticationType type, const char *text)
    552 {
    553 	switch (type)
    554 	{
    555 		case AUTHTYPE_PLAINTEXT:
    556 			return text;
    557 
    558 		case AUTHTYPE_ARGON2:
    559 			return mkpass_argon2(text);
    560 
    561 		case AUTHTYPE_BCRYPT:
    562 			return mkpass_bcrypt(text);
    563 
    564 		default:
    565 			return NULL;
    566 	}
    567 }