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 }