- int result, status;
- unsigned int buflen, outlen;
- char *buf, outbuf[BUFSIZ], 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 };
-
- /*
- * 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) {
- status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL);
- if (status != SASL_OK) {
- sm_ierror("SASL base64 encode failed: %s",
- sasl_errstring(status, NULL, NULL));
- 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)
- 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 {
- result = sasl_decode64(sm_reply.text, sm_reply.length,
- outbuf, sizeof(outbuf), &outlen);
- if (result != SASL_OK) {
- smtalk(SM_AUTH, "*");
- sm_ierror("SASL base64 decode failed: %s",
- sasl_errstring(result, NULL, NULL));
- 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));
- return NOTOK;
- }
-
- status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL);
-
- if (status != SASL_OK) {
- smtalk(SM_AUTH, "*");
- sm_ierror("SASL base64 encode failed: %s",
- sasl_errstring(status, NULL, NULL));
- return NOTOK;
- }
-
- status = smtalk(SM_AUTH, 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);