Add optional two-factor authentication primitives
This commit is contained in:
+1
-1
@@ -46,7 +46,7 @@ include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}/include/)
|
||||
|
||||
# now build our various libraries, agents, binaries.
|
||||
# DO NOT ALTER (SO)VERSION ON LIBRARIES WITHOUT ASKING!
|
||||
foreach (LIBRARY xpl streamio msgapi connio util json cal mailauth sieve sasl collectoraccounts)
|
||||
foreach (LIBRARY xpl streamio msgapi connio util json cal mailauth sieve sasl collectoraccounts authsecurity)
|
||||
add_subdirectory (src/libs/${LIBRARY})
|
||||
set(LIBRARY_TARGET bongo${LIBRARY})
|
||||
if(LIBRARY STREQUAL "sieve")
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# /****************************************************************************
|
||||
# * <Novell-copyright>
|
||||
# * Copyright (c) 2001 Novell, Inc. All Rights Reserved.
|
||||
# *
|
||||
# * This program is free software; you can redistribute it and/or
|
||||
# * modify it under the terms of version 2 of the GNU General Public License
|
||||
# * as published by the Free Software Foundation.
|
||||
# *
|
||||
# * This program is distributed in the hope that it will be useful,
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# * GNU General Public License for more details.
|
||||
# *
|
||||
# * You should have received a copy of the GNU General Public License
|
||||
# * along with this program; if not, contact Novell, Inc.
|
||||
# *
|
||||
# * To contact Novell about this file by physical or electronic mail, you
|
||||
# * may find current contact information at www.novell.com.
|
||||
# * </Novell-copyright>
|
||||
# ****************************************************************************/
|
||||
|
||||
find_path(Argon2_INCLUDE_DIR NAMES argon2.h)
|
||||
find_library(Argon2_LIBRARY NAMES argon2)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Argon2
|
||||
REQUIRED_VARS Argon2_LIBRARY Argon2_INCLUDE_DIR)
|
||||
|
||||
if(Argon2_FOUND AND NOT TARGET Argon2::Argon2)
|
||||
add_library(Argon2::Argon2 UNKNOWN IMPORTED)
|
||||
set_target_properties(Argon2::Argon2 PROPERTIES
|
||||
IMPORTED_LOCATION "${Argon2_LIBRARY}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${Argon2_INCLUDE_DIR}")
|
||||
endif()
|
||||
|
||||
mark_as_advanced(Argon2_INCLUDE_DIR Argon2_LIBRARY)
|
||||
@@ -0,0 +1,44 @@
|
||||
# /****************************************************************************
|
||||
# * <Novell-copyright>
|
||||
# * Copyright (c) 2001 Novell, Inc. All Rights Reserved.
|
||||
# *
|
||||
# * This program is free software; you can redistribute it and/or
|
||||
# * modify it under the terms of version 2 of the GNU General Public License
|
||||
# * as published by the Free Software Foundation.
|
||||
# *
|
||||
# * This program is distributed in the hope that it will be useful,
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# * GNU General Public License for more details.
|
||||
# *
|
||||
# * You should have received a copy of the GNU General Public License
|
||||
# * along with this program; if not, contact Novell, Inc.
|
||||
# *
|
||||
# * To contact Novell about this file by physical or electronic mail, you
|
||||
# * may find current contact information at www.novell.com.
|
||||
# * </Novell-copyright>
|
||||
# ****************************************************************************/
|
||||
|
||||
find_path(OATH_INCLUDE_DIR NAMES liboath/oath.h)
|
||||
find_library(OATH_LIBRARY NAMES oath)
|
||||
|
||||
if(OATH_INCLUDE_DIR)
|
||||
file(STRINGS "${OATH_INCLUDE_DIR}/liboath/oath.h" _OATH_VERSION_LINE
|
||||
REGEX "^# *define OATH_VERSION +\"[0-9]+\\.[0-9]+\\.[0-9]+\"")
|
||||
string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" OATH_VERSION
|
||||
"${_OATH_VERSION_LINE}")
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(OATH
|
||||
REQUIRED_VARS OATH_LIBRARY OATH_INCLUDE_DIR
|
||||
VERSION_VAR OATH_VERSION)
|
||||
|
||||
if(OATH_FOUND AND NOT TARGET OATH::OATH)
|
||||
add_library(OATH::OATH UNKNOWN IMPORTED)
|
||||
set_target_properties(OATH::OATH PROPERTIES
|
||||
IMPORTED_LOCATION "${OATH_LIBRARY}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${OATH_INCLUDE_DIR}")
|
||||
endif()
|
||||
|
||||
mark_as_advanced(OATH_INCLUDE_DIR OATH_LIBRARY)
|
||||
@@ -58,6 +58,10 @@ find_package(Mailutils REQUIRED COMPONENTS Sieve)
|
||||
# Shared SASL implementation for SMTP, IMAP, POP3 and ManageSieve.
|
||||
find_package(CyrusSASL 2.1.28 REQUIRED)
|
||||
|
||||
# TOTP, recovery codes and service-scoped app passwords.
|
||||
find_package(OATH 2.6 REQUIRED)
|
||||
find_package(Argon2 REQUIRED)
|
||||
|
||||
# Native mail authentication. Bongo links to the protocol libraries directly;
|
||||
# it does not require the corresponding milter daemons at runtime.
|
||||
find_package(SPF2 REQUIRED)
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/****************************************************************************
|
||||
* <Novell-copyright>
|
||||
* Copyright (c) 2001 Novell, Inc. All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, contact Novell, Inc.
|
||||
* </Novell-copyright>
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef BONGO_AUTH_SECURITY_H
|
||||
#define BONGO_AUTH_SECURITY_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <time.h>
|
||||
|
||||
#define BONGO_AUTH_TOTP_SECRET_SIZE 64
|
||||
#define BONGO_AUTH_CREDENTIAL_SIZE 48
|
||||
#define BONGO_AUTH_HASH_SIZE 128
|
||||
|
||||
int BongoAuthSecurityInit(void);
|
||||
void BongoAuthSecurityDone(void);
|
||||
int BongoAuthGenerateTotpSecret(char *result, size_t result_size);
|
||||
int BongoAuthVerifyTotp(const char *secret, const char *otp, time_t now);
|
||||
int BongoAuthGenerateCredential(char *result, size_t result_size);
|
||||
int BongoAuthHashCredential(const char *credential, char *result,
|
||||
size_t result_size);
|
||||
int BongoAuthVerifyCredential(const char *encoded, const char *credential);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,16 @@
|
||||
add_library(bongoauthsecurity SHARED
|
||||
authsecurity.c)
|
||||
|
||||
target_link_libraries(bongoauthsecurity
|
||||
Argon2::Argon2
|
||||
LibGcrypt::LibGcrypt
|
||||
OATH::OATH)
|
||||
|
||||
install(TARGETS bongoauthsecurity DESTINATION ${LIB_INSTALL_DIR})
|
||||
StrictCompile()
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_executable(auth-security-test tests/authsecurity-test.c)
|
||||
target_link_libraries(auth-security-test PRIVATE bongoauthsecurity OATH::OATH)
|
||||
add_test(NAME auth-security COMMAND auth-security-test)
|
||||
endif()
|
||||
@@ -0,0 +1,173 @@
|
||||
/****************************************************************************
|
||||
* <Novell-copyright>
|
||||
* Copyright (c) 2001 Novell, Inc. All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, contact Novell, Inc.
|
||||
* </Novell-copyright>
|
||||
****************************************************************************/
|
||||
|
||||
#include <argon2.h>
|
||||
#include <gcrypt.h>
|
||||
#include <liboath/oath.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <bongoauthsecurity.h>
|
||||
|
||||
#define TOTP_SECRET_BYTES 20U
|
||||
#define CREDENTIAL_BYTES 20U
|
||||
#define ARGON_TIME_COST 3U
|
||||
#define ARGON_MEMORY_COST 32768U
|
||||
#define ARGON_PARALLELISM 1U
|
||||
#define ARGON_SALT_BYTES 16U
|
||||
#define ARGON_HASH_BYTES 32U
|
||||
|
||||
static void
|
||||
SecureClear(void *buffer, size_t length)
|
||||
{
|
||||
volatile unsigned char *cursor = buffer;
|
||||
|
||||
while (length-- > 0)
|
||||
*cursor++ = 0;
|
||||
}
|
||||
|
||||
int
|
||||
BongoAuthSecurityInit(void)
|
||||
{
|
||||
if (!gcry_check_version(NULL))
|
||||
return -1;
|
||||
return oath_init() == OATH_OK ? 0 : -1;
|
||||
}
|
||||
|
||||
void
|
||||
BongoAuthSecurityDone(void)
|
||||
{
|
||||
oath_done();
|
||||
}
|
||||
|
||||
int
|
||||
BongoAuthGenerateTotpSecret(char *result, size_t result_size)
|
||||
{
|
||||
unsigned char random[TOTP_SECRET_BYTES];
|
||||
char *encoded = NULL;
|
||||
size_t encoded_size = 0;
|
||||
int status = -1;
|
||||
|
||||
if (!result || result_size == 0)
|
||||
return -1;
|
||||
gcry_randomize(random, sizeof(random), GCRY_STRONG_RANDOM);
|
||||
if (oath_base32_encode((const char *)random, sizeof(random), &encoded,
|
||||
&encoded_size) == OATH_OK && encoded &&
|
||||
encoded_size < result_size) {
|
||||
memcpy(result, encoded, encoded_size);
|
||||
result[encoded_size] = '\0';
|
||||
status = 0;
|
||||
}
|
||||
if (encoded) {
|
||||
SecureClear(encoded, encoded_size);
|
||||
free(encoded);
|
||||
}
|
||||
SecureClear(random, sizeof(random));
|
||||
return status;
|
||||
}
|
||||
|
||||
int
|
||||
BongoAuthVerifyTotp(const char *secret, const char *otp, time_t now)
|
||||
{
|
||||
char *decoded = NULL;
|
||||
size_t decoded_size = 0;
|
||||
int status;
|
||||
size_t i;
|
||||
|
||||
if (!secret || !otp || strlen(otp) != 6)
|
||||
return 0;
|
||||
for (i = 0; otp[i]; i++)
|
||||
if (!isdigit((unsigned char)otp[i]))
|
||||
return 0;
|
||||
if (oath_base32_decode(secret, strlen(secret), &decoded,
|
||||
&decoded_size) != OATH_OK || !decoded)
|
||||
return 0;
|
||||
status = oath_totp_validate(decoded, decoded_size, now,
|
||||
OATH_TOTP_DEFAULT_TIME_STEP_SIZE,
|
||||
OATH_TOTP_DEFAULT_START_TIME, 1, otp);
|
||||
SecureClear(decoded, decoded_size);
|
||||
free(decoded);
|
||||
return status >= 0;
|
||||
}
|
||||
|
||||
int
|
||||
BongoAuthGenerateCredential(char *result, size_t result_size)
|
||||
{
|
||||
unsigned char random[CREDENTIAL_BYTES];
|
||||
char *encoded = NULL;
|
||||
size_t encoded_size = 0;
|
||||
size_t i;
|
||||
size_t output = 0;
|
||||
int status = -1;
|
||||
|
||||
if (!result || result_size < BONGO_AUTH_CREDENTIAL_SIZE)
|
||||
return -1;
|
||||
gcry_randomize(random, sizeof(random), GCRY_STRONG_RANDOM);
|
||||
if (oath_base32_encode((const char *)random, sizeof(random), &encoded,
|
||||
&encoded_size) != OATH_OK || !encoded)
|
||||
goto cleanup;
|
||||
for (i = 0; i < encoded_size && encoded[i] != '='; i++) {
|
||||
if (i > 0 && i % 4 == 0)
|
||||
result[output++] = '-';
|
||||
result[output++] = (char)tolower((unsigned char)encoded[i]);
|
||||
}
|
||||
result[output] = '\0';
|
||||
status = 0;
|
||||
|
||||
cleanup:
|
||||
if (encoded) {
|
||||
SecureClear(encoded, encoded_size);
|
||||
free(encoded);
|
||||
}
|
||||
SecureClear(random, sizeof(random));
|
||||
return status;
|
||||
}
|
||||
|
||||
int
|
||||
BongoAuthHashCredential(const char *credential, char *result,
|
||||
size_t result_size)
|
||||
{
|
||||
unsigned char salt[ARGON_SALT_BYTES];
|
||||
size_t required;
|
||||
int status;
|
||||
|
||||
if (!credential || !credential[0] || !result)
|
||||
return -1;
|
||||
required = argon2_encodedlen(ARGON_TIME_COST, ARGON_MEMORY_COST,
|
||||
ARGON_PARALLELISM, ARGON_SALT_BYTES,
|
||||
ARGON_HASH_BYTES, Argon2_id);
|
||||
if (result_size < required)
|
||||
return -1;
|
||||
gcry_randomize(salt, sizeof(salt), GCRY_STRONG_RANDOM);
|
||||
status = argon2id_hash_encoded(ARGON_TIME_COST, ARGON_MEMORY_COST,
|
||||
ARGON_PARALLELISM, credential,
|
||||
strlen(credential), salt, sizeof(salt),
|
||||
ARGON_HASH_BYTES, result, result_size);
|
||||
SecureClear(salt, sizeof(salt));
|
||||
return status == ARGON2_OK ? 0 : -1;
|
||||
}
|
||||
|
||||
int
|
||||
BongoAuthVerifyCredential(const char *encoded, const char *credential)
|
||||
{
|
||||
if (!encoded || !credential)
|
||||
return 0;
|
||||
return argon2id_verify(encoded, credential, strlen(credential)) == ARGON2_OK;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/****************************************************************************
|
||||
* <Novell-copyright>
|
||||
* Copyright (c) 2001 Novell, Inc. All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, contact Novell, Inc.
|
||||
*
|
||||
* To contact Novell about this file by physical or electronic mail, you
|
||||
* may find current contact information at www.novell.com.
|
||||
* </Novell-copyright>
|
||||
****************************************************************************/
|
||||
|
||||
#include <liboath/oath.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <bongoauthsecurity.h>
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
char secret[BONGO_AUTH_TOTP_SECRET_SIZE];
|
||||
char credential[BONGO_AUTH_CREDENTIAL_SIZE];
|
||||
char second[BONGO_AUTH_CREDENTIAL_SIZE];
|
||||
char hash[BONGO_AUTH_HASH_SIZE];
|
||||
char otp[7];
|
||||
char *decoded = NULL;
|
||||
size_t decoded_size = 0;
|
||||
time_t now = 1234567890;
|
||||
int result = 0;
|
||||
|
||||
if (BongoAuthSecurityInit() != 0)
|
||||
return 1;
|
||||
if (BongoAuthGenerateTotpSecret(secret, sizeof(secret)) != 0 ||
|
||||
oath_base32_decode(secret, strlen(secret), &decoded,
|
||||
&decoded_size) != OATH_OK)
|
||||
result = 2;
|
||||
else if (oath_totp_generate(decoded, decoded_size, now, 30, 0, 6, otp) != OATH_OK ||
|
||||
!BongoAuthVerifyTotp(secret, otp, now) ||
|
||||
BongoAuthVerifyTotp(secret, "00000x", now))
|
||||
result = 3;
|
||||
free(decoded);
|
||||
if (!result && (BongoAuthGenerateCredential(credential, sizeof(credential)) != 0 ||
|
||||
BongoAuthGenerateCredential(second, sizeof(second)) != 0 ||
|
||||
strcmp(credential, second) == 0 || strlen(credential) != 39))
|
||||
result = 4;
|
||||
if (!result && (BongoAuthHashCredential(credential, hash, sizeof(hash)) != 0 ||
|
||||
strncmp(hash, "$argon2id$", 10) != 0 ||
|
||||
!BongoAuthVerifyCredential(hash, credential) ||
|
||||
BongoAuthVerifyCredential(hash, second)))
|
||||
result = 5;
|
||||
BongoAuthSecurityDone();
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user