Re: [PATCH v18 04/10] imap-send: add support for OAuth2.0 authentication

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



I'm not really on the list at the moment but I saw this was slated for next in what's cooking. Apologies if the formatting is off, I'm in my phone. 

On 9 June 2025 21:22:49 BST, Aditya Garg <gargaditya08@xxxxxxxx> wrote:
> 
>+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);

This looks very fragile. It would be safer to use an strbuf or if there are no embedded nul bytes xstrfmt() and strlen(). This applies to the next patch as well and any others that are building strings with snprintf() or memcpy(). 

Also the comment above mentions the host and port but I don't see them here.

Thanks

Phillip

>+
>+	/* 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)





[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux