Skip to content

Commit 6acc281

Browse files
committed
Use alternative string trimming in auth service
Previous string trimming was changing the strings supplied by MQ to be null-terminated. MQ uses fixed-width strings, and the changes to the data could cause problems in the queue manager.
1 parent 08c533e commit 6acc281

File tree

5 files changed

+173
-38
lines changed

5 files changed

+173
-38
lines changed

authservice/mqhtpass/Makefile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,18 @@ CFLAGS += -std=gnu11 -fPIC -Wall ${CFLAGS.${ARCH}}
3333
LIB_APR = -L/usr/lib64 -lapr-1 -laprutil-1
3434
LIB_MQ = -L/opt/mqm/lib64 -lmqm_r
3535

36-
all: $(BUILD_DIR)/mqhtpass.so $(BUILD_DIR)/htpass_test
36+
all: $(BUILD_DIR)/mqhtpass.so $(BUILD_DIR)/htpass_test $(BUILD_DIR)/log_test
3737

3838
$(BUILD_DIR)/log.o : $(SRC_DIR)/log.c $(SRC_DIR)/log.h
3939
mkdir -p ${dir $@}
4040
gcc $(CFLAGS) -c $(SRC_DIR)/log.c -o $@
4141

42+
$(BUILD_DIR)/log_test : $(BUILD_DIR)/log.o
43+
mkdir -p ${dir $@}
44+
gcc $(CFLAGS) $(SRC_DIR)/log_test.c $^ -o $@
45+
# Run Logging tests, and print log if they fail
46+
$@ || (cat log_test*.log && exit 1)
47+
4248
$(BUILD_DIR)/htpass.o : $(SRC_DIR)/htpass.c $(SRC_DIR)/htpass.h
4349
mkdir -p ${dir $@}
4450
gcc $(CFLAGS) -c $(SRC_DIR)/htpass.c -I /usr/include/apr-1 -o $@

authservice/mqhtpass/src/log.c

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
© Copyright IBM Corporation 2021
2+
© Copyright IBM Corporation 2021, 2022
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -124,7 +124,7 @@ void log_printf(const char *source_file, int source_line, const char *level, con
124124
if (strftime(date_buf, sizeof date_buf, "%FT%T", utc))
125125
{
126126
// Round microseconds down to milliseconds, for consistency
127-
cur += snprintf(cur, end-cur, ", \"ibm_datetime\":\"%s.%03ldZ\"", date_buf, now.tv_usec / 1000);
127+
cur += snprintf(cur, end-cur, ", \"ibm_datetime\":\"%s.%03ldZ\"", date_buf, now.tv_usec / (long)1000);
128128
}
129129
cur += snprintf(cur, end-cur, ", \"ibm_processId\":\"%d\"", pid);
130130
cur += snprintf(cur, end-cur, ", \"host\":\"%s\"", hostname);
@@ -146,7 +146,17 @@ void log_printf(const char *source_file, int source_line, const char *level, con
146146

147147
// Important: Just do one file write, to prevent problems with multi-threading.
148148
// This only works if the log message is not too long for the buffer.
149-
fprintf(fp, buf);
149+
fprintf(fp, "%s", buf);
150150
}
151151
}
152152

153+
int trimmed_len(char *s, int max_len)
154+
{
155+
int i;
156+
for (i = max_len - 1; i >= 0; i--)
157+
{
158+
if (s[i] != ' ')
159+
break;
160+
}
161+
return i+1;
162+
}

authservice/mqhtpass/src/log.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
© Copyright IBM Corporation 2021
2+
© Copyright IBM Corporation 2021, 2022
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -59,5 +59,12 @@ void log_close();
5959
*/
6060
#define log_debugf(format,...) log_printf(__FILE__, __LINE__, "DEBUG", format, ##__VA_ARGS__)
6161

62+
/**
63+
* Return the length of the string when trimmed of trailing spaces.
64+
* IBM MQ uses fixed length strings, so this function can be used to print
65+
* a trimmed version of a string using the "%.*s" printf format string.
66+
* For example, `log_printf("%.*s", trimmed_len(fw_str, 48), fw_str)`
67+
*/
68+
int trimmed_len(char *s, int);
6269

