]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-sasl: Add OAUTHBEARER and XOAUTH2 mechanism
authorAki Tuomi <aki.tuomi@dovecot.fi>
Wed, 18 Jan 2017 11:46:12 +0000 (13:46 +0200)
committerGitLab <gitlab@git.dovecot.net>
Fri, 20 Jan 2017 22:07:24 +0000 (00:07 +0200)
src/lib-sasl/Makefile.am
src/lib-sasl/dsasl-client-private.h
src/lib-sasl/dsasl-client.c
src/lib-sasl/mech-oauthbearer.c [new file with mode: 0644]

index dd6d37377b53f9e847f46964d37f538e405a1fff..ac428a1a54776e50c916488a44aff9e30c2268f8 100644 (file)
@@ -7,6 +7,7 @@ libsasl_la_SOURCES = \
        mech-external.c \
        mech-login.c \
        mech-plain.c \
+       mech-oauthbearer.c \
        dsasl-client.c 
 
 headers = \
index 7f0d29caedb10074d98f44b17c2bff99d9c2a71a..3b0c202b1cd20a06dd65ee38e381d44ea32bca07 100644 (file)
@@ -31,6 +31,8 @@ struct dsasl_client_mech {
 
 extern const struct dsasl_client_mech dsasl_client_mech_external;
 extern const struct dsasl_client_mech dsasl_client_mech_login;
+extern const struct dsasl_client_mech dsasl_client_mech_oauthbearer;
+extern const struct dsasl_client_mech dsasl_client_mech_xoauth2;
 
 void dsasl_client_mech_register(const struct dsasl_client_mech *mech);
 void dsasl_client_mech_unregister(const struct dsasl_client_mech *mech);
index 055b63ac8d7f8b9d99182474b7d961bff2935fbc..5b000dc619710f79125ba812e1ce79771a2979f2 100644 (file)
@@ -118,6 +118,7 @@ int dsasl_client_get_result(struct dsasl_client *client,
                        client->mech->get_result(client, key, value_r, error_r);
                i_assert(ret <= 0 || *value_r != NULL);
                i_assert(ret >= 0 || *error_r != NULL);
+               return ret;
        } else
                return 0;
 }
@@ -131,6 +132,8 @@ void dsasl_clients_init(void)
        dsasl_client_mech_register(&dsasl_client_mech_external);
        dsasl_client_mech_register(&dsasl_client_mech_plain);
        dsasl_client_mech_register(&dsasl_client_mech_login);
+       dsasl_client_mech_register(&dsasl_client_mech_oauthbearer);
+       dsasl_client_mech_register(&dsasl_client_mech_xoauth2);
 }
 
 void dsasl_clients_deinit(void)
