first commit
This commit is contained in:
2
authservice/mqsimpleauth/.gitignore
vendored
Normal file
2
authservice/mqsimpleauth/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/testSecret*
|
||||
/*.log
|
||||
59
authservice/mqsimpleauth/Makefile
Normal file
59
authservice/mqsimpleauth/Makefile
Normal file
@@ -0,0 +1,59 @@
|
||||
# © Copyright IBM Corporation 2017, 2022
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This Makefile expects the following to be installed:
|
||||
# - gcc
|
||||
# - ldd
|
||||
# - MQ SDK (mqm_r library, plus header files)
|
||||
|
||||
SRC_DIR = src
|
||||
BUILD_DIR = ./build
|
||||
ARCH ?= $(if $(findstring x86_64,$(shell uname -m)),amd64,$(if $(findstring aarch64,$(shell uname -m)),aarch64,$(shell uname -m)))
|
||||
|
||||
# Flags passed to the C compiler. Need to use gnu11 to get POSIX functions needed for file locking.
|
||||
CFLAGS.amd64 := -m64
|
||||
CFLAGS.ppc64le := -m64
|
||||
CFLAGS.s390x := -m64
|
||||
# -m64 is not a valid compiler option on aarch64/arm64 (ARM)
|
||||
CFLAGS.arm64 :=
|
||||
CFLAGS += -std=gnu11 -fPIC -Wall ${CFLAGS.${ARCH}}
|
||||
|
||||
LIB_MQ = -L/opt/mqm/lib64 -lmqm_r
|
||||
|
||||
all: $(BUILD_DIR)/mqsimpleauth.so $(BUILD_DIR)/simpleauth_test $(BUILD_DIR)/log_test
|
||||
|
||||
$(BUILD_DIR)/log.o : $(SRC_DIR)/log.c $(SRC_DIR)/log.h
|
||||
mkdir -p ${dir $@}
|
||||
gcc $(CFLAGS) -c $(SRC_DIR)/log.c -o $@
|
||||
|
||||
$(BUILD_DIR)/log_test : $(BUILD_DIR)/log.o
|
||||
mkdir -p ${dir $@}
|
||||
gcc $(CFLAGS) $(SRC_DIR)/log_test.c $^ -o $@
|
||||
# Run Logging tests, and print log if they fail
|
||||
$@ || (cat log_test*.log && exit 1)
|
||||
|
||||
$(BUILD_DIR)/simpleauth.o : $(SRC_DIR)/simpleauth.c $(SRC_DIR)/simpleauth.h
|
||||
mkdir -p ${dir $@}
|
||||
gcc $(CFLAGS) -c $(SRC_DIR)/simpleauth.c -o $@
|
||||
|
||||
$(BUILD_DIR)/simpleauth_test : $(BUILD_DIR)/simpleauth.o $(BUILD_DIR)/log.o
|
||||
mkdir -p ${dir $@}
|
||||
gcc $(CFLAGS) -lpthread $(SRC_DIR)/simpleauth_test.c $^ -o $@
|
||||
# Run SimpleAuth tests, and print log if they fail
|
||||
$@ || (cat simpleauth_test*.log && exit 1)
|
||||
|
||||
$(BUILD_DIR)/mqsimpleauth.so : $(BUILD_DIR)/log.o $(BUILD_DIR)/simpleauth.o
|
||||
mkdir -p ${dir $@}
|
||||
gcc $(CFLAGS) -I/opt/mqm/inc -D_REENTRANT $(LIB_MQ) -Wl,-rpath,/opt/mqm/lib64 -Wl,-rpath,/usr/lib64 -shared $(SRC_DIR)/mqsimpleauth.c $^ -o $@
|
||||
ldd $@
|
||||
162
authservice/mqsimpleauth/src/log.c
Normal file
162
authservice/mqsimpleauth/src/log.c
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2021, 2022
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
FILE *fp = NULL;
|
||||
int pid;
|
||||
char hostname[255];
|
||||
bool debug = false;
|
||||
|
||||
/**
|
||||
* Determine whether debugging is enabled or not, using an environment variable.
|
||||
*/
|
||||
void init_debug(){
|
||||
char *debug_env = getenv("DEBUG");
|
||||
if (debug_env != NULL)
|
||||
{
|
||||
// Enable debug logging if the DEBUG environment variable is set
|
||||
if (strncmp(debug_env, "true", 4) || strncmp(debug_env, "1", 1))
|
||||
{
|
||||
debug = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function to initialize the log with the given file mode.
|
||||
*/
|
||||
int log_init_internal(char *filename, const char *mode)
|
||||
{
|
||||
int result = 0;
|
||||
pid = getpid();
|
||||
hostname[254] = '\0';
|
||||
gethostname(hostname, 254);
|
||||
if (!fp)
|
||||
{
|
||||
fp = fopen(filename, "a");
|
||||
if (fp)
|
||||
{
|
||||
// Disable buffering for this file
|
||||
setbuf(fp, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = 1;
|
||||
}
|
||||
}
|
||||
init_debug();
|
||||
return result;
|
||||
}
|
||||
|
||||
int log_init_reset(char *filename)
|
||||
{
|
||||
// Open the log file for writing (overwrite if it already exists)
|
||||
return log_init_internal(filename, "w");
|
||||
}
|
||||
|
||||
int log_init(char *filename)
|
||||
{
|
||||
// Open the log file file for appending
|
||||
return log_init_internal(filename, "a");
|
||||
}
|
||||
|
||||
void log_init_file(FILE *f)
|
||||
{
|
||||
fp = f;
|
||||
init_debug();
|
||||
}
|
||||
|
||||
void log_close()
|
||||
{
|
||||
if (fp)
|
||||
{
|
||||
fclose(fp);
|
||||
fp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void log_printf(const char *source_file, int source_line, const char *level, const char *format, ...)
|
||||
{
|
||||
if (fp)
|
||||
{
|
||||
// If this is a DEBUG message, and debugging is off
|
||||
if ((strncmp(level, "DEBUG", 5) == 0) && !debug)
|
||||
{
|
||||
return;
|
||||
}
|
||||
char buf[1024] = "";
|
||||
char *cur = buf;
|
||||
char* const end = buf + sizeof buf;
|
||||
char date_buf[70];
|
||||
struct tm *utc;
|
||||
time_t t;
|
||||
struct timeval now;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
t = now.tv_sec;
|
||||
t = time(NULL);
|
||||
utc = gmtime(&t);
|
||||
|
||||
cur += snprintf(cur, end-cur, "{");
|
||||
cur += snprintf(cur, end-cur, "\"loglevel\":\"%s\"", level);
|
||||
// Print ISO-8601 time and date
|
||||
if (strftime(date_buf, sizeof date_buf, "%FT%T", utc))
|
||||
{
|
||||
// Round microseconds down to milliseconds, for consistency
|
||||
cur += snprintf(cur, end-cur, ", \"ibm_datetime\":\"%s.%03ldZ\"", date_buf, now.tv_usec / (long)1000);
|
||||
}
|
||||
cur += snprintf(cur, end-cur, ", \"ibm_processId\":\"%d\"", pid);
|
||||
cur += snprintf(cur, end-cur, ", \"host\":\"%s\"", hostname);
|
||||
cur += snprintf(cur, end-cur, ", \"module\":\"%s:%d\"", source_file, source_line);
|
||||
cur += snprintf(cur, end-cur, ", \"message\":\"");
|
||||
|
||||
if (strncmp(level, "DEBUG", 5) == 0)
|
||||
{
|
||||
// Add a prefix on any debug messages
|
||||
cur += snprintf(cur, end-cur, "mqsimpleauth: ");
|
||||
}
|
||||
|
||||
// Print log message, using varargs
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
cur += vsnprintf(cur, end-cur, format, args);
|
||||
va_end(args);
|
||||
cur += snprintf(cur, end-cur, "\"}\n");
|
||||
|
||||
// Important: Just do one file write, to prevent problems with multi-threading.
|
||||
// This only works if the log message is not too long for the buffer.
|
||||
fprintf(fp, "%s", buf);
|
||||
}
|
||||
}
|
||||
|
||||
int trimmed_len(char *s, int max_len)
|
||||
{
|
||||
int i;
|
||||
for (i = max_len - 1; i >= 0; i--)
|
||||
{
|
||||
if (s[i] != ' ')
|
||||
break;
|
||||
}
|
||||
return i+1;
|
||||
}
|
||||
70
authservice/mqsimpleauth/src/log.h
Normal file
70
authservice/mqsimpleauth/src/log.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2021, 2022
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LOG_H
|
||||
#define _LOG_H
|
||||
|
||||
/**
|
||||
* Initialize the log to use the given file name, wiping any existing contents.
|
||||
*/
|
||||
int log_init_reset(char *filename);
|
||||
|
||||
/**
|
||||
* Initialize the log to use the given file name.
|
||||
*/
|
||||
int log_init(char *filename);
|
||||
|
||||
/**
|
||||
* Initialize the log with an existing file handle.
|
||||
*/
|
||||
void log_init_file(FILE *f);
|
||||
|
||||
/**
|
||||
* Write a message to the log file, based on a printf format string.
|
||||
*
|
||||
* @param source_file the name of the source code file submitting this log message
|
||||
* @param source_line the line of code in the source file
|
||||
* @param level the log level, one of "DEBUG", "INFO" or "ERROR"
|
||||
* @param format the printf format string for the message
|
||||
*/
|
||||
void log_printf(const char *source_file, int source_line, const char *level, const char *format, ...);
|
||||
|
||||
void log_close();
|
||||
|
||||
/**
|
||||
* Variadic macro to write an informational message to the log file, based on a printf format string.
|
||||
*/
|
||||
#define log_infof(format,...) log_printf(__FILE__, __LINE__, "INFO", format, ##__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Variadic macro to write an error message to the log file, based on a printf format string.
|
||||
*/
|
||||
#define log_errorf(format,...) log_printf(__FILE__, __LINE__, "ERROR", format, ##__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Variadic macro to write a debug message to the log file, based on a printf format string.
|
||||
*/
|
||||
#define log_debugf(format,...) log_printf(__FILE__, __LINE__, "DEBUG", format, ##__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Return the length of the string when trimmed of trailing spaces.
|
||||
* IBM MQ uses fixed length strings, so this function can be used to print
|
||||
* a trimmed version of a string using the "%.*s" printf format string.
|
||||
* For example, `log_printf("%.*s", trimmed_len(fw_str, 48), fw_str)`
|
||||
*/
|
||||
int trimmed_len(char *s, int);
|
||||
|
||||
#endif
|
||||
120
authservice/mqsimpleauth/src/log_test.c
Normal file
120
authservice/mqsimpleauth/src/log_test.c
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2022
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "log.h"
|
||||
|
||||
// Headers for multi-threaded tests
|
||||
#include <pthread.h>
|
||||
|
||||
// Start a test and log the function name
|
||||
#define test_start() printf("=== RUN: %s\n", __func__)
|
||||
|
||||
// Indicate test has passed
|
||||
#define test_pass() printf("--- PASS: %s\n", __func__)
|
||||
|
||||
// The length of strings used in the tests
|
||||
#define STR_LEN 5
|
||||
|
||||
// Indicate test has failed
|
||||
void test_fail(const char *test_name)
|
||||
{
|
||||
printf("--- FAIL: %s\n", test_name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Print a fixed-width string in hexadecimal
|
||||
void print_hex(char fw_string[STR_LEN])
|
||||
{
|
||||
printf("[");
|
||||
for (int i=0; i<STR_LEN; i++)
|
||||
{
|
||||
printf("%02x", fw_string[i]);
|
||||
if (i < STR_LEN-1)
|
||||
printf(",");
|
||||
}
|
||||
printf("]");
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Tests for string manipulation
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void test_trimmed_len(const char *test_name, char fw_string[STR_LEN], int expected_len)
|
||||
{
|
||||
printf("=== RUN: %s\n", test_name);
|
||||
int len;
|
||||
// Create a copy of the fixed-width string
|
||||
char fw_string2[STR_LEN];
|
||||
memcpy(fw_string2, fw_string, STR_LEN * sizeof(char));
|
||||
// Call the function under test
|
||||
len = trimmed_len(fw_string, STR_LEN);
|
||||
// Check the result is correct
|
||||
if (len != expected_len)
|
||||
{
|
||||
printf("%s: Expected result to be %d; got %d\n", __func__, expected_len, len);
|
||||
test_fail(test_name);
|
||||
}
|
||||
// Check that the original string has not been changed
|
||||
for (int i=0; i<STR_LEN; i++)
|
||||
{
|
||||
if (fw_string[i] != fw_string2[i])
|
||||
{
|
||||
printf("%c-%c\n", fw_string[i], fw_string2[i]);
|
||||
printf("%s: Expected string to be identical to input hex ", __func__);
|
||||
print_hex(fw_string2);
|
||||
printf("; got hex ");
|
||||
print_hex(fw_string);
|
||||
printf("\n");
|
||||
test_fail(test_name);
|
||||
}
|
||||
}
|
||||
printf("--- PASS: %s\n", test_name);
|
||||
}
|
||||
|
||||
void test_trimmed_len_normal()
|
||||
{
|
||||
char fw_string[STR_LEN] = {'a','b','c',' ',' '};
|
||||
test_trimmed_len(__func__, fw_string, 3);
|
||||
}
|
||||
|
||||
void test_trimmed_len_full()
|
||||
{
|
||||
char fw_string[STR_LEN] = {'a','b','c','d','e'};
|
||||
test_trimmed_len(__func__, fw_string, 5);
|
||||
}
|
||||
|
||||
void test_trimmed_len_empty()
|
||||
{
|
||||
char fw_string[STR_LEN] = {' ',' ',' ',' ',' '};
|
||||
test_trimmed_len(__func__, fw_string, 0);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
int main()
|
||||
{
|
||||
// Turn on debugging for the tests
|
||||
setenv("DEBUG", "true", true);
|
||||
log_init("log_test.log");
|
||||
test_trimmed_len_normal();
|
||||
test_trimmed_len_full();
|
||||
test_trimmed_len_empty();
|
||||
log_close();
|
||||
}
|
||||
1
authservice/mqsimpleauth/src/mqAdminPassword
Normal file
1
authservice/mqsimpleauth/src/mqAdminPassword
Normal file
@@ -0,0 +1 @@
|
||||
fred:$2y$05$3Fp9
|
||||
336
authservice/mqsimpleauth/src/mqsimpleauth.c
Normal file
336
authservice/mqsimpleauth/src/mqsimpleauth.c
Normal file
@@ -0,0 +1,336 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2021, 2024
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// This is a developer only configuration and not recommended for production usage.
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cmqec.h>
|
||||
#include "log.h"
|
||||
#include "simpleauth.h"
|
||||
|
||||
// Declare the internal functions that implement the interface
|
||||
MQZ_INIT_AUTHORITY MQStart;
|
||||
static MQZ_AUTHENTICATE_USER mqsimpleauth_authenticate_user;
|
||||
static MQZ_FREE_USER mqsimpleauth_free_user;
|
||||
static MQZ_TERM_AUTHORITY mqsimpleauth_terminate;
|
||||
|
||||
#define LOG_FILE "/var/mqm/errors/simpleauth.json"
|
||||
#define NAME "MQ Advanced for Developers custom authentication service"
|
||||
|
||||
/**
|
||||
* Initialization and entrypoint for the dynamically loaded
|
||||
* authorization installable service. It registers the addresses of the
|
||||
* other functions which are to be called by the queue manager.
|
||||
*
|
||||
* This function is called whenever the module is loaded. The Options
|
||||
* field will show whether it's a PRIMARY (i.e. during qmgr startup) or
|
||||
* SECONDARY.
|
||||
*/
|
||||
void MQENTRY MQStart(
|
||||
MQHCONFIG hc,
|
||||
MQLONG Options,
|
||||
MQCHAR48 QMgrName,
|
||||
MQLONG ComponentDataLength,
|
||||
PMQBYTE ComponentData,
|
||||
PMQLONG Version,
|
||||
PMQLONG pCompCode,
|
||||
PMQLONG pReason)
|
||||
{
|
||||
MQLONG CC = MQCC_OK;
|
||||
MQLONG Reason = MQRC_NONE;
|
||||
int log_rc = 0;
|
||||
|
||||
if (Options == MQZIO_PRIMARY)
|
||||
{
|
||||
// Reset the log file. The file could still get large if debug is turned on,
|
||||
// but this is a simpler solution for now.
|
||||
log_rc = log_init_reset(LOG_FILE);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_rc = log_init(LOG_FILE);
|
||||
}
|
||||
|
||||
if (log_rc != 0)
|
||||
{
|
||||
CC = MQCC_FAILED;
|
||||
Reason = MQRC_INITIALIZATION_FAILED;
|
||||
}
|
||||
|
||||
if (Options == MQZIO_PRIMARY)
|
||||
{
|
||||
log_infof("Initializing %s", NAME);
|
||||
}
|
||||
|
||||
log_debugf("MQStart options=%s qmgr=%.*s", ((Options == MQZIO_SECONDARY) ? "Secondary" : "Primary"), trimmed_len(QMgrName, MQ_Q_MGR_NAME_LENGTH), QMgrName);
|
||||
|
||||
// Initialize the functions to use for each entry point
|
||||
if (CC == MQCC_OK)
|
||||
{
|
||||
hc->MQZEP_Call(hc, MQZID_INIT_AUTHORITY, (PMQFUNC)MQStart, &CC, &Reason);
|
||||
}
|
||||
if (CC == MQCC_OK)
|
||||
{
|
||||
hc->MQZEP_Call(hc, MQZID_TERM_AUTHORITY, (PMQFUNC)mqsimpleauth_terminate, &CC, &Reason);
|
||||
}
|
||||
if (CC == MQCC_OK)
|
||||
{
|
||||
hc->MQZEP_Call(hc, MQZID_AUTHENTICATE_USER, (PMQFUNC)mqsimpleauth_authenticate_user, &CC, &Reason);
|
||||
}
|
||||
if (CC == MQCC_OK)
|
||||
{
|
||||
hc->MQZEP_Call(hc, MQZID_FREE_USER, (PMQFUNC)mqsimpleauth_free_user, &CC, &Reason);
|
||||
}
|
||||
*Version = MQZAS_VERSION_6;
|
||||
*pCompCode = CC;
|
||||
*pReason = Reason;
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called during the connection of any application which supplies an MQCSP (Connection Security Parameters).
|
||||
* This is the usual case.
|
||||
* See https://www.ibm.com/support/knowledgecenter/SSFKSJ_latest/com.ibm.mq.ref.dev.doc/q095610_.html
|
||||
*/
|
||||
static void MQENTRY mqsimpleauth_authenticate_user_csp(
|
||||
PMQCHAR pQMgrName,
|
||||
PMQCSP pSecurityParms,
|
||||
PMQZAC pApplicationContext,
|
||||
PMQZIC pIdentityContext,
|
||||
PMQPTR pCorrelationPtr,
|
||||
PMQBYTE pComponentData,
|
||||
PMQLONG pContinuation,
|
||||
PMQLONG pCompCode,
|
||||
PMQLONG pReason)
|
||||
{
|
||||
char *csp_user = NULL;
|
||||
char *csp_pass = NULL;
|
||||
|
||||
// Firstly, create null-terminated strings from the user credentials in the MQ CSP object
|
||||
csp_user = malloc(pSecurityParms->CSPUserIdLength + 1);
|
||||
if (!csp_user)
|
||||
{
|
||||
log_errorf("%s is unable to allocate memory for a user", NAME);
|
||||
*pCompCode = MQCC_FAILED;
|
||||
*pReason = MQRC_SERVICE_ERROR;
|
||||
return;
|
||||
}
|
||||
strncpy(csp_user, pSecurityParms->CSPUserIdPtr, pSecurityParms->CSPUserIdLength);
|
||||
csp_user[pSecurityParms->CSPUserIdLength] = 0;
|
||||
csp_pass = malloc((pSecurityParms->CSPPasswordLength + 1));
|
||||
if (!csp_pass)
|
||||
{
|
||||
log_errorf("%s is unable to allocate memory for a password", NAME);
|
||||
*pCompCode = MQCC_FAILED;
|
||||
*pReason = MQRC_SERVICE_ERROR;
|
||||
if (csp_user)
|
||||
{
|
||||
free(csp_user);
|
||||
}
|
||||
return;
|
||||
}
|
||||
strncpy(csp_pass, pSecurityParms->CSPPasswordPtr, pSecurityParms->CSPPasswordLength);
|
||||
csp_pass[pSecurityParms->CSPPasswordLength] = 0;
|
||||
log_debugf("%s with CSP user set. user=%s", __func__, csp_user);
|
||||
int auth_result = simpleauth_authenticate_user(csp_user, csp_pass);
|
||||
|
||||
if (auth_result == SIMPLEAUTH_VALID)
|
||||
{
|
||||
// An OK completion code means MQ will accept this user is authenticated
|
||||
*pCompCode = MQCC_OK;
|
||||
*pReason = MQRC_NONE;
|
||||
// Tell the queue manager to stop trying other authorization services.
|
||||
*pContinuation = MQZCI_STOP;
|
||||
memcpy(pIdentityContext->UserIdentifier, csp_user, sizeof(pIdentityContext->UserIdentifier));
|
||||
log_debugf("Authenticated user=%s", pIdentityContext->UserIdentifier);
|
||||
}
|
||||
// If the simpleauth file does not have an entry for this user
|
||||
else if (auth_result == SIMPLEAUTH_INVALID_USER)
|
||||
{
|
||||
*pCompCode = MQCC_WARNING;
|
||||
*pReason = MQRC_NONE;
|
||||
// Tell the queue manager to continue trying other authorization services, as they might have the user.
|
||||
*pContinuation = MQZCI_CONTINUE;
|
||||
log_debugf(
|
||||
"User authentication failed due to invalid user. user=%.*s effuser=%.*s applname=%.*s csp_user=%s cc=%d reason=%d",
|
||||
trimmed_len(pIdentityContext->UserIdentifier, MQ_USER_ID_LENGTH),
|
||||
pIdentityContext->UserIdentifier,
|
||||
trimmed_len(pApplicationContext->EffectiveUserID, MQ_USER_ID_LENGTH),
|
||||
pApplicationContext->EffectiveUserID,
|
||||
trimmed_len(pApplicationContext->ApplName, MQ_APPL_NAME_LENGTH),
|
||||
pApplicationContext->ApplName,
|
||||
csp_user,
|
||||
*pCompCode,
|
||||
*pReason);
|
||||
}
|
||||
// If the simpleauth file has an entry for this user, but the password supplied is incorrect
|
||||
else if (auth_result == SIMPLEAUTH_INVALID_PASSWORD)
|
||||
{
|
||||
*pCompCode = MQCC_WARNING;
|
||||
*pReason = MQRC_NOT_AUTHORIZED;
|
||||
// Tell the queue manager to stop trying other authorization services.
|
||||
*pContinuation = MQZCI_STOP;
|
||||
log_debugf(
|
||||
"User authentication failed due to invalid password. user=%.*s effuser=%.*s applname=%.*s csp_user=%s cc=%d reason=%d",
|
||||
trimmed_len(pIdentityContext->UserIdentifier, MQ_USER_ID_LENGTH),
|
||||
pIdentityContext->UserIdentifier,
|
||||
trimmed_len(pApplicationContext->EffectiveUserID, MQ_USER_ID_LENGTH),
|
||||
pApplicationContext->EffectiveUserID,
|
||||
trimmed_len(pApplicationContext->ApplName, MQ_APPL_NAME_LENGTH),
|
||||
pApplicationContext->ApplName,
|
||||
csp_user,
|
||||
*pCompCode,
|
||||
*pReason);
|
||||
}
|
||||
if (csp_user)
|
||||
{
|
||||
free(csp_user);
|
||||
}
|
||||
if (csp_pass)
|
||||
{
|
||||
free(csp_pass);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called during the connection of any application.
|
||||
* For more information on the parameters, see https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_latest/com.ibm.mq.ref.dev.doc/q110090_.html
|
||||
*/
|
||||
static void MQENTRY mqsimpleauth_authenticate_user(
|
||||
PMQCHAR pQMgrName,
|
||||
PMQCSP pSecurityParms,
|
||||
PMQZAC pApplicationContext,
|
||||
PMQZIC pIdentityContext,
|
||||
PMQPTR pCorrelationPtr,
|
||||
PMQBYTE pComponentData,
|
||||
PMQLONG pContinuation,
|
||||
PMQLONG pCompCode,
|
||||
PMQLONG pReason)
|
||||
{
|
||||
char *spuser = NULL;
|
||||
// By default, return a warning, which indicates to MQ that this
|
||||
// authorization service hasn't authenticated the user.
|
||||
*pCompCode = MQCC_WARNING;
|
||||
*pReason = MQRC_NONE;
|
||||
// By default, tell the queue manager to continue trying other
|
||||
// authorization services.
|
||||
*pContinuation = MQZCI_CONTINUE;
|
||||
|
||||
if ((pSecurityParms->AuthenticationType) == MQCSP_AUTH_USER_ID_AND_PWD)
|
||||
{
|
||||
mqsimpleauth_authenticate_user_csp(pQMgrName, pSecurityParms, pApplicationContext, pIdentityContext, pCorrelationPtr, pComponentData, pContinuation, pCompCode, pReason);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Password not supplied, so just check that the user ID is valid
|
||||
spuser = malloc(sizeof(PMQCHAR12) + 1);
|
||||
if (!spuser)
|
||||
{
|
||||
log_errorf("%s is unable to allocate memory to check a user", NAME);
|
||||
*pCompCode = MQCC_FAILED;
|
||||
*pReason = MQRC_SERVICE_ERROR;
|
||||
return;
|
||||
}
|
||||
strncpy(spuser, pApplicationContext->EffectiveUserID, strlen(pApplicationContext->EffectiveUserID));
|
||||
spuser[sizeof(PMQCHAR12)] = 0;
|
||||
log_debugf("%s without CSP user set. effectiveuid=%s env=%d, callertype=%d, type=%d, accttoken=%d applidentitydata=%d", __func__, spuser, pApplicationContext->Environment, pApplicationContext->CallerType, pApplicationContext->AuthenticationType, pIdentityContext->AccountingToken, pIdentityContext->ApplIdentityData);
|
||||
if (strncmp(spuser, "mqm", 3) == 0)
|
||||
{
|
||||
// Special case: pass the "mqm" user on for validation up the chain
|
||||
// A warning in the completion code means MQ will pass this to other authorization services
|
||||
*pCompCode = MQCC_WARNING;
|
||||
*pReason = MQRC_NONE;
|
||||
*pContinuation = MQZCI_CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool valid_user = simpleauth_valid_user(spuser);
|
||||
if (valid_user)
|
||||
{
|
||||
// An OK completion code means MQ will accept this user is authenticated
|
||||
*pCompCode = MQCC_OK;
|
||||
*pReason = MQRC_NONE;
|
||||
*pContinuation = MQZCI_STOP;
|
||||
memcpy(pIdentityContext->UserIdentifier, spuser, sizeof(pIdentityContext->UserIdentifier));
|
||||
}
|
||||
else
|
||||
{
|
||||
log_debugf(
|
||||
"User authentication failed user=%.*s effuser=%.*s applname=%.*s cspuser=%s cc=%d reason=%d",
|
||||
trimmed_len(pIdentityContext->UserIdentifier, MQ_USER_ID_LENGTH),
|
||||
pIdentityContext->UserIdentifier,
|
||||
trimmed_len(pApplicationContext->EffectiveUserID, MQ_USER_ID_LENGTH),
|
||||
pApplicationContext->EffectiveUserID,
|
||||
trimmed_len(pApplicationContext->ApplName, MQ_APPL_NAME_LENGTH),
|
||||
pApplicationContext->ApplName,
|
||||
spuser,
|
||||
*pCompCode,
|
||||
*pReason);
|
||||
}
|
||||
if (spuser)
|
||||
{
|
||||
free(spuser);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called during MQDISC, as the inverse of the call to authenticate.
|
||||
*/
|
||||
static void MQENTRY mqsimpleauth_free_user(
|
||||
PMQCHAR pQMgrName,
|
||||
PMQZFP pFreeParms,
|
||||
PMQBYTE pComponentData,
|
||||
PMQLONG pContinuation,
|
||||
|
||||
PMQLONG pCompCode,
|
||||
PMQLONG pReason)
|
||||
{
|
||||
log_debugf("mqsimpleauth_freeuser()");
|
||||
*pCompCode = MQCC_WARNING;
|
||||
*pReason = MQRC_NONE;
|
||||
*pContinuation = MQZCI_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the authorization service is terminated.
|
||||
*/
|
||||
static void MQENTRY mqsimpleauth_terminate(
|
||||
MQHCONFIG hc,
|
||||
MQLONG Options,
|
||||
PMQCHAR pQMgrName,
|
||||
PMQBYTE pComponentData,
|
||||
PMQLONG pCompCode,
|
||||
PMQLONG pReason)
|
||||
{
|
||||
if (Options == MQZTO_PRIMARY)
|
||||
{
|
||||
log_infof("Terminating %s", NAME);
|
||||
log_close();
|
||||
}
|
||||
else {
|
||||
log_debugf("Terminating secondary");
|
||||
}
|
||||
*pCompCode = MQCC_OK;
|
||||
*pReason = MQRC_NONE;
|
||||
}
|
||||
|
||||
153
authservice/mqsimpleauth/src/simpleauth.c
Normal file
153
authservice/mqsimpleauth/src/simpleauth.c
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2021, 2024
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "log.h"
|
||||
#include "simpleauth.h"
|
||||
#include <linux/limits.h>
|
||||
|
||||
const char *_mq_app_secret_file = MQ_APP_SECRET_FILE_DEFAULT;
|
||||
const char *_mq_admin_secret_file = MQ_ADMIN_SECRET_FILE_DEFAULT;
|
||||
|
||||
// Check if the user is valid
|
||||
int simpleauth_authenticate_user(const char *const user, const char *const password)
|
||||
{
|
||||
int result = -1;
|
||||
|
||||
if (simpleauth_valid_user(user))
|
||||
{
|
||||
char *pwd = get_secret_for_user(user);
|
||||
if (pwd != NULL)
|
||||
{
|
||||
if (strcmp(pwd, password) == 0)
|
||||
{
|
||||
log_debugf("Correct password supplied. user=%s", user);
|
||||
result = SIMPLEAUTH_VALID;
|
||||
}
|
||||
else
|
||||
{
|
||||
log_debugf("Incorrect password supplied. user=%s", user);
|
||||
result = SIMPLEAUTH_INVALID_PASSWORD;
|
||||
}
|
||||
memset(pwd, 0, strlen(pwd));
|
||||
free(pwd);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_debugf("Failed to get secret for user '%s'", user);
|
||||
result = SIMPLEAUTH_INVALID_PASSWORD;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log_debugf("User does not exist. user=%s", user);
|
||||
result = SIMPLEAUTH_INVALID_USER;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool simpleauth_valid_user(const char *const user)
|
||||
{
|
||||
bool valid = false;
|
||||
if ((strcmp(user, APP_USER_NAME) == 0 || strcmp(user, ADMIN_USER_NAME) == 0))
|
||||
{
|
||||
valid = true;
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_secret_for_user will return a char* containing the credential for the given user
|
||||
* the credential is read from the filesystem if the relevant file exists and an environment
|
||||
* variable if not
|
||||
*
|
||||
* The caller is responsible for clearing then freeing memory
|
||||
*/
|
||||
char *get_secret_for_user(const char *const user)
|
||||
{
|
||||
if (0 == strcmp(user, APP_USER_NAME))
|
||||
{
|
||||
char *secret = read_secret(_mq_app_secret_file);
|
||||
if (secret != NULL)
|
||||
{
|
||||
return secret;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *pwdFromEnv = getenv("MQ_APP_PASSWORD");
|
||||
if (pwdFromEnv != NULL)
|
||||
{
|
||||
log_infof("Environment variable MQ_APP_PASSWORD is deprecated, use secrets to set the passwords");
|
||||
}
|
||||
return strdup(pwdFromEnv);
|
||||
}
|
||||
}
|
||||
else if (0 == strcmp(user, ADMIN_USER_NAME))
|
||||
{
|
||||
char *secret = read_secret(_mq_admin_secret_file);
|
||||
if (secret != NULL)
|
||||
{
|
||||
return secret;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *pwdFromEnv = getenv("MQ_ADMIN_PASSWORD");
|
||||
if (pwdFromEnv != NULL)
|
||||
{
|
||||
log_infof("Environment variable MQ_ADMIN_PASSWORD is deprecated, use secrets to set the passwords");
|
||||
}
|
||||
return strdup(pwdFromEnv);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* read_secret will return a char* containing the credential read from the filesystem for the given user
|
||||
*
|
||||
* The caller is responsible for clearing then freeing memory
|
||||
*/
|
||||
char *read_secret(const char *const secret)
|
||||
{
|
||||
FILE *fp = fopen(secret, "r");
|
||||
if (fp)
|
||||
{
|
||||
const int line_size = MAX_PASSWORD_LENGTH + 1;
|
||||
char *pwd = malloc(line_size);
|
||||
char *result;
|
||||
result = fgets(pwd, line_size, fp);
|
||||
fclose(fp);
|
||||
if (result == NULL)
|
||||
{
|
||||
memset(pwd, 0, line_size);
|
||||
free(pwd);
|
||||
return NULL;
|
||||
}
|
||||
result[strcspn(result, "\r\n")] = 0;
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
62
authservice/mqsimpleauth/src/simpleauth.h
Normal file
62
authservice/mqsimpleauth/src/simpleauth.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2021, 2024
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _SIMPLEAUTH_H
|
||||
#define _SIMPLEAUTH_H
|
||||
|
||||
#define SIMPLEAUTH_VALID 0
|
||||
#define SIMPLEAUTH_INVALID_USER 1
|
||||
#define SIMPLEAUTH_INVALID_PASSWORD 2
|
||||
#define MQ_APP_SECRET_FILE_DEFAULT "/run/secrets/mqAppPassword"
|
||||
#define MQ_ADMIN_SECRET_FILE_DEFAULT "/run/secrets/mqAdminPassword"
|
||||
#define APP_USER_NAME "app"
|
||||
#define ADMIN_USER_NAME "admin"
|
||||
#define MAX_PASSWORD_LENGTH 256
|
||||
|
||||
extern const char *_mq_app_secret_file;
|
||||
extern const char *_mq_admin_secret_file;
|
||||
|
||||
/**
|
||||
* Authenticate a user, based on the supplied file name.
|
||||
*
|
||||
* @param user the user name to authenticate
|
||||
* @param password the password of the user
|
||||
* @return SIMPLEAUTH_VALID, SIMPLEAUTH_INVALID_USER or SIMPLEAUTH_INVALID_PASSWORD
|
||||
*/
|
||||
int simpleauth_authenticate_user(const char *const user, const char *const password);
|
||||
|
||||
/**
|
||||
* Validate that a user exists in the password file.
|
||||
*
|
||||
* @param user the user name to validate
|
||||
*/
|
||||
bool simpleauth_valid_user(const char *const user);
|
||||
|
||||
/**
|
||||
* Get the secret of the UserId.
|
||||
*
|
||||
* @param user the user name to validate
|
||||
*/
|
||||
char *get_secret_for_user(const char *const user);
|
||||
|
||||
/**
|
||||
* Get the secret of the UserId.
|
||||
*
|
||||
* @param secret path for the secret file
|
||||
*/
|
||||
char *read_secret(const char *const secret);
|
||||
|
||||
#endif
|
||||
412
authservice/mqsimpleauth/src/simpleauth_test.c
Normal file
412
authservice/mqsimpleauth/src/simpleauth_test.c
Normal file
@@ -0,0 +1,412 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2021, 2024
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "log.h"
|
||||
#include "simpleauth.h"
|
||||
#include "simpleauth_test.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
// Headers for multi-threaded tests
|
||||
#include <pthread.h>
|
||||
|
||||
// Start a test and log the function name
|
||||
#define test_start() printf("=== RUN: %s\n", __func__)
|
||||
|
||||
// Indicate test has passed
|
||||
#define test_pass() printf("--- PASS: %s\n", __func__)
|
||||
|
||||
// Indicate test has failed
|
||||
void test_fail(const char *test_name)
|
||||
{
|
||||
printf("--- FAIL: %s\n", test_name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Simple test to read secret
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void test_read_secret_ok()
|
||||
{
|
||||
test_start();
|
||||
char *pwd = read_secret("./src/mqAdminPassword");
|
||||
char *password = "fred:$2y$05$3Fp9";
|
||||
if (0 != strcmp(pwd, password))
|
||||
{
|
||||
printf("%s: pwd: '%s'; password: '%s'\n", __func__, pwd, password);
|
||||
test_fail(__func__);
|
||||
}
|
||||
|
||||
test_pass();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Simple tests for authentication
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void test_simpleauth_valid_user_app_valid()
|
||||
{
|
||||
test_start();
|
||||
bool validUser = simpleauth_valid_user(APP_USER_NAME);
|
||||
printf("%s: app - %d\n", __func__, validUser);
|
||||
|
||||
if (!validUser)
|
||||
test_fail(__func__);
|
||||
test_pass();
|
||||
}
|
||||
|
||||
void test_simpleauth_valid_user_admin_valid()
|
||||
{
|
||||
test_start();
|
||||
bool validUser = simpleauth_valid_user(ADMIN_USER_NAME);
|
||||
printf("%s: admin - %d\n", __func__, validUser);
|
||||
if (!validUser)
|
||||
test_fail(__func__);
|
||||
test_pass();
|
||||
}
|
||||
|
||||
void test_simpleauth_valid_user_george_invalid()
|
||||
{
|
||||
test_start();
|
||||
bool validUser = simpleauth_valid_user("george");
|
||||
printf("%s: george - %d\n", __func__, validUser);
|
||||
if (validUser)
|
||||
test_fail(__func__);
|
||||
test_pass();
|
||||
}
|
||||
|
||||
void test_simpleauth_authenticate_user_fred_unknown()
|
||||
{
|
||||
test_start();
|
||||
test_set_app_password_env("passw0rd-fred-env");
|
||||
int rc = simpleauth_authenticate_user("fred", "passw0rd-fred-env");
|
||||
printf("%s: fred - %d\n", __func__, rc);
|
||||
if (rc != SIMPLEAUTH_INVALID_USER)
|
||||
test_fail(__func__);
|
||||
test_pass();
|
||||
}
|
||||
|
||||
void test_simpleauth_authenticate_user_app_ok()
|
||||
{
|
||||
test_start();
|
||||
test_set_app_password_env("passw0rd-app-env");
|
||||
int rc = simpleauth_authenticate_user("app", "passw0rd-app-env");
|
||||
printf("%s: app - %d\n", __func__, rc);
|
||||
if (rc != SIMPLEAUTH_VALID)
|
||||
test_fail(__func__);
|
||||
test_pass();
|
||||
}
|
||||
|
||||
void test_simpleauth_authenticate_user_admin_ok()
|
||||
{
|
||||
test_start();
|
||||
test_set_admin_password_env("passw0rd-admin-env");
|
||||
int rc = simpleauth_authenticate_user("admin", "passw0rd-admin-env");
|
||||
printf("%s: admin - %d\n", __func__, rc);
|
||||
if (rc != SIMPLEAUTH_VALID)
|
||||
test_fail(__func__);
|
||||
test_pass();
|
||||
}
|
||||
|
||||
void test_simpleauth_authenticate_user_admin_invalidpasswords()
|
||||
{
|
||||
test_start();
|
||||
test_set_admin_password_env("password-admin-env");
|
||||
const char *bad_passwords[] = {
|
||||
"",
|
||||
"passw0rd-admin-env",
|
||||
"Password-admin-env",
|
||||
"pass",
|
||||
"password",
|
||||
"password-app",
|
||||
"password-app-env",
|
||||
"password-admin-env-123"};
|
||||
size_t bad_pass_len = sizeof(bad_passwords) / sizeof(bad_passwords[0]);
|
||||
|
||||
for (int i = 0; i < bad_pass_len; i++)
|
||||
{
|
||||
int rc = simpleauth_authenticate_user("admin", bad_passwords[i]);
|
||||
printf("%s: admin/%s - %d\n", __func__, bad_passwords[i], rc);
|
||||
if (rc != SIMPLEAUTH_INVALID_PASSWORD)
|
||||
test_fail(__func__);
|
||||
test_pass();
|
||||
}
|
||||
}
|
||||
|
||||
void test_simpleauth_authenticate_user_admin_secret_file_valid()
|
||||
{
|
||||
test_start();
|
||||
test_set_admin_password_file("password-admin-file");
|
||||
int rc = simpleauth_authenticate_user("admin", "password-admin-file");
|
||||
printf("%s: admin - %d\n", __func__, rc);
|
||||
if (rc != SIMPLEAUTH_VALID)
|
||||
test_fail(__func__);
|
||||
test_pass();
|
||||
}
|
||||
|
||||
void test_simpleauth_authenticate_user_admin_secret_file_long()
|
||||
{
|
||||
test_start();
|
||||
|
||||
const int test_password_length = MAX_PASSWORD_LENGTH + 7;
|
||||
char test_password[test_password_length];
|
||||
char truncated_password[MAX_PASSWORD_LENGTH + 1];
|
||||
for (int i = 0; i < test_password_length; i++)
|
||||
{
|
||||
test_password[i] = '0' + ((i + 1) % 10);
|
||||
if (i < MAX_PASSWORD_LENGTH)
|
||||
{
|
||||
truncated_password[i] = test_password[i];
|
||||
}
|
||||
}
|
||||
test_password[test_password_length] = 0;
|
||||
truncated_password[MAX_PASSWORD_LENGTH] = 0;
|
||||
test_set_admin_password_file(test_password);
|
||||
|
||||
int rc = simpleauth_authenticate_user("admin", test_password);
|
||||
if (rc != SIMPLEAUTH_INVALID_PASSWORD)
|
||||
{
|
||||
printf("%s: admin/'%s' - %d\n", __func__, test_password, rc);
|
||||
test_fail(__func__);
|
||||
}
|
||||
|
||||
rc = simpleauth_authenticate_user("admin", truncated_password);
|
||||
if (rc != SIMPLEAUTH_VALID)
|
||||
{
|
||||
printf("%s: admin/'%s' - %d\n", __func__, truncated_password, rc);
|
||||
test_fail(__func__);
|
||||
}
|
||||
|
||||
test_pass();
|
||||
}
|
||||
|
||||
void test_simpleauth_authenticate_user_admin_secret_file_invalid()
|
||||
{
|
||||
test_start();
|
||||
|
||||
test_set_admin_password_file("password-admin-file");
|
||||
const char *bad_passwords[] = {
|
||||
"",
|
||||
"passw0rd-admin-file",
|
||||
"Password-admin-file",
|
||||
"pass",
|
||||
"password",
|
||||
"password-app-file",
|
||||
"password-admin-file-123"};
|
||||
size_t bad_pass_len = sizeof(bad_passwords) / sizeof(bad_passwords[0]);
|
||||
|
||||
for (int i = 0; i < bad_pass_len; i++)
|
||||
{
|
||||
int rc = simpleauth_authenticate_user("admin", bad_passwords[i]);
|
||||
printf("%s: admin/%s - %d\n", __func__, bad_passwords[i], rc);
|
||||
if (rc != SIMPLEAUTH_INVALID_PASSWORD)
|
||||
test_fail(__func__);
|
||||
test_pass();
|
||||
}
|
||||
}
|
||||
|
||||
void test_simpleauth_authenticate_user_app_secret_file_valid()
|
||||
{
|
||||
test_start();
|
||||
test_set_app_password_file("password-app-file");
|
||||
int rc = simpleauth_authenticate_user("app", "password-app-file");
|
||||
printf("%s: admin - %d\n", __func__, rc);
|
||||
if (rc != SIMPLEAUTH_VALID)
|
||||
test_fail(__func__);
|
||||
test_pass();
|
||||
}
|
||||
|
||||
void test_simpleauth_authenticate_user_app_secret_file_invalid()
|
||||
{
|
||||
test_start();
|
||||
|
||||
test_set_app_password_file("password-app-file");
|
||||
const char *bad_passwords[] = {
|
||||
"",
|
||||
"passw0rd-app-file",
|
||||
"Password-app-file",
|
||||
"pass",
|
||||
"password",
|
||||
"password-admin-file",
|
||||
"password-app-file-123"};
|
||||
size_t bad_pass_len = sizeof(bad_passwords) / sizeof(bad_passwords[0]);
|
||||
|
||||
for (int i = 0; i < bad_pass_len; i++)
|
||||
{
|
||||
int rc = simpleauth_authenticate_user("app", bad_passwords[i]);
|
||||
printf("%s: app/%s - %d\n", __func__, bad_passwords[i], rc);
|
||||
if (rc != SIMPLEAUTH_INVALID_PASSWORD)
|
||||
test_fail(__func__);
|
||||
test_pass();
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Multi-threaded test
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#define NUM_THREADS 5
|
||||
// Number of tests to perform per thread. Higher numbers are more likely to trigger timing issue.
|
||||
#define NUM_TESTS_PER_THREAD 1000
|
||||
// Maximum number of JSON errors to report (log can get flooded)
|
||||
#define MAX_JSON_ERRORS 10
|
||||
|
||||
// Authenticate multiple users, multiple times
|
||||
void *authenticate_many_times(void *p)
|
||||
{
|
||||
test_set_admin_password_env("passw0rd");
|
||||
test_set_app_password_env("passw0rd");
|
||||
for (int i = 0; i < NUM_TESTS_PER_THREAD; i++)
|
||||
{
|
||||
int rc = simpleauth_authenticate_user("admin", "passw0rd");
|
||||
if (rc != SIMPLEAUTH_VALID)
|
||||
test_fail(__func__);
|
||||
rc = simpleauth_authenticate_user("app", "passw0rd");
|
||||
if (rc != SIMPLEAUTH_VALID)
|
||||
test_fail(__func__);
|
||||
}
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
void check_log_file_valid(char *filename)
|
||||
{
|
||||
int errors = 0;
|
||||
printf("--- Checking log file is valid\n");
|
||||
// Check that the JSON log file isn't corrupted
|
||||
FILE *log = fopen(filename, "r");
|
||||
if (log == NULL)
|
||||
{
|
||||
test_fail(__func__);
|
||||
}
|
||||
const size_t line_size = 1024;
|
||||
char *line = malloc(line_size);
|
||||
while (fgets(line, line_size, log) != NULL)
|
||||
{
|
||||
if ((line[0] != '{') && (errors < MAX_JSON_ERRORS))
|
||||
{
|
||||
printf("*** Invalid JSON detected: %s\n", line);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
if (line)
|
||||
{
|
||||
free(line);
|
||||
}
|
||||
fclose(log);
|
||||
}
|
||||
|
||||
// Test authenticate_user with multiple threads, each doing many authentications
|
||||
void test_simpleauth_authenticate_user_multithreaded(char *logfile)
|
||||
{
|
||||
pthread_t threads[NUM_THREADS];
|
||||
int rc;
|
||||
test_start();
|
||||
// Re-initialize the log to use a file for the multi-threaded test
|
||||
log_init(logfile);
|
||||
for (int i = 0; i < NUM_THREADS; i++)
|
||||
{
|
||||
printf("Creating thread %d\n", i);
|
||||
rc = pthread_create(&threads[i], NULL, authenticate_many_times, NULL);
|
||||
if (rc)
|
||||
{
|
||||
printf("Error: Unable to create thread, %d\n", rc);
|
||||
test_fail(__func__);
|
||||
}
|
||||
}
|
||||
// Wait for all the threads to complete
|
||||
for (int i = 0; i < NUM_THREADS; i++)
|
||||
{
|
||||
pthread_join(threads[i], NULL);
|
||||
}
|
||||
check_log_file_valid(logfile);
|
||||
test_pass();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Test utility functions
|
||||
// ----------------------------------------------------------------------------
|
||||
int write_secret(const char *const secretFile, const char *const value)
|
||||
{
|
||||
FILE *fp = fopen(secretFile, "w");
|
||||
if (fp)
|
||||
{
|
||||
int rc;
|
||||
rc = fprintf(fp, "%s\n", value);
|
||||
fclose(fp);
|
||||
return rc;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void test_set_admin_password_env(const char *const password)
|
||||
{
|
||||
setenv("MQ_ADMIN_PASSWORD", password, 1);
|
||||
_mq_admin_secret_file = MQ_ADMIN_SECRET_FILE_DEFAULT;
|
||||
}
|
||||
|
||||
void test_set_app_password_env(const char *const password)
|
||||
{
|
||||
setenv("MQ_APP_PASSWORD", password, 1);
|
||||
_mq_app_secret_file = MQ_APP_SECRET_FILE_DEFAULT;
|
||||
}
|
||||
|
||||
void test_set_admin_password_file(const char *const password)
|
||||
{
|
||||
write_secret(MQ_ADMIN_SECRET_FILE_TEST, password);
|
||||
_mq_admin_secret_file = MQ_ADMIN_SECRET_FILE_TEST;
|
||||
unsetenv("MQ_ADMIN_PASSWORD");
|
||||
}
|
||||
|
||||
void test_set_app_password_file(const char *const password)
|
||||
{
|
||||
write_secret(MQ_APP_SECRET_FILE_TEST, password);
|
||||
_mq_app_secret_file = MQ_APP_SECRET_FILE_TEST;
|
||||
unsetenv("MQ_APP_PASSWORD");
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
int main()
|
||||
{
|
||||
// Turn on debugging for the tests
|
||||
setenv("DEBUG", "true", true);
|
||||
log_init("simpleauth_test.log");
|
||||
|
||||
test_read_secret_ok();
|
||||
test_simpleauth_valid_user_app_valid();
|
||||
test_simpleauth_valid_user_admin_valid();
|
||||
test_simpleauth_valid_user_george_invalid();
|
||||
test_simpleauth_authenticate_user_fred_unknown();
|
||||
test_simpleauth_authenticate_user_app_ok();
|
||||
test_simpleauth_authenticate_user_admin_ok();
|
||||
test_simpleauth_authenticate_user_admin_invalidpasswords();
|
||||
test_simpleauth_authenticate_user_admin_secret_file_valid();
|
||||
test_simpleauth_authenticate_user_admin_secret_file_long();
|
||||
test_simpleauth_authenticate_user_admin_secret_file_invalid();
|
||||
test_simpleauth_authenticate_user_app_secret_file_valid();
|
||||
test_simpleauth_authenticate_user_app_secret_file_invalid();
|
||||
|
||||
log_close();
|
||||
|
||||
// Call multi-threaded test last, because it re-initializes the log to use a file
|
||||
test_simpleauth_authenticate_user_multithreaded("simpleauth_test_multithreaded.log");
|
||||
}
|
||||
28
authservice/mqsimpleauth/src/simpleauth_test.h
Normal file
28
authservice/mqsimpleauth/src/simpleauth_test.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2024
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _SIMPLEAUTH_TEST_H
|
||||
#define _SIMPLEAUTH_TEST_H
|
||||
|
||||
#define MQ_ADMIN_SECRET_FILE_TEST "testSecretAdmin"
|
||||
#define MQ_APP_SECRET_FILE_TEST "testSecretApp"
|
||||
|
||||
void test_set_admin_password_env(const char *const password);
|
||||
void test_set_admin_password_file(const char *const password);
|
||||
void test_set_app_password_env(const char *const password);
|
||||
void test_set_app_password_file(const char *const password);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user