6370
#endif
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
© Copyright IBM Corporation 2022
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
#include <stdbool.h>
18+
#include <stdio.h>
19+
#include <stdlib.h>
20+
#include <string.h>
21+
#include "log.h"
22+
23+
// Headers for multi-threaded tests
24+
#include <pthread.h>
25+
26+
// Start a test and log the function name
27+
#define test_start() printf("=== RUN: %s\n", __func__)
28+
29+
// Indicate test has passed
30+
#define test_pass() printf("--- PASS: %s\n", __func__)
31+
32+
// The length of strings used in the tests
33+
#define STR_LEN 5
34+
35+
// Indicate test has failed
36+
void test_fail(const char *test_name)
37+
{
38+
printf("--- FAIL: %s\n", test_name);
39+
exit(1);
40+
}
41+
42+
// Print a fixed-width string in hexadecimal
43+
void print_hex(char fw_string[STR_LEN])
44+
{
45+
printf("[");
46+
for (int i=0; i<STR_LEN; i++)
47+
{
48+
printf("%02x", fw_string[i]);
49+
if (i < STR_LEN-1)
50+
printf(",");
51+
}
52+
printf("]");
53+
}
54+
55+
// ----------------------------------------------------------------------------
56+
// Tests for string manipulation
57+
// ----------------------------------------------------------------------------
58+
59+
void test_trimmed_len(const char *test_name, char fw_string[STR_LEN], int expected_len)
60+
{
61+
printf("=== RUN: %s\n", test_name);
62+
int len;
63+
// Create a copy of the fixed-width string
64+
char fw_string2[STR_LEN];
65+
memcpy(fw_string2, fw_string, STR_LEN * sizeof(char));
66+
// Call the function under test
67+
len = trimmed_len(fw_string, STR_LEN);
68+
// Check the result is correct
69+
if (len != expected_len)
70+
{
71+
printf("%s: Expected result to be %d; got %d\n", __func__, expected_len, len);
72+
test_fail(test_name);
73+
}
74+
// Check that the original string has not been changed
75+
for (int i=0; i<STR_LEN; i++)
76+
{
77+
if (fw_string[i] != fw_string2[i])
78+
{
79+
printf("%c-%c\n", fw_string[i], fw_string2[i]);
80+
printf("%s: Expected string to be identical to input hex ", __func__);
81+
print_hex(fw_string2);
82+
printf("; got hex ");
83+
print_hex(fw_string);
84+
printf("\n");
85+
test_fail(test_name);
86+
}
87+
}
88+
printf("--- PASS: %s\n", test_name);
89+
}
90+
91+
void test_trimmed_len_normal()
92+
{
93+
char fw_string[STR_LEN] = {'a','b','c',' ',' '};
94+
test_trimmed_len(__func__, fw_string, 3);
95+
}
96+
97+
void test_trimmed_len_full()
98+
{
99+
char fw_string[STR_LEN] = {'a','b','c','d','e'};
100+
test_trimmed_len(__func__, fw_string, 5);
101+
}
102+
103+
void test_trimmed_len_empty()
104+
{
105+
char fw_string[STR_LEN] = {' ',' ',' ',' ',' '};
106+
test_trimmed_len(__func__, fw_string, 0);
107+
}
108+
109+
// ----------------------------------------------------------------------------
110+
111+
int main()
112+
{
113+
// Turn on debugging for the tests
114+
setenv("DEBUG", "true", true);
115+
log_init("log_test.log");
116+
test_trimmed_len_normal();
117+
test_trimmed_len_full();
118+
test_trimmed_len_empty();
119+
log_close();
120+
}

authservice/mqhtpass/src/mqhtpass.c

Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@ static MQZ_TERM_AUTHORITY mqhtpass_terminate;
3434
#define HTPASSWD_FILE "/etc/mqm/mq.htpasswd"
3535
#define NAME "MQ Advanced for Developers custom authentication service"
3636