diff --git a/src/lib-sasl/mech-oauthbearer.c b/src/lib-sasl/mech-oauthbearer.c
new file mode 100644 (file)
index 0000000..72dfe2b
--- /dev/null
@@ -0,0 +1,199 @@
+/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "net.h"
+#include "json-parser.h"
+#include "istream.h"
+#include "dsasl-client-private.h"
+
+struct oauthbearer_dsasl_client {
+       struct dsasl_client client;
+       const char *host;
+       const char *status;
+       in_port_t port;
+       bool output_sent;
+};
+
+static int
+mech_oauthbearer_input(struct dsasl_client *_client,
+                const unsigned char *input, size_t input_len,
+                const char **error_r)
+{
+       struct oauthbearer_dsasl_client *client =
+               (struct oauthbearer_dsasl_client *)_client;
+
+       if (!client->output_sent) {
+               if (input_len > 0) {
+                       *error_r = "Server sent non-empty initial response";
+                       return -1;
+               }
+       } else {
+               client->status = "";
+               /* if response is empty, authentication has *SUCCEEDED* */
+               if (input_len == 0)
+                       return 0;
+
+               /* authentication has failed, try parse status.
+                  we are only interested in extracting status if possible
+                  so we don't really need to much error handling. */
+               struct istream *is = i_stream_create_from_data(input, input_len);
+               const char *status = NULL, *value;
+               const char *error = NULL;
+               enum json_type jtype;
+               bool found_status = FALSE;
+               struct json_parser *parser = json_parser_init(is);
+               while (json_parse_next(parser, &jtype, &value)>0) {
+                       if (found_status && status == NULL) {
+                               if (jtype == JSON_TYPE_STRING ||
+                                   jtype == JSON_TYPE_NUMBER)
+                                       status = t_strdup(value);
+                               break;
+                       } else if (jtype == JSON_TYPE_OBJECT_KEY &&
+                                  strcmp(value, "status") == 0) {
+                               found_status = TRUE;
+                       } else json_parse_skip_next(parser);
+               }
+
+               /* deinitialize json parser */
+               int ret = json_parser_deinit(&parser, &error);
+
+               if (status != NULL)
+                       client->status = p_strdup(_client->pool, status);
+               else {
+                       ret = -1;
+                       if (error == NULL)
+                               error = "Status value missing";
+               }
+               if (ret < 0)
+                       *error_r = t_strdup_printf("Error parsing JSON reply: %s",
+                                                  error);
+               else
+                       *error_r = t_strdup_printf("Failed to authenticate: %s",
+                                                  client->status);
+               return -1;
+       }
+       return 0;
+}
+
+static int
+mech_oauthbearer_output(struct dsasl_client *_client,
+                 const unsigned char **output_r, size_t *output_len_r,
+                 const char **error_r)
+{
+       struct oauthbearer_dsasl_client *client =
+               (struct oauthbearer_dsasl_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);
+
+       str_printfa(str, "n,a=%s,\x01", _client->set.authid);
+       if (client->host != NULL && *client->host != '\0')
+               str_printfa(str, "host=%s\x01", client->host);
+       if (client->port > 0)
+               str_printfa(str, "port=%u\x01", client->port);
+       str_printfa(str, "auth=Bearer %s\x01", _client->password);
+       str_append_c(str, '\x01');
+
+       *output_r = str_data(str);
+       *output_len_r = str_len(str);
+       client->output_sent = TRUE;
+       return 0;
+}
+
+static int
+mech_xoauth2_output(struct dsasl_client *_client,
+                   const unsigned char **output_r, size_t *output_len_r,
+                   const char **error_r)
+{
+       struct oauthbearer_dsasl_client *client =
+               (struct oauthbearer_dsasl_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);
+
+       str_printfa(str, "%s\x01", _client->set.authid);
+       str_printfa(str, "auth=Bearer %s\x01", _client->password);
+       str_append_c(str, '\x01');
+
+       *output_r = str_data(str);
+       *output_len_r = str_len(str);
+       client->output_sent = TRUE;
+       return 0;
+}
+
+static int
+mech_oauthbearer_set_parameter(struct dsasl_client *_client, const char *key,
+                              const char *value, const char **error_r)
+{
+       struct oauthbearer_dsasl_client *client =
+               (struct oauthbearer_dsasl_client *)_client;
+       if (strcmp(key, "host") == 0) {
+               if (value != NULL)
+                       client->host = p_strdup(_client->pool, value);
+               else
+                       client->host = NULL;
+               return 1;
+       } else if (strcmp(key, "port") == 0) {
+               if (value == NULL) {
+                       client->port = 0;
+               } else if (net_str2port(key, &client->port) < 0) {
+                       *error_r = "Invalid port value";
+                       return -1;
+               }
+               return 1;
+       }
+       return 0;
+}
+
+static int
+mech_oauthbearer_get_result(struct dsasl_client *_client, const char *key,
+                           const char **value_r, const char **error_r ATTR_UNUSED)
+{
+       struct oauthbearer_dsasl_client *client =
+               (struct oauthbearer_dsasl_client *)_client;
+       if (strcmp(key, "status") == 0) {
+               /* this is set to value after login attempt */
+               i_assert(client->status != NULL);
+               *value_r = client->status;
+               return 1;
+       }
+       return 0;
+}
+
+const struct dsasl_client_mech dsasl_client_mech_oauthbearer = {
+       .name = "OAUTHBEARER",
+       .struct_size = sizeof(struct oauthbearer_dsasl_client),
+
+       .input = mech_oauthbearer_input,
+       .output = mech_oauthbearer_output,
+       .set_parameter = mech_oauthbearer_set_parameter,
+       .get_result = mech_oauthbearer_get_result,
+};
+
+const struct dsasl_client_mech dsasl_client_mech_xoauth2 = {
+       .name = "XOAUTH2",
+       .struct_size = sizeof(struct oauthbearer_dsasl_client),
+
+       .output = mech_xoauth2_output,
+       .set_parameter = mech_oauthbearer_set_parameter,
+       .get_result = mech_oauthbearer_get_result,
+};