1
0
mirror of https://gitlab.xiph.org/xiph/icecast-server.git synced 2024-09-22 04:15:54 -04:00

Merge branch 'update-password-hash' into devel

Closes: #2010
This commit is contained in:
Philipp Schafft 2023-02-21 23:39:50 +00:00
commit d7f401e36c
14 changed files with 431 additions and 370 deletions

View File

@ -100,6 +100,7 @@ AC_CHECK_HEADERS([sys/timeb.h])
AC_CHECK_HEADERS([sys/socket.h])
AC_CHECK_HEADERS([pwd.h grp.h])
AC_CHECK_HEADERS([sys/resource.h])
AC_CHECK_HEADERS([crypt.h])
AC_C_BIGENDIAN
@ -122,6 +123,15 @@ AC_CHECK_FUNCS([gettimeofday])
AC_CHECK_FUNCS([ftime])
AC_CHECK_FUNCS([getrlimit])
dnl check for crypt():
AC_SEARCH_LIBS([crypt_r], [crypt], [
AC_DEFINE([HAVE_CRYPT_R], [1], [Define if you have crypt_r])
], [
AC_SEARCH_LIBS([crypt], [crypt], [
AC_DEFINE([HAVE_CRYPT], [1], [Define if you have crypt])
])
])
dnl Do not check for poll on Darwin, it is broken in some versions
AS_IF([test "${SYS}" != "darwin"], [
AC_CHECK_FUNCS([poll])
@ -226,6 +236,26 @@ PKG_HAVE_WITH_MODULES([OPENSSL], [openssl >= 1.1.0], [
LIBS="${LIBS} ${OPENSSL_LIBS}"
])
dnl
dnl librhash - first try pkgconfig and then basic search
dnl since the function is defined in rhash.h we need to check for that first,
dnl before we can check for the function, bit crude but seems to work.
dnl
PKG_CHECK_MODULES([LIBRHASH], [librhash], [
CFLAGS="${CFLAGS} ${LIBRHASH_CFLAGS}"
LIBS="${LIBS} ${LIBRHASH_LIBS}"
], [
AC_CHECK_HEADER([rhash.h], [
AC_CHECK_LIB(rhash, rhash_library_init, [], [
AC_MSG_ERROR([${LIBRHASH_PKG_ERRORS}. librhash is required.])
])
], [
AC_MSG_ERROR([${LIBRHASH_PKG_ERRORS}. librhash is required.])
])
])
dnl Check for library-specific functions
AC_CHECK_FUNCS([xsltSaveResultToString])

View File

@ -17,6 +17,7 @@ noinst_HEADERS = \
global.h \
util.h \
util_string.h \
util_crypt.h \
errors.h \
curl.h \
slave.h \
@ -29,7 +30,6 @@ noinst_HEADERS = \
fserve.h \
xslt.h \
yp.h \
md5.h \
digest.h \
prng.h \
matchfile.h \
@ -71,6 +71,7 @@ icecast_SOURCES = \
global.c \
util.c \
util_string.c \
util_crypt.c \
errors.c \
slave.c \
source.c \
@ -82,7 +83,6 @@ icecast_SOURCES = \
fserve.c \
admin.c \
resourcematch.c \
md5.c \
digest.c \
prng.c \
matchfile.c \

View File

@ -80,6 +80,7 @@
#include "reportxml.h"
#include "reportxml_helper.h"
#include "xml2json.h"
#include "util_crypt.h"
#include "format.h"
@ -1839,6 +1840,10 @@ static void command_dashboard (client_t *client, source_t *source, adm
}
}
if (!util_crypt_is_new_secure()) {
__reportxml_add_maintenance(reportnode, config->reportxml_db, "40d134e3-fbbe-46b1-a409-9b2ca8954528", "warning", "No secure password hash support detected.", NULL);
}
reportxml_helper_add_value_health(resource, "status", health);
reportxml_node_add_child(incident, resource);
@ -1922,6 +1927,12 @@ static void command_version (client_t *client, source_t *source, adm
#ifdef HAVE_GETADDRINFO
"getaddrinfo",
#endif
#ifdef HAVE_CRYPT
"crypt",
#endif
#ifdef HAVE_CRYPT_R
"crypt_r",
#endif
#ifdef WIN32
"win32",
#endif
@ -2035,6 +2046,16 @@ static void command_version (client_t *client, source_t *source, adm
reportxml_helper_add_value_flag(rflags, "bound-inet6", listensocket_container_is_family_included(global.listensockets, SOCK_FAMILY_INET6));
global_unlock();
reportxml_helper_add_value_flag(rflags, "crypt-1", util_crypt_is_supported("$1$"));
reportxml_helper_add_value_flag(rflags, "crypt-3", util_crypt_is_supported("$3$"));
reportxml_helper_add_value_flag(rflags, "crypt-5", util_crypt_is_supported("$5$"));
reportxml_helper_add_value_flag(rflags, "crypt-6", util_crypt_is_supported("$6$"));
reportxml_helper_add_value_flag(rflags, "crypt-7", util_crypt_is_supported("$7$"));
reportxml_helper_add_value_flag(rflags, "crypt-md5", util_crypt_is_supported("$md5$"));
reportxml_helper_add_value_flag(rflags, "crypt-sha1", util_crypt_is_supported("$sha1$"));
reportxml_helper_add_value_flag(rflags, "crypt-y", util_crypt_is_supported("$y$"));
reportxml_helper_add_value_flag(rflags, "crypt-gy", util_crypt_is_supported("$gy$"));
refobject_unref(config);
refobject_unref(dependencies);
refobject_unref(cflags);

View File

@ -31,7 +31,7 @@
#include "client.h"
#include "cfgfile.h"
#include "common/httpp/httpp.h"
#include "md5.h"
#include "util_crypt.h"
#include "logging.h"
#define CATMODULE "auth_htpasswd"
@ -68,22 +68,6 @@ static void htpasswd_clear(auth_t *self)
}
/* md5 hash */
static char *get_hash(const char *data, int len)
{
struct MD5Context context;
unsigned char digest[16];
MD5Init(&context);
MD5Update(&context, (const unsigned char *)data, len);
MD5Final(digest, &context);
return util_bin_to_hex(digest, 16);
}
static int compare_users(void *arg, void *a, void *b)
{
htpasswd_user *user1 = (htpasswd_user *)a;
@ -200,15 +184,11 @@ static auth_result htpasswd_auth (auth_client *auth_user)
entry.name = client->username;
if (avl_get_by_key (htpasswd->users, &entry, &result) == 0) {
htpasswd_user *found = result;
char *hashed_pw;
thread_rwlock_unlock (&htpasswd->file_rwlock);
hashed_pw = get_hash (client->password, strlen (client->password));
if (strcmp (found->pass, hashed_pw) == 0) {
free (hashed_pw);
if (util_crypt_check(client->password, found->pass)) {
return AUTH_OK;
}
free (hashed_pw);
ICECAST_LOG_DEBUG("incorrect password for client with username: %s", client->username);
return AUTH_FAILED;
}
@ -291,7 +271,7 @@ static auth_result htpasswd_adduser (auth_t *auth, const char *username, const c
return AUTH_FAILED;
}
hashed_password = get_hash(password, strlen(password));
hashed_password = util_crypt_hash(password);
if (hashed_password) {
fprintf(passwdfile, "%s:%s\n", username, hashed_password);
free(hashed_password);

View File

@ -16,11 +16,13 @@
/* for strcmp() and strdup() */
#include <string.h>
#include <stdbool.h>
#include "auth.h"
#include "cfgfile.h"
#include "client.h"
#include "util.h"
#include "util_crypt.h"
#include "logging.h"
#define CATMODULE "auth_static"
@ -30,6 +32,7 @@ typedef struct auth_static {
char *password;
auth_alter_t action;
char *arg;
bool is_hashed;
} auth_static_t;
static auth_result static_auth(auth_client *auth_user)
@ -48,8 +51,13 @@ static auth_result static_auth(auth_client *auth_user)
if (!client->password)
return AUTH_NOMATCH;
if (strcmp(auth_info->password, client->password) != 0)
return AUTH_FAILED;
if (auth_info->is_hashed) {
if (!util_crypt_check(client->password, auth_info->password))
return AUTH_FAILED;
} else {
if (strcmp(auth_info->password, client->password) != 0)
return AUTH_FAILED;
}
if (auth_info->action != AUTH_ALTER_NOOP) {
@ -118,6 +126,8 @@ int auth_get_static_auth (auth_t *authenticator, config_options_t *options)
if (auth_info->password)
free(auth_info->password);
auth_info->password = strdup(options->value);
} else if (strcmp(options->name, "hashed") == 0) {
auth_info->is_hashed = util_str_to_bool(options->value);
} else if (strcmp(options->name, "action") == 0) {
auth_info->action = auth_str2alter(options->value);
if (auth_info->action == AUTH_ALTER_NOOP) {

View File

@ -17,7 +17,6 @@
#include <stdlib.h>
#include "digest.h"
#include "md5.h"
#include "logging.h"
#define CATMODULE "digest"
@ -32,7 +31,6 @@ struct digest_tag {
/* state */
int done;
union {
struct MD5Context md5;
struct {
/* 1600 bits algorithm hashing state */
uint64_t hash[25];
@ -328,9 +326,6 @@ const char *digest_algo_id2str(digest_algo_t algo)
{
switch (algo) {
case DIGEST_ALGO_MD5:
return "MD5";
break;
case DIGEST_ALGO_SHA3_224:
return "SHA3-224";
break;
@ -350,9 +345,6 @@ const char *digest_algo_id2str(digest_algo_t algo)
ssize_t digest_algo_length_bytes(digest_algo_t algo)
{
switch (algo) {
case DIGEST_ALGO_MD5:
return 16;
break;
case DIGEST_ALGO_SHA3_224:
return 224/8;
break;
@ -379,9 +371,6 @@ digest_t * digest_new(digest_algo_t algo)
digest->algo = algo;
switch (algo) {
case DIGEST_ALGO_MD5:
MD5Init(&(digest->state.md5));
break;
case DIGEST_ALGO_SHA3_224:
sha3_init(digest, 224);
break;
@ -427,10 +416,6 @@ ssize_t digest_write(digest_t *digest, const void *data, size_t len)
return -1;
switch (digest->algo) {
case DIGEST_ALGO_MD5:
MD5Update(&(digest->state.md5), (const unsigned char *)data, len);
return len;
break;
case DIGEST_ALGO_SHA3_224:
case DIGEST_ALGO_SHA3_256:
case DIGEST_ALGO_SHA3_384:
@ -455,17 +440,6 @@ ssize_t digest_read(digest_t *digest, void *buf, size_t len)
digest->done = 1;
switch (digest->algo) {
case DIGEST_ALGO_MD5:
if (len < HASH_LEN) {
unsigned char buffer[HASH_LEN];
MD5Final(buffer, &(digest->state.md5));
memcpy(buf, buffer, len);
return len;
} else {
MD5Final((unsigned char*)buf, &(digest->state.md5));
return HASH_LEN;
}
break;
case DIGEST_ALGO_SHA3_224:
case DIGEST_ALGO_SHA3_256:
case DIGEST_ALGO_SHA3_384:
@ -490,7 +464,6 @@ ssize_t digest_length_bytes(digest_t *digest)
static size_t __digest_algo_blocklength(digest_algo_t algo)
{
switch (algo) {
case DIGEST_ALGO_MD5: return 64; break;
case DIGEST_ALGO_SHA3_224: return 144; break;
case DIGEST_ALGO_SHA3_256: return 136; break;
case DIGEST_ALGO_SHA3_384: return 104; break;

View File

@ -15,7 +15,6 @@ REFOBJECT_FORWARD_TYPE(digest_t);
REFOBJECT_FORWARD_TYPE(hmac_t);
typedef enum {
DIGEST_ALGO_MD5,
DIGEST_ALGO_SHA3_224,
DIGEST_ALGO_SHA3_256,
DIGEST_ALGO_SHA3_384,

View File

@ -62,6 +62,8 @@
#include <pwd.h>
#endif
#include <rhash.h>
#include "main.h"
#include "cfgfile.h"
#include "util.h"
@ -604,6 +606,11 @@ int main(int argc, char **argv)
return 1;
}
/* likely libigloo already did this for us, however how should we know?
* Still/Only required for htpasswd MD5 support.
*/
rhash_library_init();
/* parse the '-c icecast.xml' option
** only, so that we can read a configfile
*/

280
src/md5.c
View File

@ -1,280 +0,0 @@
/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@xiph.org>
* and others (see AUTHORS for details).
* Copyright 2014-2015, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
/*
* md5.c
*
* This code implements the MD5 message-digest algorithm.
* The algorithm is due to Ron Rivest. This code was
* written by Colin Plumb in 1993, no copyright is claimed.
* This code is in the public domain; do with it what you wish.
*
* Equivalent code is available from RSA Data Security, Inc.
* This code has been tested against that, and is equivalent,
* except that you don't need to include two pages of legalese
* with every copy.
*
* To compute the message digest of a chunk of bytes, declare an
* MD5Context structure, pass it to MD5Init, call MD5Update as
* needed on buffers full of bytes, and then call MD5Final, which
* will fill a supplied 16-byte array with the digest.
*/
/* Modified for icecast by Mike Smith <msmith@xiph.org>, mostly changing header
* and type definitions
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "compat.h"
#include "md5.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
static void MD5Transform(uint32_t buf[4], uint32_t const in[HASH_LEN]);
/*
* Note: this code is harmless on little-endian machines.
*/
static void byteReverse(unsigned char *buf, unsigned longs)
{
uint32_t t;
do
{
t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
((unsigned) buf[1] << 8 | buf[0]);
*(uint32_t *) buf = t;
buf += 4;
}
while (--longs);
}
/*
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
* initialization constants.
*/
void MD5Init(struct MD5Context *ctx)
{
ctx->buf[0] = 0x67452301;
ctx->buf[1] = 0xefcdab89;
ctx->buf[2] = 0x98badcfe;
ctx->buf[3] = 0x10325476;
ctx->bits[0] = 0;
ctx->bits[1] = 0;
}
/*
* Update context to reflect the concatenation of another buffer full
* of bytes.
*/
void MD5Update(struct MD5Context *ctx, unsigned char const *buf,
unsigned len)
{
uint32_t t;
/* Update bitcount */
t = ctx->bits[0];
if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
ctx->bits[1]++;
/* Carry from low to high */
ctx->bits[1] += len >> 29;
t = (t >> 3) & 0x3f;
/* Bytes already in shsInfo->data */
/* Handle any leading odd-sized chunks */
if (t)
{
unsigned char *p = (unsigned char *) ctx->in + t;
t = 64 - t;
if (len < t)
{
memcpy(p, buf, len);
return;
}
memcpy(p, buf, t);
byteReverse(ctx->in, HASH_LEN);
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
buf += t;
len -= t;
}
/* Process data in 64-byte chunks */
while (len >= 64)
{
memcpy(ctx->in, buf, 64);
byteReverse(ctx->in, HASH_LEN);
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
buf += 64;
len -= 64;
}
/* Handle any remaining bytes of data. */
memcpy(ctx->in, buf, len);
}
/*
* Final wrapup - pad to 64-byte boundary with the bit pattern
* 1 0* (64-bit count of bits processed, MSB-first)
*/
void MD5Final(unsigned char digest[HASH_LEN], struct MD5Context *ctx)
{
unsigned count;
unsigned char *p;
/* Compute number of bytes mod 64 */
count = (ctx->bits[0] >> 3) & 0x3F;
/* Set the first char of padding to 0x80. This is safe since there is
always at least one byte free */
p = ctx->in + count;
*p++ = 0x80;
/* Bytes of padding needed to make 64 bytes */
count = 64 - 1 - count;
/* Pad out to 56 mod 64 */
if (count < 8)
{
/* Two lots of padding: Pad the first block to 64 bytes */
memset(p, 0, count);
byteReverse(ctx->in, HASH_LEN);
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
/* Now fill the next block with 56 bytes */
memset(ctx->in, 0, 56);
}
else
{
/* Pad block to 56 bytes */
memset(p, 0, count - 8);
}
byteReverse(ctx->in, 14);
/* Append length in bits and transform */
((uint32_t *) ctx->in)[14] = ctx->bits[0];
((uint32_t *) ctx->in)[15] = ctx->bits[1];
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
byteReverse((unsigned char *) ctx->buf, 4);
memcpy(digest, ctx->buf, HASH_LEN);
memset(ctx, 0, sizeof(*ctx));
/* In case it's sensitive */
}
/* The four core functions - F1 is optimized somewhat */
/* #define F1(x, y, z) (x & y | ~x & z) */
# define F1(x, y, z) (z ^ (x & (y ^ z)))
# define F2(x, y, z) F1(z, x, y)
# define F3(x, y, z) (x ^ y ^ z)
# define F4(x, y, z) (y ^ (x | ~z))
/* This is the central step in the MD5 algorithm. */
# define MD5STEP(f, w, x, y, z, data, s) do { w += f(x, y, z) + data; w = (w<<s) | (w>>(32-s)); w += x; }while(0)
/*
* The core of the MD5 algorithm, this alters an existing MD5 hash to
* reflect the addition of 16 longwords of new data. MD5Update blocks
* the data and converts bytes into longwords for this routine.
*/
static void MD5Transform(uint32_t buf[4], uint32_t const in[HASH_LEN])
{
register uint32_t a, b, c, d;
a = buf[0];
b = buf[1];
c = buf[2];
d = buf[3];
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
buf[0] += a;
buf[1] += b;
buf[2] += c;
buf[3] += d;
}

View File

@ -1,35 +0,0 @@
/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@xiph.org>
* and others (see AUTHORS for details).
*/
#ifndef __MD5_H__
#define __MD5_H__
#include "compat.h"
#define HASH_LEN 16
struct MD5Context
{
uint32_t buf[4];
uint32_t bits[2];
unsigned char in[64];
};
void MD5Init(struct MD5Context *context);
void MD5Update(struct MD5Context *context, unsigned char const *buf,
unsigned len);
void MD5Final(unsigned char digest[HASH_LEN], struct MD5Context *context);
#endif

View File

@ -33,5 +33,11 @@ ctest_string_renderer_test_LDADD = \
icecast-string_renderer.o
check_PROGRAMS += ctest_string_renderer.test
ctest_crypt_test_SOURCES = tests/ctest_crypt.c
ctest_crypt_test_LDADD = \
icecast-util_string.o \
icecast-util_crypt.o
check_PROGRAMS += ctest_crypt.test
# Add all programs to TESTS
TESTS = $(check_PROGRAMS)

78
src/tests/ctest_crypt.c Normal file
View File

@ -0,0 +1,78 @@
/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2018-2023, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <rhash.h>
#include <igloo/tap.h>
#include <igloo/ro.h>
#include "../util_crypt.h"
/* Workaround: Avoiding the need to add global.c */
igloo_ro_t igloo_instance = igloo_RO_NULL;
void test_md5_hash(const char *in, const char *expect, bool positive)
{
char *out = util_crypt_hash_oldstyle(in);
if (positive) {
igloo_tap_test("md5 positive vector", strcmp(out, expect) == 0);
igloo_tap_test("md5 positive match", util_crypt_check(in, expect));
} else {
igloo_tap_test("md5 negative vector", strcmp(out, expect) != 0);
igloo_tap_test("md5 negative match", !util_crypt_check(in, expect));
}
free(out);
}
static void test_md5(void)
{
struct vector {
const char *in;
const char *out;
};
static const struct vector table_pos[] = {
{"", "d41d8cd98f00b204e9800998ecf8427e"},
{"\n", "68b329da9893e34099c7d8ad5cb9c940"}
};
static const struct vector table_neg[] = {
{"XXX", "d41d8cd98f00b204e9800998ecf8427e"},
{"YYY", "1234567890abcdef1234567890abcdef"}
};
size_t i;
for (i = 0; i < (sizeof(table_pos)/sizeof(*table_pos)); i++) {
test_md5_hash(table_pos[i].in, table_pos[i].out, true);
}
for (i = 0; i < (sizeof(table_neg)/sizeof(*table_neg)); i++) {
test_md5_hash(table_neg[i].in, table_neg[i].out, false);
}
}
int main (void)
{
igloo_tap_init();
igloo_tap_exit_on(igloo_TAP_EXIT_ON_FIN, NULL);
rhash_library_init();
igloo_tap_group_run("md5 vectors", test_md5);
igloo_tap_fin();
return EXIT_FAILURE; // return failure as we should never reach this point!
}

250
src/util_crypt.c Normal file
View File

@ -0,0 +1,250 @@
/* Icecast
*
* Copyright 2023 Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <rhash.h>
#ifdef HAVE_CRYPT_H
#include <crypt.h>
#endif
#if (defined(HAVE_CRYPT_R) || defined(HAVE_CRYPT)) && defined(HAVE_PTHREAD)
#include <pthread.h>
#endif
#include <igloo/prng.h>
#include "global.h"
#include "util_crypt.h"
#include "util_string.h"
#define HASH_LEN 16
#if !defined(HAVE_CRYPT_R) && defined(HAVE_CRYPT) && defined(HAVE_PTHREAD)
static pthread_mutex_t crypt_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif
#if (defined(HAVE_CRYPT_R) || defined(HAVE_CRYPT)) && HAVE_PTHREAD
struct algo {
const char prefix[4];
const size_t saltlen;
const bool secure;
};
static pthread_once_t crypt_detect = PTHREAD_ONCE_INIT;
static const struct algo *new_algo;
#define HAVE_new_algo
void crypt_detect_run(void)
{
static const struct algo list[] = {{"$6$", 12, true}, {"$5$", 12, true}, {"$1$", 6, false}};
for (size_t i = 0; i < (sizeof(list)/sizeof(*list)); i++) {
if (util_crypt_is_supported(list[i].prefix)) {
new_algo = &(list[i]);
return;
}
}
}
#endif
char * util_crypt_hash_oldstyle(const char *pw)
{
unsigned char digest[HASH_LEN];
if (rhash_msg(RHASH_MD5, pw, strlen(pw), digest) != 0)
return NULL;
return util_bin_to_hex(digest, HASH_LEN);
}
char * util_crypt_hash(const char *pw)
{
#if (defined(HAVE_CRYPT_R) || defined(HAVE_CRYPT)) && HAVE_PTHREAD
if (pthread_once(&crypt_detect, crypt_detect_run) != 0)
return NULL;
if (new_algo) {
char input[128];
char salt[64];
char *salt_base64;
ssize_t len;
#ifdef HAVE_CRYPT_R
struct crypt_data data;
#elif defined(HAVE_CRYPT) && defined(HAVE_PTHREAD)
char *data;
#endif
/* if this is true, we have a bug */
if (new_algo->saltlen > sizeof(salt))
return NULL;
len = igloo_prng_read(igloo_instance, salt, new_algo->saltlen, igloo_PRNG_FLAG_NONE);
if (len != (ssize_t)new_algo->saltlen)
return NULL;
salt_base64 = util_base64_encode(salt, new_algo->saltlen);
if (!salt_base64)
return NULL;
snprintf(input, sizeof(input), "%s%s", new_algo->prefix, salt_base64);
free(salt_base64);
#ifdef HAVE_CRYPT_R
memset(&data, 0, sizeof(data));
return strdup(crypt_r(pw, input, &data));
#elif defined(HAVE_CRYPT) && defined(HAVE_PTHREAD)
if (pthread_mutex_lock(&crypt_mutex) != 0)
return NULL;
data = strdup(crypt(pw, input));
pthread_mutex_unlock(&crypt_mutex);
return data;
#else
#error "BUG"
#endif
} else {
#endif
return util_crypt_hash_oldstyle(pw);
#if (defined(HAVE_CRYPT_R) || defined(HAVE_CRYPT)) && HAVE_PTHREAD
}
#endif
}
bool util_crypt_check(const char *plain, const char *crypted)
{
size_t len;
if (!plain || !crypted)
return false;
len = strlen(crypted);
if (!len)
return false;
/* below here we know that plain and crypted are non-null and that crypted is at least one byte long */
if (len == (HASH_LEN*2) && crypted[0] != '$') {
char *digest = util_crypt_hash_oldstyle(plain);
bool res;
if (!digest)
return false;
res = strcmp(digest, crypted) == 0;
free(digest);
return res;
}
if (crypted[0] == '$') {
const char *cres;
#ifdef HAVE_CRYPT_R
struct crypt_data data;
memset(&data, 0, sizeof(data));
cres = crypt_r(plain, crypted, &data);
if (cres && strcmp(crypted, cres) == 0)
return true;
#elif defined(HAVE_CRYPT) && defined(HAVE_PTHREAD)
bool res = false;
if (pthread_mutex_lock(&crypt_mutex) != 0)
return false;
cres = crypt(plain, crypted);
if (cres && strcmp(crypted, cres) == 0)
res = true;
pthread_mutex_unlock(&crypt_mutex);
return res;
#endif
}
return false;
}
bool util_crypt_is_supported(const char *prefix)
{
static const struct {
const char *plain;
const char *crypted;
const bool expected;
} vectors[] = {
{"abc", "$1$xxxxxxxx$3GbMJKRcRFz50R9Q96xFb.", true},
{"abX", "$1$xxxxxxxx$3GbMJKRcRFz50R9Q96xFb.", false},
{"abc", "$1$xxxxxxxx$3GbMJKRcRFz50R9Q96xFbY", false},
{"abX", "$1$xxxxxxxx$3GbMJKRcRFz50R9Q96xFbY", false},
{"abc", "$3$$e0fba38268d0ec66ef1cb452d5885e53", true},
{"abX", "$3$$e0fba38268d0ec66ef1cb452d5885e53", false},
{"abc", "$3$$e0fba38268d0ec66ef1cb452d5885e5Y", false},
{"abX", "$3$$e0fba38268d0ec66ef1cb452d5885e5Y", false},
{"abc", "$5$xxxxxxxxxxxxxxxx$zNpAueQbvBleD3aSz0KwnySLaHSedk8ULXPvT1m7DUC", true},
{"abX", "$5$xxxxxxxxxxxxxxxx$zNpAueQbvBleD3aSz0KwnySLaHSedk8ULXPvT1m7DUC", false},
{"abc", "$5$xxxxxxxxxxxxxxxx$zNpAueQbvBleD3aSz0KwnySLaHSedk8ULXPvT1m7DUY", false},
{"abX", "$5$xxxxxxxxxxxxxxxx$zNpAueQbvBleD3aSz0KwnySLaHSedk8ULXPvT1m7DUY", false},
{"abc", "$6$xxxxxxxxxxxxxxxx$yNfBmH1zabagyi9HZwRuCgebrSjfr1zXUE6pFhnTG1BcvINxhgU53sjSUJDnQ5s6FPq8NSIntrpmc5ox87wX5.", true},
{"abX", "$6$xxxxxxxxxxxxxxxx$yNfBmH1zabagyi9HZwRuCgebrSjfr1zXUE6pFhnTG1BcvINxhgU53sjSUJDnQ5s6FPq8NSIntrpmc5ox87wX5.", false},
{"abc", "$6$xxxxxxxxxxxxxxxx$yNfBmH1zabagyi9HZwRuCgebrSjfr1zXUE6pFhnTG1BcvINxhgU53sjSUJDnQ5s6FPq8NSIntrpmc5ox87wX5Y", false},
{"abX", "$6$xxxxxxxxxxxxxxxx$yNfBmH1zabagyi9HZwRuCgebrSjfr1zXUE6pFhnTG1BcvINxhgU53sjSUJDnQ5s6FPq8NSIntrpmc5ox87wX5Y", false},
{"abc", "$7$DU..../....2Q9obwLhin8qvQl6sisAO/$n4xOT1fpjmazI6Ekeq3slWypZS0PKKV/QVpUE1X0MH6", true},
{"abX", "$7$DU..../....2Q9obwLhin8qvQl6sisAO/$n4xOT1fpjmazI6Ekeq3slWypZS0PKKV/QVpUE1X0MH6", false},
{"abc", "$7$DU..../....2Q9obwLhin8qvQl6sisAO/$n4xOT1fpjmazI6Ekeq3slWypZS0PKKV/QVpUE1X0MHY", false},
{"abX", "$7$DU..../....2Q9obwLhin8qvQl6sisAO/$n4xOT1fpjmazI6Ekeq3slWypZS0PKKV/QVpUE1X0MHY", false},
{"abc", "$md5$GUBv0xjJ$$59nlXSorBz79sJsp1gfwk1", true},
{"abX", "$md5$GUBv0xjJ$$59nlXSorBz79sJsp1gfwk1", false},
{"abc", "$md5$GUBv0xjJ$$59nlXSorBz79sJsp1gfwkY", false},
{"abX", "$md5$GUBv0xjJ$$59nlXSorBz79sJsp1gfwkY", false},
{"abc", "$sha1$40000$jtNX3nZ2$Cw.7bEep2dEG6qIx3.0HkiF/YoLW", true},
{"abX", "$sha1$40000$jtNX3nZ2$Cw.7bEep2dEG6qIx3.0HkiF/YoLW", false},
{"abc", "$sha1$40000$jtNX3nZ2$Cw.7bEep2dEG6qIx3.0HkiF/YoLY", false},
{"abX", "$sha1$40000$jtNX3nZ2$Cw.7bEep2dEG6qIx3.0HkiF/YoLY", false},
{"abc", "$y$j9T$F5Jx5fExrKuPp53xLKQ..1$aC5fZPrKSlHTuOtuJjdRm7BCdVfOnO9UIkyfXQcyB83", true},
{"abX", "$y$j9T$F5Jx5fExrKuPp53xLKQ..1$aC5fZPrKSlHTuOtuJjdRm7BCdVfOnO9UIkyfXQcyB83", false},
{"abc", "$y$j9T$F5Jx5fExrKuPp53xLKQ..1$aC5fZPrKSlHTuOtuJjdRm7BCdVfOnO9UIkyfXQcyB8Y", false},
{"abX", "$y$j9T$F5Jx5fExrKuPp53xLKQ..1$aC5fZPrKSlHTuOtuJjdRm7BCdVfOnO9UIkyfXQcyB8Y", false},
{"abc", "$gy$jCT$HM87v.7RwpQLba8fDjNSk1$3jEy/aqqTrXmZVCK3RqOQJJS8ve8hM5pSUTTkaTO.l5", true},
{"abX", "$gy$jCT$HM87v.7RwpQLba8fDjNSk1$3jEy/aqqTrXmZVCK3RqOQJJS8ve8hM5pSUTTkaTO.l5", false},
{"abc", "$gy$jCT$HM87v.7RwpQLba8fDjNSk1$3jEy/aqqTrXmZVCK3RqOQJJS8ve8hM5pSUTTkaTO.lY", false},
{"abX", "$gy$jCT$HM87v.7RwpQLba8fDjNSk1$3jEy/aqqTrXmZVCK3RqOQJJS8ve8hM5pSUTTkaTO.lY", false}
};
size_t prefixlen = strlen(prefix);
bool supported = false;
for (size_t i = 0; i < (sizeof(vectors)/sizeof(*vectors)); i++) {
if (strncmp(vectors[i].crypted, prefix, prefixlen) == 0) {
bool res = util_crypt_check(vectors[i].plain, vectors[i].crypted);
if (res != vectors[i].expected)
return false;
supported = true;
}
}
return supported;
}
bool util_crypt_is_new_secure(void)
{
#ifdef HAVE_new_algo
if (pthread_once(&crypt_detect, crypt_detect_run) != 0)
return NULL;
return new_algo->secure;
#else
return false;
#endif
}

22
src/util_crypt.h Normal file
View File

@ -0,0 +1,22 @@
/* Icecast
*
* Copyright 2023 Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*/
#ifndef __UTIL_CRYPT_H__
#define __UTIL_CRYPT_H__
#include <stdbool.h>
char * util_crypt_hash(const char *pw);
bool util_crypt_check(const char *plain, const char *crypted);
bool util_crypt_is_supported(const char *prefix);
bool util_crypt_is_new_secure(void);
/* Exported for tests only!: */
char * util_crypt_hash_oldstyle(const char *pw);
#endif /* __UTIL_CRYPT_H__ */