src/lib-var-expand/var-expand-parser.c
src/lib-var-expand/var-expand-parser.h
src/lib-var-expand/var-expand-lexer.c
+src/lib-sasl/ntlm_dummy
-I$(top_srcdir)/src/lib-json \
-I$(top_srcdir)/src/lib-ssl-iostream \
-I$(top_srcdir)/src/lib-otp \
- -I$(top_srcdir)/src/lib-auth
+ -I$(top_srcdir)/src/lib-auth \
+ -DTEST_WINBIND_HELPER_PATH=\""$(abs_builddir)/ntlm_dummy"\"
client_mechanisms = \
dsasl-client-mech-anonymous.c \
pkginc_libdir=$(pkgincludedir)
pkginc_lib_HEADERS = $(headers)
+noinst_HEADERS =\
+ dsasl-client-mech-ntlm-dummy.h
+
test_programs = \
test-sasl-oauth2 \
test-sasl-client \
test-sasl-authentication
-noinst_PROGRAMS = $(test_programs)
+noinst_PROGRAMS = $(test_programs) ntlm_dummy
test_libs = \
libsasl.la \
test_sasl_client_LDADD = $(test_libs)
test_sasl_client_DEPENDENCIES = $(test_deps)
-test_sasl_authentication_SOURCES = test-sasl-authentication.c
+test_sasl_authentication_SOURCES = \
+ dsasl-client-mech-ntlm-dummy.c \
+ test-sasl-authentication.c
test_sasl_authentication_LDADD = $(test_libs)
test_sasl_authentication_DEPENDENCIES = $(test_deps)
+ntlm_dummy_SOURCES = ntlm_dummy.c
+ntlm_dummy_LDADD = ../lib/liblib.la
+ntlm_dummy_DEPENDENCIES = ../lib/liblib.la
+
check-local:
for bin in $(test_programs); do \
if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \
--- /dev/null
+/* Copyright (c) 2023 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "dsasl-client-private.h"
+#include "dsasl-client-mech-ntlm-dummy.h"
+
+/* Dummy NTLM mechanism
+
+ This has nothing to do with actual NTLM; it just serves as a means to test
+ the winbind server mechanisms.
+ */
+
+enum ntlm_state {
+ STATE_INIT = 0,
+ STATE_RESPONSE
+};
+
+struct ntlm_dsasl_client {
+ struct dsasl_client client;
+ enum ntlm_state state;
+};
+
+void dasl_client_mech_ntlm_init_dummy(void);
+
+static enum dsasl_client_result
+mech_ntlm_input(struct dsasl_client *_client,
+ const unsigned char *input, size_t input_len,
+ const char **error_r)
+{
+ static const char *challenge = "Challenge";
+ struct ntlm_dsasl_client *client =
+ container_of(_client, struct ntlm_dsasl_client, client);
+ size_t chal_size;
+
+ if (client->state == STATE_RESPONSE) {
+ *error_r = "Server didn't finish authentication";
+ return DSASL_CLIENT_RESULT_ERR_PROTOCOL;
+ }
+ chal_size = strlen(challenge);
+ if (input_len != chal_size ||
+ memcmp(input, challenge, chal_size) != 0) {
+ *error_r = "Invalid challenge";
+ return DSASL_CLIENT_RESULT_ERR_PROTOCOL;
+ }
+ client->state++;
+ return DSASL_CLIENT_RESULT_OK;
+}
+
+static enum dsasl_client_result
+mech_ntlm_output(struct dsasl_client *_client,
+ const unsigned char **output_r, size_t *output_len_r,
+ const char **error_r)
+{
+ struct ntlm_dsasl_client *client =
+ container_of(_client, struct ntlm_dsasl_client, client);
+
+ if (_client->set.authid == NULL) {
+ *error_r = "authid not set";
+ return DSASL_CLIENT_RESULT_ERR_INTERNAL;
+ }
+ if (_client->password == NULL) {
+ *error_r = "password not set";
+ return DSASL_CLIENT_RESULT_ERR_INTERNAL;
+ }
+
+ const char *response;
+
+ switch (client->state) {
+ case STATE_INIT:
+ *output_r = uchar_empty_ptr;
+ *output_len_r = 0;
+ return DSASL_CLIENT_RESULT_OK;
+ case STATE_RESPONSE:
+ response = t_strconcat("Response: ", _client->set.authid, NULL);
+ *output_r = (const unsigned char *)response;
+ *output_len_r = strlen(response);
+ return DSASL_CLIENT_RESULT_OK;
+ }
+ i_unreached();
+}
+
+const struct dsasl_client_mech dsasl_client_mech_ntlm = {
+ .name = SASL_MECH_NAME_NTLM,
+ .struct_size = sizeof(struct ntlm_dsasl_client),
+
+ .input = mech_ntlm_input,
+ .output = mech_ntlm_output
+};
+
+void dsasl_client_mech_ntlm_init_dummy(void)
+{
+ dsasl_client_mech_register(&dsasl_client_mech_ntlm);
+}
--- /dev/null
+#ifndef DSASL_CLIENT_MECH_NTLM_DUMMY_H
+#define DSASL_CLIENT_MECH_NTLM_DUMMY_H
+
+void dsasl_client_mech_ntlm_init_dummy(void);
+
+#endif
--- /dev/null
+/* Copyright (c) 2023 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "base64.h"
+#include "istream.h"
+
+#include <stdio.h>
+
+/* Dummy NTLM helper
+
+ This has nothing to do with actual NTLM; it just serves as a means to test
+ the winbind server mechanisms.
+ */
+
+#define USER "user"
+#define MAX_LINE_LENGTH 16384
+
+static void run_ntlm_cmd_yr(void)
+{
+ static const char *challenge = "Challenge";
+ const size_t chal_size = strlen(challenge);
+ string_t *str;
+
+ str = t_str_new(MAX_BASE64_ENCODED_SIZE(chal_size + 1));
+ base64_encode(challenge, chal_size, str);
+
+ printf("TT %s\n", str_c(str));
+}
+
+static void run_ntlm_cmd_kk(const char *param)
+{
+ static const char *response = "Response: ";
+ static const char *user = USER;
+ const size_t resp_size = strlen(response);
+ const size_t user_size = strlen(user);
+ const unsigned char *data;
+ size_t size;
+ buffer_t *buf;
+
+ buf = t_base64_decode_str(param);
+ data = buf->data;
+ size = buf->used;
+
+ if (size <= resp_size ||
+ memcmp(data, response, resp_size) != 0) {
+ printf("BH Invalid client response\n");
+ return;
+ }
+ data += resp_size;
+ size -= resp_size;
+
+ if (size <= user_size || memcmp(data, user, user_size) != 0 ||
+ data[user_size] != '@') {
+ printf("NA No such user\n");
+ return;
+ }
+
+ printf("AF user@EXAMPLE.COM\n");
+}
+
+static void run_ntlm(void)
+{
+ struct istream *input;
+ const char *line;
+ int fd = 0;
+
+ input = i_stream_create_fd_autoclose(&fd, MAX_LINE_LENGTH);
+
+ while ((line = i_stream_read_next_line(input)) != NULL) {
+ const char *const *args = t_strsplit_spaces(line, " ");
+
+ if (strcmp(args[0], "YR") == 0) {
+ if (args[1] != NULL && strlen(args[1]) > 0)
+ i_fatal("Invalid YR command: %s", line);
+ run_ntlm_cmd_yr();
+ } else if (strcmp(args[0], "KK") == 0) {
+ if (args[1] == NULL || args[2] != NULL)
+ i_fatal("Invalid KK command: %s", line);
+ run_ntlm_cmd_kk(args[1]);
+ } else {
+ i_fatal("Invalid command: %s", line);
+ }
+ fflush(stdout);
+ }
+
+ i_stream_destroy(&input);
+}
+
+int main(int argc, char *argv[])
+{
+ lib_init();
+
+ if (argc < 2)
+ i_fatal("Invalid arguments");
+ if (strcmp(argv[1], "--helper-protocol=squid-2.5-ntlmssp") == 0)
+ run_ntlm();
+ else
+ i_fatal("Invalid arguments");
+
+ lib_deinit();
+}
/* Copyright (c) 2025 Dovecot authors, see the included COPYING file */
#include "lib.h"
+#include "lib-signals.h"
#include "str.h"
#include "base64.h"
#include "randgen.h"
#include "sasl-server.h"
#include "sasl-server-oauth2.h"
#include "dsasl-client.h"
+#include "dsasl-client-mech-ntlm-dummy.h"
#include <unistd.h>
{
struct sasl_server *server;
struct sasl_server_instance *server_inst;
- const struct sasl_server_mech *server_mech;
test_begin(t_strdup_printf("sasl %s %s%s",
label, test->mech,
sasl_server_mech_register_xoauth2(server_inst, &server_oauth2_funcs,
NULL);
+ struct sasl_server_winbind_settings winbind_set = {
+ .helper_path = TEST_WINBIND_HELPER_PATH,
+ };
+ sasl_server_mech_register_winbind_ntlm(server_inst, &winbind_set);
+
+ const struct sasl_server_mech *server_mech;
+
server_mech = sasl_server_mech_find(server_inst, test->mech);
i_assert(server_mech != NULL);
.password = "",
},
},
+ /* NTLM */
+ {
+ .mech = "NTLM",
+ .authid_type = SASL_SERVER_AUTHID_TYPE_USERNAME,
+ .server = {
+ .authid = "user@EXAMPLE.COM",
+ .authzid = "",
+ .password = "",
+ },
+ },
/* XOAUTH2 */
{
.mech = "XOAUTH2",
},
.failure = TRUE,
},
+ /* NTLM */
+ {
+ .mech = "NTLM",
+ .authid_type = SASL_SERVER_AUTHID_TYPE_USERNAME,
+ .server = {
+ .authid = "user@EXAMPLE.COM",
+ .authzid = "",
+ .password = "",
+ },
+ .client = {
+ .authid = "userb@EXAMPLE.COM",
+ },
+ .failure = TRUE,
+ },
/* XOAUTH2 */
{
.mech = "XOAUTH2",
int ret, c;
lib_init();
+ lib_signals_init();
while ((c = getopt(argc, argv, "D")) > 0) {
switch (c) {
event_set_forced_debug(test_event, debug);
password_schemes_init();
dsasl_clients_init();
+ dsasl_client_mech_ntlm_init_dummy();
ret = test_run(test_functions);
dsasl_clients_deinit();
password_schemes_deinit();
event_unref(&test_event);
+ lib_signals_deinit();
lib_deinit();
return ret;
}