changeset 16486:1cbff0a8a849

Added initial libsasl for implementing client side SASL library. Initially supports PLAIN and LOGIN mechanisms.
author Timo Sirainen <tss@iki.fi>
date Sun, 09 Jun 2013 06:02:14 +0300
parents 7e54af474ea4
children 266101990d63
files configure.ac src/Makefile.am src/lib-dovecot/Makefile.am src/lib-sasl/Makefile.am src/lib-sasl/mech-login.c src/lib-sasl/mech-plain.c src/lib-sasl/sasl-client-private.h src/lib-sasl/sasl-client.c src/lib-sasl/sasl-client.h
diffstat 9 files changed, 336 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/configure.ac	Sun Jun 09 03:10:43 2013 +0300
+++ b/configure.ac	Sun Jun 09 06:02:14 2013 +0300
@@ -2559,7 +2559,7 @@
   LIBDOVECOT_COMPRESS='$(top_builddir)/src/lib-compression/libcompression.la'
   LIBDOVECOT_LDA='$(top_builddir)/src/lib-lda/libdovecot-lda.la'
 else
-  LIBDOVECOT_DEPS='$(top_builddir)/src/lib-master/libmaster.la $(top_builddir)/src/lib-settings/libsettings.la $(top_builddir)/src/lib-http/libhttp.la $(top_builddir)/src/lib-dict/libdict.la $(top_builddir)/src/lib-dns/libdns.la $(top_builddir)/src/lib-fs/libfs.la $(top_builddir)/src/lib-imap/libimap.la $(top_builddir)/src/lib-mail/libmail.la $(top_builddir)/src/lib-auth/libauth.la $(top_builddir)/src/lib-charset/libcharset.la $(top_builddir)/src/lib-ssl-iostream/libssl_iostream.la $(top_builddir)/src/lib-test/libtest.la $(top_builddir)/src/lib/liblib.la'
+  LIBDOVECOT_DEPS='$(top_builddir)/src/lib-master/libmaster.la $(top_builddir)/src/lib-settings/libsettings.la $(top_builddir)/src/lib-http/libhttp.la $(top_builddir)/src/lib-dict/libdict.la $(top_builddir)/src/lib-dns/libdns.la $(top_builddir)/src/lib-fs/libfs.la $(top_builddir)/src/lib-imap/libimap.la $(top_builddir)/src/lib-mail/libmail.la $(top_builddir)/src/lib-sasl/libsasl.la $(top_builddir)/src/lib-auth/libauth.la $(top_builddir)/src/lib-charset/libcharset.la $(top_builddir)/src/lib-ssl-iostream/libssl_iostream.la $(top_builddir)/src/lib-test/libtest.la $(top_builddir)/src/lib/liblib.la'
   LIBDOVECOT="$LIBDOVECOT_DEPS \$(LIBICONV) \$(MODULE_LIBS)"
   LIBDOVECOT_STORAGE_LAST='$(top_builddir)/src/lib-storage/list/libstorage_list.la $(top_builddir)/src/lib-storage/index/libstorage_index.la $(top_builddir)/src/lib-storage/libstorage.la $(top_builddir)/src/lib-index/libindex.la $(top_builddir)/src/lib-imap-storage/libimap-storage.la'
   LIBDOVECOT_STORAGE_FIRST='$(top_builddir)/src/lib-storage/libstorage_service.la $(top_builddir)/src/lib-storage/register/libstorage_register.la'
@@ -2822,6 +2822,7 @@
 src/lib-ntlm/Makefile
 src/lib-otp/Makefile
 src/lib-dovecot/Makefile
+src/lib-sasl/Makefile
 src/lib-settings/Makefile
 src/lib-ssl-iostream/Makefile
 src/lib-test/Makefile
--- a/src/Makefile.am	Sun Jun 09 03:10:43 2013 +0300
+++ b/src/Makefile.am	Sun Jun 09 06:02:14 2013 +0300
@@ -7,6 +7,7 @@
 	lib-charset \
 	lib-dns \
 	lib-dict \
+	lib-sasl \
 	lib-ssl-iostream \
 	lib-http \
 	lib-fs \
--- a/src/lib-dovecot/Makefile.am	Sun Jun 09 03:10:43 2013 +0300
+++ b/src/lib-dovecot/Makefile.am	Sun Jun 09 06:02:14 2013 +0300
@@ -7,6 +7,7 @@
 	../lib-dict/libdict.la \
 	../lib-imap/libimap.la \
 	../lib-mail/libmail.la \
+	../lib-sasl/libsasl.la \
 	../lib-auth/libauth.la \
 	../lib-dns/libdns.la \
 	../lib-charset/libcharset.la \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-sasl/Makefile.am	Sun Jun 09 06:02:14 2013 +0300
@@ -0,0 +1,16 @@
+noinst_LTLIBRARIES = libsasl.la
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/src/lib
+
+libsasl_la_SOURCES = \
+	mech-login.c \
+	mech-plain.c \
+	sasl-client.c 
+
+headers = \
+	sasl-client.h \
+	sasl-client-private.h
+
+pkginc_libdir=$(pkgincludedir)
+pkginc_lib_HEADERS = $(headers)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-sasl/mech-login.c	Sun Jun 09 06:02:14 2013 +0300
@@ -0,0 +1,73 @@
+/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "sasl-client-private.h"
+
+enum login_state {
+	STATE_INIT = 0,
+	STATE_USER,
+	STATE_PASS
+};
+
+struct login_sasl_client {
+	struct sasl_client client;
+	enum login_state state;
+};
+
+static int
+mech_login_input(struct sasl_client *_client,
+		 const unsigned char *input ATTR_UNUSED,
+		 unsigned int input_len ATTR_UNUSED,
+		 const char **error_r)
+{
+	struct login_sasl_client *client = (struct login_sasl_client *)_client;
+
+	if (client->state == STATE_PASS) {
+		*error_r = "Server didn't finish authentication";
+		return -1;
+	}
+	client->state++;
+	return 0;
+}
+
+static int
+mech_login_output(struct sasl_client *_client,
+		  const unsigned char **output_r, unsigned int *output_len_r,
+		  const char **error_r)
+{
+	struct login_sasl_client *client = (struct login_sasl_client *)_client;
+
+	if (_client->set.authid == NULL) {
+		*error_r = "authid not set";
+		return -1;
+	}
+	if (_client->password == NULL) {
+		*error_r = "password not set";
+		return -1;
+	}
+
+	switch (client->state) {
+	case STATE_INIT:
+		*output_r = &uchar_nul;
+		*output_len_r = 0;
+		return 0;
+	case STATE_USER:
+		*output_r = (const unsigned char *)_client->set.authid;
+		*output_len_r = strlen(_client->set.authid);
+		return 0;
+	case STATE_PASS:
+		*output_r = (const unsigned char *)_client->set.password;
+		*output_len_r = strlen(_client->set.password);
+		return 0;
+	}
+	i_unreached();
+}
+
+const struct sasl_client_mech sasl_client_mech_login = {
+	.name = "LOGIN",
+	.struct_size = sizeof(struct login_sasl_client),
+
+	.input = mech_login_input,
+	.output = mech_login_output
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-sasl/mech-plain.c	Sun Jun 09 06:02:14 2013 +0300
@@ -0,0 +1,68 @@
+/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "sasl-client-private.h"
+
+struct plain_sasl_client {
+	struct sasl_client client;
+	bool output_sent;
+};
+
+static int
+mech_plain_input(struct sasl_client *_client,
+		 const unsigned char *input ATTR_UNUSED, unsigned int input_len,
+		 const char **error_r)
+{
+	struct plain_sasl_client *client = (struct plain_sasl_client *)_client;
+
+	if (!client->output_sent) {
+		if (input_len > 0) {
+			*error_r = "Server sent non-empty initial response";
+			return -1;
+		}
+	} else {
+		*error_r = "Server didn't finish authentication";
+		return -1;
+	}
+	return 0;
+}
+
+static int
+mech_plain_output(struct sasl_client *_client,
+		  const unsigned char **output_r, unsigned int *output_len_r,
+		  const char **error_r)
+{
+	struct plain_sasl_client *client = (struct plain_sasl_client *)_client;
+	string_t *str;
+
+	if (_client->set.authid == NULL) {
+		*error_r = "authid not set";
+		return -1;
+	}
+	if (_client->password == NULL) {
+		*error_r = "password not set";
+		return -1;
+	}
+
+	str = str_new(_client->pool, 64);
+	if (_client->set.authzid != NULL)
+		str_append(str, _client->set.authzid);
+	str_append_c(str, '\0');
+	str_append(str, _client->set.authid);
+	str_append_c(str, '\0');
+	str_append(str, _client->password);
+
+	*output_r = str_data(str);
+	*output_len_r = str_len(str);
+	client->output_sent = TRUE;
+	return 0;
+}
+
+const struct sasl_client_mech sasl_client_mech_plain = {
+	.name = "PLAIN",
+	.struct_size = sizeof(struct plain_sasl_client),
+
+	.input = mech_plain_input,
+	.output = mech_plain_output
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-sasl/sasl-client-private.h	Sun Jun 09 06:02:14 2013 +0300
@@ -0,0 +1,33 @@
+#ifndef SASL_CLIENT_PRIVATE_H
+#define SASL_CLIENT_PRIVATE_H
+
+#include "sasl-client.h"
+
+struct sasl_client {
+	pool_t pool;
+	struct sasl_client_settings set;
+	char *password;
+	const struct sasl_client_mech *mech;
+};
+
+struct sasl_client_mech {
+	const char *name;
+	size_t struct_size;
+
+	int (*input)(struct sasl_client *client,
+		     const unsigned char *input,
+		     unsigned int input_len,
+		     const char **error_r);
+	int (*output)(struct sasl_client *client,
+		      const unsigned char **output_r,
+		      unsigned int *output_len_r,
+		      const char **error_r);
+	void (*free)(struct sasl_client *client);
+};
+
+extern const struct sasl_client_mech sasl_client_mech_login;
+
+void sasl_client_mech_register(const struct sasl_client_mech *mech);
+void sasl_client_mech_unregister(const struct sasl_client_mech *mech);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-sasl/sasl-client.c	Sun Jun 09 06:02:14 2013 +0300
@@ -0,0 +1,104 @@
+/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "safe-memset.h"
+#include "sasl-client-private.h"
+
+static ARRAY(const struct sasl_client_mech *) sasl_mechanisms = ARRAY_INIT;
+
+static const struct sasl_client_mech *
+sasl_client_mech_find_idx(const char *name, unsigned int *idx_r)
+{
+	const struct sasl_client_mech *const *mechp;
+
+	array_foreach(&sasl_mechanisms, mechp) {
+		if (strcasecmp((*mechp)->name, name) == 0) {
+			*idx_r = array_foreach_idx(&sasl_mechanisms, mechp);
+			return *mechp;
+		}
+	}
+	return NULL;
+}
+
+const struct sasl_client_mech *sasl_client_mech_find(const char *name)
+{
+	unsigned int idx;
+
+	return sasl_client_mech_find_idx(name, &idx);
+}
+
+const char *sasl_client_mech_get_name(const struct sasl_client_mech *mech)
+{
+	return mech->name;
+}
+
+void sasl_client_mech_register(const struct sasl_client_mech *mech)
+{
+	array_append(&sasl_mechanisms, &mech, 1);
+}
+
+void sasl_client_mech_unregister(const struct sasl_client_mech *mech)
+{
+	unsigned int idx;
+
+	if (sasl_client_mech_find_idx(mech->name, &idx) == NULL)
+		i_panic("SASL mechanism not registered: %s", mech->name);
+	array_delete(&sasl_mechanisms, idx, 1);
+}
+
+struct sasl_client *sasl_client_new(const struct sasl_client_mech *mech,
+				    const struct sasl_client_settings *set)
+{
+	struct sasl_client *client;
+	pool_t pool = pool_alloconly_create("sasl client", 512);
+
+	client = p_malloc(pool, mech->struct_size);
+	client->pool = pool;
+	client->mech = mech;
+	client->set.authid = p_strdup(pool, set->authid);
+	client->set.authzid = p_strdup(pool, set->authzid);
+	client->password = p_strdup(pool, set->password);
+	client->set.password = client->password;
+	return client;
+}
+
+void sasl_client_free(struct sasl_client **_client)
+{
+	struct sasl_client *client = *_client;
+
+	*_client = NULL;
+
+	if (client->mech->free != NULL)
+		client->mech->free(client);
+	safe_memset(client->password, 0, strlen(client->password));
+	pool_unref(&client->pool);
+}
+
+int sasl_client_input(struct sasl_client *client,
+		      const unsigned char *input,
+		      unsigned int input_len,
+		      const char **error_r)
+{
+	return client->mech->input(client, input, input_len, error_r);
+}
+
+int sasl_client_output(struct sasl_client *client,
+		       const unsigned char **output_r,
+		       unsigned int *output_len_r,
+		       const char **error_r)
+{
+	return client->mech->output(client, output_r, output_len_r, error_r);
+}
+
+void sasl_clients_init(void)
+{
+	i_array_init(&sasl_mechanisms, 8);
+	sasl_client_mech_register(&sasl_client_mech_plain);
+	sasl_client_mech_register(&sasl_client_mech_login);
+}
+
+void sasl_clients_deinit(void)
+{
+	array_free(&sasl_mechanisms);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-sasl/sasl-client.h	Sun Jun 09 06:02:14 2013 +0300
@@ -0,0 +1,38 @@
+#ifndef SASL_CLIENT_H
+#define SASL_CLIENT_H
+
+struct sasl_client_settings {
+	/* authentication ID - must be set with most mechanisms */
+	const char *authid;
+	/* authorization ID ("master user") */
+	const char *authzid;
+	/* password - must be set with most mechanisms */
+	const char *password;
+};
+
+/* PLAIN mechanism always exists and can be accessed directly via this. */
+extern const struct sasl_client_mech sasl_client_mech_plain;
+
+const struct sasl_client_mech *sasl_client_mech_find(const char *name);
+const char *sasl_client_mech_get_name(const struct sasl_client_mech *mech);
+
+struct sasl_client *sasl_client_new(const struct sasl_client_mech *mech,
+				    const struct sasl_client_settings *set);
+void sasl_client_free(struct sasl_client **client);
+
+/* Call for server input. */
+int sasl_client_input(struct sasl_client *client,
+		      const unsigned char *input,
+		      unsigned int input_len,
+		      const char **error_r);
+/* Call for getting server output. Also used to get the initial SASL response
+   if supported by the protocol. */
+int sasl_client_output(struct sasl_client *client,
+		       const unsigned char **output_r,
+		       unsigned int *output_len_r,
+		       const char **error_r);
+
+void sasl_clients_init(void);
+void sasl_clients_deinit(void);
+
+#endif