-#ifdef CYRUS_SASL
-/*
- * This function implements the AUTH command for various SASL mechanisms
- *
- * We do the whole SASL dialog here. If this completes, then we've
- * authenticated successfully and have (possibly) negotiated a security
- * layer.
- */
-
-#define CHECKB64SIZE(insize, outbuf, outsize) \
- { size_t wantout = (((insize + 2) / 3) * 4) + 32; \
- if (wantout > outsize) { \
- outbuf = mh_xrealloc(outbuf, outsize = wantout); \
- } \
- }
-
-int
-pop_auth_sasl(char *user, char *host, char *mech)
-{
- int result, status;
- unsigned int buflen, outlen;
- char server_mechs[256], *buf, *outbuf = NULL;
- size_t outbufsize = 0;
- const char *chosen_mech;
- sasl_security_properties_t secprops;
- struct pass_context p_context;
- sasl_ssf_t *ssf;
- int *moutbuf;
-
- if ((status = check_mech(server_mechs, sizeof(server_mechs), mech)) != OK) {
- return status;
- }
-
- /*
- * Start the SASL process. First off, initialize the SASL library.
- */
-
- callbacks[POP_SASL_CB_N_USER].context = user;
- p_context.user = user;
- p_context.host = host;
- callbacks[POP_SASL_CB_N_PASS].context = &p_context;
-
- result = sasl_client_init(callbacks);
-
- if (result != SASL_OK) {
- snprintf(response, sizeof(response), "SASL library initialization "
- "failed: %s", sasl_errstring(result, NULL, NULL));
- return NOTOK;
- }
-
- result = sasl_client_new("pop", host, NULL, NULL, NULL, 0, &conn);
-
- if (result != SASL_OK) {
- snprintf(response, sizeof(response), "SASL client initialization "
- "failed: %s", sasl_errstring(result, NULL, NULL));
- return NOTOK;
- }
-
- /*
- * Initialize the security properties
- */
-
- memset(&secprops, 0, sizeof(secprops));
- secprops.maxbufsize = SASL_BUFFER_SIZE;
- secprops.max_ssf = tls_active ? 0 : UINT_MAX;
-
- result = sasl_setprop(conn, SASL_SEC_PROPS, &secprops);
-
- if (result != SASL_OK) {
- snprintf(response, sizeof(response), "SASL security property "
- "initialization failed: %s", sasl_errdetail(conn));
- return NOTOK;
- }
-
- /*
- * Start the actual protocol. Feed the mech list into the library
- * and get out a possible initial challenge
- */
-
- result = sasl_client_start(conn,
- (const char *) (mech ? mech : server_mechs),
- NULL, (const char **) &buf,
- &buflen, &chosen_mech);
-
- if (result != SASL_OK && result != SASL_CONTINUE) {
- snprintf(response, sizeof(response), "SASL client start failed: %s",
- sasl_errdetail(conn));
- return NOTOK;
- }
-
- if (buflen) {
- CHECKB64SIZE(buflen, outbuf, outbufsize);
- status = sasl_encode64(buf, buflen, outbuf, outbufsize, NULL);
- if (status != SASL_OK) {
- snprintf(response, sizeof(response), "SASL base64 encode "
- "failed: %s", sasl_errstring(status, NULL, NULL));
- if (outbuf)
- free(outbuf);
- return NOTOK;
- }
-
- status = command("AUTH %s %s", chosen_mech, outbuf);
- } else
- status = command("AUTH %s", chosen_mech);
-
- while (result == SASL_CONTINUE) {
- size_t inlen;
-
- if (status == NOTOK) {
- if (outbuf)
- free(outbuf);
- return NOTOK;
- }
-
- /*
- * If we get a "+OK" prefix to our response, then we should
- * exit out of this exchange now (because authenticated should
- * have succeeded)
- */
-
- if (strncmp(response, "+OK", 3) == 0)
- break;
-
- /*
- * Otherwise, make sure the server challenge is correctly formatted
- */
-
- if (strncmp(response, "+ ", 2) != 0) {
- command("*");
- snprintf(response, sizeof(response),
- "Malformed authentication message from server");
- if (outbuf)
- free(outbuf);
- return NOTOK;
- }
-
- /*
- * For decode, it will always be shorter, so just make sure
- * that outbuf is as at least as big as the encoded response.
- */
-
- inlen = strlen(response + 2);
-
- if (inlen > outbufsize) {
- outbuf = mh_xrealloc(outbuf, outbufsize = inlen);
- }
-
- result = sasl_decode64(response + 2, strlen(response + 2),
- outbuf, outbufsize, &outlen);
-
- if (result != SASL_OK) {
- command("*");
- snprintf(response, sizeof(response), "SASL base64 decode "
- "failed: %s", sasl_errstring(result, NULL, NULL));
- if (outbuf)
- free(outbuf);
- return NOTOK;
- }
-
- result = sasl_client_step(conn, outbuf, outlen, NULL,
- (const char **) &buf, &buflen);
-
- if (result != SASL_OK && result != SASL_CONTINUE) {
- command("*");
- snprintf(response, sizeof(response), "SASL client negotiaton "
- "failed: %s", sasl_errdetail(conn));
- if (outbuf)
- free(outbuf);
- return NOTOK;
- }
-
- CHECKB64SIZE(buflen, outbuf, outbufsize);
-
- status = sasl_encode64(buf, buflen, outbuf, outbufsize, NULL);
-
- if (status != SASL_OK) {
- command("*");
- snprintf(response, sizeof(response), "SASL base64 encode "
- "failed: %s", sasl_errstring(status, NULL, NULL));
- if (outbuf)
- free(outbuf);
- return NOTOK;
- }
-
- status = command(outbuf);
- }
-
- if (outbuf)
- free(outbuf);
-
- /*
- * If we didn't get a positive final response, then error out
- * (that probably means we failed an authorization check).
- */
-
- if (status != OK)
- return NOTOK;
-
- /*
- * We _should_ be okay now. Get a few properties now that negotiation
- * has completed.
- */
-
- result = sasl_getprop(conn, SASL_MAXOUTBUF, (const void **) &moutbuf);
-
- if (result != SASL_OK) {
- snprintf(response, sizeof(response), "Cannot retrieve SASL negotiated "
- "output buffer size: %s", sasl_errdetail(conn));
- return NOTOK;
- }
-
- maxoutbuf = *moutbuf;
-
- result = sasl_getprop(conn, SASL_SSF, (const void **) &ssf);
-
- sasl_ssf = *ssf;
-
- if (result != SASL_OK) {
- snprintf(response, sizeof(response), "Cannot retrieve SASL negotiated "
- "security strength factor: %s", sasl_errdetail(conn));
- return NOTOK;
- }
-
- /*
- * Limit this to what we can deal with. Shouldn't matter much because
- * this is only outgoing data (which should be small)
- */
-
- if (maxoutbuf == 0 || maxoutbuf > BUFSIZ)
- maxoutbuf = BUFSIZ;
-
- sasl_complete = 1;
-
- return status;
-}
-
-/*
- * Callback to return the userid sent down via the user parameter
- */
-
-static int
-sasl_get_user(void *context, int id, const char **result, unsigned *len)
-{
- char *user = (char *) context;
-
- if (! result || id != SASL_CB_USER)
- return SASL_BADPARAM;
-
- *result = user;
- if (len)
- *len = strlen(user);
-
- return SASL_OK;
-}
-
-/*
- * Callback to return the password (we call ruserpass, which can get it
- * out of the .netrc
- */
-
-static int
-sasl_get_pass(sasl_conn_t *conn, void *context, int id, sasl_secret_t **psecret)
-{
- struct pass_context *p_context = (struct pass_context *) context;
- struct nmh_creds creds = { 0, 0, 0 };
- int len;
-
- NMH_UNUSED (conn);
-
- if (! psecret || id != SASL_CB_PASS)
- return SASL_BADPARAM;
-
- if (creds.password == NULL) {
- /*
- * Pass the 0 third argument to nmh_get_credentials() so
- * that the default password isn't used. With legacy/.netrc
- * credentials support, we'll only get here if the -user
- * switch to send(1)/post(8) wasn't used.
- */
- if (nmh_get_credentials (p_context->host, p_context->user, 0, &creds)
- != OK) {
- return SASL_BADPARAM;
- }
- }
-
- len = strlen (creds.password);
-
- *psecret = (sasl_secret_t *) mh_xmalloc(sizeof(sasl_secret_t) + len);
-
- (*psecret)->len = len;
- strcpy((char *) (*psecret)->data, creds.password);
-
- return SASL_OK;
-}
-#endif /* CYRUS_SASL */
-
-int
-pop_auth_xoauth(const char *client_res)
-{
- char server_mechs[256];
- int status = check_mech(server_mechs, sizeof(server_mechs), "XOAUTH");
-
- if (status != OK) return status;
-
- if ((status = command("AUTH XOAUTH2 %s", client_res)) != OK) {
- return status;
- }
- if (strncmp(response, "+OK", 3) == 0) {
- return OK;
- }
-
- /* response contains base64-encoded JSON, which is always the same.
- * See mts/smtp/smtp.c for more notes on that. */
- /* Then we're supposed to send an empty response ("\r\n"). */
- return command("");
-}
-