Show 24 quoted lines
>
>+static char *oauthbearer_base64(const char *user, const char *access_token)
>+{
>+ int raw_len, b64_len;
>+ char *raw, *b64;
>+
>+ /*
>+ * Compose the OAUTHBEARER string
>+ *
>+ * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
>+ *
>+ * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
>+ * * gs2-cb-flag `n` -> client does not support CB
>+ * * gs2-authzid `a=" {User} "`
>+ *
>+ * The second part are key value pairs containing host, port and auth as
>+ * described in RFC7628.
>+ *
>+ * https://datatracker.ietf.org/doc/html/rfc5801
>+ * https://datatracker.ietf.org/doc/html/rfc7628
>+ */
>+ raw_len = strlen(user) + strlen(access_token) + 20;
>+ raw = xmallocz(raw_len + 1);
>+ snprintf(raw, raw_len + 1, "n,a=%s,\001auth=Bearer %s\001\001", user, access_token);Show 153 quoted lines
>+
>+ /* Base64 encode */
>+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
>+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
>+ free(raw);
>+
>+ if (b64_len < 0) {
>+ free(b64);
>+ return NULL;
>+ }
>+ return b64;
>+}
>+
>+static char *xoauth2_base64(const char *user, const char *access_token)
>+{
>+ int raw_len, b64_len;
>+ char *raw, *b64;
>+
>+ /*
>+ * Compose the XOAUTH2 string
>+ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
>+ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
>+ */
>+ raw_len = strlen(user) + strlen(access_token) + 20;
>+ raw = xmallocz(raw_len + 1);
>+ snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
>+
>+ /* Base64 encode */
>+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
>+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
>+ free(raw);
>+
>+ if (b64_len < 0) {
>+ free(b64);
>+ return NULL;
>+ }
>+ return b64;
>+}
>+
> static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
> {
> int ret;
>@@ -903,9 +969,51 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
> return 0;
> }
>
>+static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
>+{
>+ int ret;
>+ char *b64;
>+
>+ b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass);
>+ if (!b64)
>+ return error("OAUTHBEARER: base64 encoding failed");
>+
>+ /* Send the base64-encoded response */
>+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
>+ if (ret != (int)strlen(b64)) {
>+ free(b64);
>+ return error("IMAP error: sending OAUTHBEARER response failed");
>+ }
>+
>+ free(b64);
>+ return 0;
>+}
>+
>+static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
>+{
>+ int ret;
>+ char *b64;
>+
>+ b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass);
>+ if (!b64)
>+ return error("XOAUTH2: base64 encoding failed");
>+
>+ /* Send the base64-encoded response */
>+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
>+ if (ret != (int)strlen(b64)) {
>+ free(b64);
>+ return error("IMAP error: sending XOAUTH2 response failed");
>+ }
>+
>+ free(b64);
>+ return 0;
>+}
>+
> #else
>
> #define auth_cram_md5 NULL
>+#define auth_oauthbearer NULL
>+#define auth_xoauth2 NULL
>
> #endif
>
>@@ -1118,6 +1226,12 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
> if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
> if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
> goto bail;
>+ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
>+ if (try_auth_method(srvc, ctx, imap, "OAUTHBEARER", AUTH_OAUTHBEARER, auth_oauthbearer))
>+ goto bail;
>+ } else if (!strcmp(srvc->auth_method, "XOAUTH2")) {
>+ if (try_auth_method(srvc, ctx, imap, "XOAUTH2", AUTH_XOAUTH2, auth_xoauth2))
>+ goto bail;
> } else {
> fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
> goto bail;
>@@ -1419,7 +1533,16 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
>
> server_fill_credential(srvc, cred);
> curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
>- curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
>+
>+ /*
>+ * Use CURLOPT_PASSWORD irrespective of whether there is
>+ * an auth method specified or not, unless it's OAuth2.0,
>+ * where we use CURLOPT_XOAUTH2_BEARER.
>+ */
>+ if (!srvc->auth_method ||
>+ (strcmp(srvc->auth_method, "XOAUTH2") &&
>+ strcmp(srvc->auth_method, "OAUTHBEARER")))
>+ curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
>
> strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
> strbuf_addstr(&path, srvc->host);
>@@ -1437,11 +1560,22 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
> curl_easy_setopt(curl, CURLOPT_PORT, (long)srvc->port);
>
> if (srvc->auth_method) {
>- struct strbuf auth = STRBUF_INIT;
>- strbuf_addstr(&auth, "AUTH=");
>- strbuf_addstr(&auth, srvc->auth_method);
>- curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
>- strbuf_release(&auth);
>+ if (!strcmp(srvc->auth_method, "XOAUTH2") ||
>+ !strcmp(srvc->auth_method, "OAUTHBEARER")) {
>+
>+ /*
>+ * While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2,
>+ * upon debugging, it has been found that it is capable of detecting
>+ * the best option out of OAUTHBEARER and XOAUTH2.
>+ */
>+ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, srvc->pass);
>+ } else {
>+ struct strbuf auth = STRBUF_INIT;
>+ strbuf_addstr(&auth, "AUTH=");
>+ strbuf_addstr(&auth, srvc->auth_method);
>+ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
>+ strbuf_release(&auth);
>+ }
> }
>
> if (!srvc->use_ssl)