-#if 0
-/*
- * This function implements SASL authentication for SMTP. If this function
- * completes successfully, then authentication is successful and we've
- * (optionally) 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); \
- } \
- }
-
-static int
-sm_auth_sasl(char *user, int saslssf, char *mechlist, char *inhost)
-{
- int result, status;
- unsigned int buflen, outlen;
- char *buf, *outbuf = NULL, host[NI_MAXHOST];
- const char *chosen_mech;
- sasl_security_properties_t secprops;
- sasl_ssf_t *ssf;
- int *outbufmax;
- struct nmh_creds creds = { 0, 0, 0 };
- size_t outbufsize = 0;
-
- /*
- * Initialize the callback contexts
- */
-
- /*
- * This is a _bit_ of a hack ... but if the hostname wasn't supplied
- * to us on the command line, then call getpeername and do a
- * reverse-address lookup on the IP address to get the name.
- */
-
- memset(host, 0, sizeof(host));
-
- if (!inhost) {
- struct sockaddr_storage sin;
- socklen_t len = sizeof(sin);
- int result;
-
- if (getpeername(fileno(sm_wfp), (struct sockaddr *) &sin, &len) < 0) {
- sm_ierror("getpeername on SMTP socket failed: %s",
- strerror(errno));
- return NOTOK;
- }
-
- result = getnameinfo((struct sockaddr *) &sin, len, host, sizeof(host),
- NULL, 0, NI_NAMEREQD);
- if (result != 0) {
- sm_ierror("Unable to look up name of connected host: %s",
- gai_strerror(result));
- return NOTOK;
- }
- } else {
- strncpy(host, inhost, sizeof(host) - 1);
- }
-
- /* It's OK to copy the addresses here. The callbacks that use
- them will only be called before this function returns. */
- creds.host = host;
- creds.user = user;
- callbacks[SM_SASL_N_CB_USER].context = &creds;
- callbacks[SM_SASL_N_CB_AUTHNAME].context = &creds;
- callbacks[SM_SASL_N_CB_PASS].context = &creds;
-
- result = sasl_client_init(callbacks);
-
- if (result != SASL_OK) {
- sm_ierror("SASL library initialization failed: %s",
- sasl_errstring(result, NULL, NULL));
- return NOTOK;
- }
-
- result = sasl_client_new("smtp", host, NULL, NULL, NULL, 0, &conn);
-
- if (result != SASL_OK) {
- sm_ierror("SASL client initialization failed: %s",
- sasl_errstring(result, NULL, NULL));
- return NOTOK;
- }
-
- /*
- * Initialize the security properties. But if TLS is active, then
- * don't negotiate encryption here.
- */
-
- memset(&secprops, 0, sizeof(secprops));
- secprops.maxbufsize = SASL_MAXRECVBUF;
- secprops.max_ssf =
- tls_active ? 0 : (saslssf != -1 ? (unsigned int) saslssf : UINT_MAX);
-
- result = sasl_setprop(conn, SASL_SEC_PROPS, &secprops);
-
- if (result != SASL_OK) {
- sm_ierror("SASL security property initialization failed: %s",
- sasl_errstring(result, NULL, NULL));
- 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, mechlist, NULL, (const char **) &buf,
- &buflen, (const char **) &chosen_mech);
-
- if (result != SASL_OK && result != SASL_CONTINUE) {
- sm_ierror("SASL client start failed: %s", sasl_errdetail(conn));
- return NOTOK;
- }
-
- /*
- * If we got an initial challenge, send it as part of the AUTH
- * command; otherwise, just send a plain AUTH command.
- */
-
- if (buflen) {
- CHECKB64SIZE(buflen, outbuf, outbufsize);
- status = sasl_encode64(buf, buflen, outbuf, outbufsize, NULL);
- if (status != SASL_OK) {
- sm_ierror("SASL base64 encode failed: %s",
- sasl_errstring(status, NULL, NULL));
- if (outbuf)
- free(outbuf);
- return NOTOK;
- }
-
- status = smtalk(SM_AUTH, "AUTH %s %s", chosen_mech, outbuf);
- } else
- status = smtalk(SM_AUTH, "AUTH %s", chosen_mech);
-
- /*
- * Now we loop until we either fail, get a SASL_OK, or a 235
- * response code. Receive the challenges and process them until
- * we're all done.
- */
-
- while (result == SASL_CONTINUE) {
-
- /*
- * If we get a 235 response, that means authentication has
- * succeeded and we need to break out of the loop (yes, even if
- * we still get SASL_CONTINUE from sasl_client_step()).
- *
- * Otherwise, if we get a message that doesn't seem to be a
- * valid response, then abort
- */
-
- if (status == 235)
- break;
- else if (status < 300 || status > 399) {
- if (outbuf)
- free(outbuf);
- return RP_BHST;
- }
-
- /*
- * Special case; a zero-length response from the SMTP server
- * is returned as a single =. If we get that, then set buflen
- * to be zero. Otherwise, just decode the response.
- */
-
- if (strcmp("=", sm_reply.text) == 0) {
- outlen = 0;
- } else {
- if (sm_reply.length > (int) outbufsize) {
- outbuf = mh_xrealloc(outbuf, outbufsize = sm_reply.length);
- }
-
- result = sasl_decode64(sm_reply.text, sm_reply.length,
- outbuf, outbufsize, &outlen);
- if (result != SASL_OK) {
- smtalk(SM_AUTH, "*");
- sm_ierror("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) {
- smtalk(SM_AUTH, "*");
- sm_ierror("SASL client negotiation failed: %s",
- sasl_errstring(result, NULL, NULL));
- if (outbuf)
- free(outbuf);
- return NOTOK;
- }
-
- CHECKB64SIZE(buflen, outbuf, outbufsize);
- status = sasl_encode64(buf, buflen, outbuf, outbufsize, NULL);
-
- if (status != SASL_OK) {
- smtalk(SM_AUTH, "*");
- sm_ierror("SASL base64 encode failed: %s",
- sasl_errstring(status, NULL, NULL));
- if (outbuf)
- free(outbuf);
- return NOTOK;
- }
-
- status = smtalk(SM_AUTH, outbuf);
- }
-
- if (outbuf)
- free(outbuf);
-
- /*
- * Make sure that we got the correct response
- */
-
- if (status < 200 || status > 299)
- return RP_BHST;
-
- /*
- * We _should_ have completed the authentication successfully.
- * Get a few properties from the authentication exchange.
- */
-
- result = sasl_getprop(conn, SASL_MAXOUTBUF, (const void **) &outbufmax);
-
- if (result != SASL_OK) {
- sm_ierror("Cannot retrieve SASL negotiated output buffer size: %s",
- sasl_errstring(result, NULL, NULL));
- return NOTOK;
- }
-
- maxoutbuf = *outbufmax;
-
- result = sasl_getprop(conn, SASL_SSF, (const void **) &ssf);
-
- sasl_ssf = *ssf;
-
- if (result != SASL_OK) {
- sm_ierror("Cannot retrieve SASL negotiated security strength "
- "factor: %s", sasl_errstring(result, NULL, NULL));
- return NOTOK;
- }
-
- if (sasl_ssf > 0) {
- sasl_outbuffer = malloc(maxoutbuf);
-
- if (sasl_outbuffer == NULL) {
- sm_ierror("Unable to allocate %d bytes for SASL output "
- "buffer", maxoutbuf);
- return NOTOK;
- }
- sasl_outbuflen = 0;
- sasl_inbuflen = 0;
- sasl_inptr = sasl_inbuffer;
- } else {
- sasl_outbuffer = NULL;
- /* Don't NULL out sasl_inbuffer because it could be used in
- sm_fgetc (). */
- }
-
- sasl_complete = 1;
-
- return RP_OK;
-}
-
-/*
- * Our callback functions to feed data to the SASL library
- */
-
-static int
-sm_get_user(void *context, int id, const char **result, unsigned *len)
-{
- nmh_creds_t creds = (nmh_creds_t) context;
-
- if (! result || ((id != SASL_CB_USER) && (id != SASL_CB_AUTHNAME)))
- return SASL_BADPARAM;
-
- if (creds->user == NULL) {
- /*
- * Pass the 1 third argument to nmh_get_credentials() so
- * that a default user if the -user switch to send(1)/post(8)
- * wasn't used, and so that a default password will be supplied.
- * That's used when those values really don't matter, and only
- * with legacy/.netrc, i.e., with a credentials profile entry.
- */
- if (nmh_get_credentials (creds->host, creds->user, 1, creds) != OK) {
- return SASL_BADPARAM;
- }
- }
-
- *result = creds->user;
- if (len)
- *len = strlen(creds->user);
-
- return SASL_OK;
-}
-
-static int
-sm_get_pass(sasl_conn_t *conn, void *context, int id,
- sasl_secret_t **psecret)
-{
- nmh_creds_t creds = (nmh_creds_t) context;
- 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 (creds->host, creds->user, 0, creds) != OK) {
- return SASL_BADPARAM;
- }
- }
-
- len = strlen (creds->password);
-
- if (! (*psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len))) {
- return SASL_NOMEM;
- }
-
- (*psecret)->len = len;
- strcpy((char *) (*psecret)->data, creds->password);
-
- return SASL_OK;
-}
-#endif /* CYRUS_SASL */
-
-/* https://developers.google.com/gmail/xoauth2_protocol */
-static int
-sm_auth_xoauth2(const char *user, const char *oauth_svc, int snoop)
-{
- const char *xoauth_client_res;
- int status;
-
-#if 0
- xoauth_client_res = mh_oauth_do_xoauth(user, oauth_svc,
- snoop ? stderr : NULL);
-
- if (xoauth_client_res == NULL) {
- return sm_ierror("Internal error: mh_oauth_do_xoauth() returned NULL");
- }
-#else
- NMH_UNUSED(user);
- NMH_UNUSED(snoop);
- adios(NULL, "sendfrom built without OAUTH_SUPPORT, "
- "so oauth_svc %s is not supported", oauth_svc);
-#endif /* OAUTH_SUPPORT */
-
- status = smtalk(SM_AUTH, "AUTH XOAUTH2 %s", xoauth_client_res);
- if (status == 235) {
- /* It worked! */
- return RP_OK;
- }
-
- /*
- * Status is 334 and sm_reply.text contains base64-encoded JSON. As far as
- * epg can tell, no matter the error, the JSON is always the same:
- * {"status":"400","schemes":"Bearer","scope":"https://mail.google.com/"}
- * I tried these errors:
- * - garbage token
- * - expired token
- * - wrong scope
- * - wrong username
- */
- /* Then we're supposed to send an empty response ("\r\n"). */
- smtalk(SM_AUTH, "");
- /*
- * And now we always get this, again, no matter the error:
- * 535-5.7.8 Username and Password not accepted. Learn more at
- * 535 5.7.8 http://support.google.com/mail/bin/answer.py?answer=14257
- */
- return RP_BHST;
-}