]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Added initial libsasl for implementing client side SASL library.
authorTimo Sirainen <tss@iki.fi>
Sun, 9 Jun 2013 03:02:14 +0000 (06:02 +0300)
committerTimo Sirainen <tss@iki.fi>
Sun, 9 Jun 2013 03:02:14 +0000 (06:02 +0300)
Initially supports PLAIN and LOGIN mechanisms.

configure.ac
src/Makefile.am
src/lib-dovecot/Makefile.am
src/lib-sasl/Makefile.am [new file with mode: 0644]
src/lib-sasl/mech-login.c [new file with mode: 0644]
src/lib-sasl/mech-plain.c [new file with mode: 0644]
src/lib-sasl/sasl-client-private.h [new file with mode: 0644]
src/lib-sasl/sasl-client.c [new file with mode: 0644]
src/lib-sasl/sasl-client.h [new file with mode: 0644]

index 6bae12111daef3c72346f2453d7867d73f940398..121082b1d03777211c722ef1ba0a30313ee603ff 100644 (file)
@@ -2559,7 +2559,7 @@ if test "$want_shared_libs" = "yes"; then
   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-master/Makefile
 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
index 4e820170e7b1d809ef79b10279590ee1fc5c1d88..378d61433cbca5d80b6c8c7c9ecb4553437d024e 100644 (file)
@@ -7,6 +7,7 @@ LIBDOVECOT_SUBDIRS = \
        lib-charset \
        lib-dns \
        lib-dict \
+       lib-sasl \
        lib-ssl-iostream \
        lib-http \
        lib-fs \
index cd7a1650e22587261e0e16cb30d90034ea986ca4..e79e964a9536df3d540adc0ab989cae92883a953 100644 (file)
@@ -7,6 +7,7 @@ libs = \
        ../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 \
diff --git a/src/lib-sasl/Makefile.am b/src/lib-sasl/Makefile.am
new file mode 100644 (file)
index 0000000..5ca1a0e
--- /dev/null
@@ -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)
diff --git a/src/lib-sasl/mech-login.c b/src/lib-sasl/mech-login.c
new file mode 100644 (file)
index 0000000..e1cbe9b
--- /dev/null
@@ -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
+};
diff --git a/src/lib-sasl/mech-plain.c b/src/lib-sasl/mech-plain.c
new file mode 100644 (file)
index 0000000..ef3d137
--- /dev/null
@@ -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
+};
diff --git a/src/lib-sasl/sasl-client-private.h b/src/lib-sasl/sasl-client-private.h
new file mode 100644 (file)
index 0000000..9c5ae22
--- /dev/null
@@ -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
diff --git a/src/lib-sasl/sasl-client.c b/src/lib-sasl/sasl-client.c
new file mode 100644 (file)
index 0000000..78f0eda
--- /dev/null
@@ -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);
+}
diff --git a/src/lib-sasl/sasl-client.h b/src/lib-sasl/sasl-client.h
new file mode 100644 (file)
index 0000000..dc969ef
--- /dev/null
@@ -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