37-
static char *trim(char *s);
38-
3937
/**
4038
* Initialization and entrypoint for the dynamically loaded
4139
* authorization installable service. It registers the addresses of the
@@ -80,7 +78,7 @@ void MQENTRY MQStart(
8078
{
8179
log_infof("Initializing %s", NAME);
8280
}
83-
log_debugf("MQStart options=%s qmgr=%s", ((Options == MQZIO_SECONDARY) ? "Secondary" : "Primary"), trim(QMgrName));
81+
log_debugf("MQStart options=%s qmgr=%.*s", ((Options == MQZIO_SECONDARY) ? "Secondary" : "Primary"), trimmed_len(QMgrName, MQ_Q_MGR_NAME_LENGTH), QMgrName);
8482

8583
if (!htpass_valid_file(HTPASSWD_FILE))
8684
{
@@ -176,11 +174,14 @@ static void MQENTRY mqhtpass_authenticate_user_csp(
176174
// Tell the queue manager to continue trying other authorization services, as they might have the user.
177175
*pContinuation = MQZCI_CONTINUE;
178176
log_debugf(
179-
"User authentication failed due to invalid user. user=%s effuser=%s applname=%s csp_user=%s cc=%d reason=%d",
180-
trim(pIdentityContext->UserIdentifier),
181-
trim(pApplicationContext->EffectiveUserID),
182-
trim(pApplicationContext->ApplName),
183-
trim(csp_user),
177+
"User authentication failed due to invalid user. user=%.*s effuser=%.*s applname=%.*s csp_user=%s cc=%d reason=%d",
178+
trimmed_len(pIdentityContext->UserIdentifier, MQ_USER_ID_LENGTH),
179+
pIdentityContext->UserIdentifier,
180+
trimmed_len(pApplicationContext->EffectiveUserID, MQ_USER_ID_LENGTH),
181+
pApplicationContext->EffectiveUserID,
182+
trimmed_len(pApplicationContext->ApplName, MQ_APPL_NAME_LENGTH),
183+
pApplicationContext->ApplName,
184+
csp_user,
184185
*pCompCode,
185186
*pReason);
186187
}
@@ -192,11 +193,14 @@ static void MQENTRY mqhtpass_authenticate_user_csp(
192193
// Tell the queue manager to stop trying other authorization services.
193194
*pContinuation = MQZCI_STOP;
194195
log_debugf(
195-
"User authentication failed due to invalid password. user=%s effuser=%s applname=%s csp_user=%s cc=%d reason=%d",
196-
trim(pIdentityContext->UserIdentifier),
197-
trim(pApplicationContext->EffectiveUserID),
198-
trim(pApplicationContext->ApplName),
199-
trim(csp_user),
196+
"User authentication failed due to invalid password. user=%.*s effuser=%.*s applname=%.*s csp_user=%s cc=%d reason=%d",
197+
trimmed_len(pIdentityContext->UserIdentifier, MQ_USER_ID_LENGTH),
198+
pIdentityContext->UserIdentifier,
199+
trimmed_len(pApplicationContext->EffectiveUserID, MQ_USER_ID_LENGTH),
200+
pApplicationContext->EffectiveUserID,
201+
trimmed_len(pApplicationContext->ApplName, MQ_APPL_NAME_LENGTH),
202+
pApplicationContext->ApplName,
203+
csp_user,
200204
*pCompCode,
201205
*pReason);
202206
}
@@ -275,11 +279,14 @@ static void MQENTRY mqhtpass_authenticate_user(
275279
else
276280
{
277281
log_debugf(
278-
"User authentication failed user=%s effuser=%s applname=%s cspuser=%s cc=%d reason=%d",
279-
trim(pIdentityContext->UserIdentifier),
280-
trim(pApplicationContext->EffectiveUserID),
281-
trim(pApplicationContext->ApplName),
282-
trim(spuser),
282+
"User authentication failed user=%.*s effuser=%.*s applname=%.*s cspuser=%s cc=%d reason=%d",
283+
trimmed_len(pIdentityContext->UserIdentifier, MQ_USER_ID_LENGTH),
284+
pIdentityContext->UserIdentifier,
285+
trimmed_len(pApplicationContext->EffectiveUserID, MQ_USER_ID_LENGTH),
286+
pApplicationContext->EffectiveUserID,
287+
trimmed_len(pApplicationContext->ApplName, MQ_APPL_NAME_LENGTH),
288+
pApplicationContext->ApplName,
289+
spuser,
283290
*pCompCode,
284291
*pReason);
285292
}
@@ -333,18 +340,3 @@ static void MQENTRY mqhtpass_terminate(
333340
*pReason = MQRC_NONE;
334341
}
335342

336-
/**
337-
* Remove trailing spaces from a string.
338-
*/
339-
static char *trim(char *s)
340-
{
341-
int i;
342-
for (i = strlen(s) - 1; i >= 0; i--)
343-
{
344-
if (s[i] == ' ')
345-
s[i] = 0;
346-
else
347-
break;
348-
}
349-
return s;
350-
}

0 commit comments

Comments
 (0)