+ int result, status, sasl_capability = 0;
+ unsigned int buflen, outlen;
+ char server_mechs[256], *buf, outbuf[BUFSIZ];
+ const char *chosen_mech;
+ sasl_security_properties_t secprops;
+ struct pass_context p_context;
+ sasl_ssf_t *ssf;
+ int *moutbuf;
+
+ /*
+ * First off, we're going to send the CAPA command to see if we can
+ * even support the AUTH command, and if we do, then we'll get a
+ * list of mechanisms the server supports. If we don't support
+ * the CAPA command, then it's unlikely that we will support
+ * SASL
+ */
+
+ if (command("CAPA") == NOTOK) {
+ snprintf(response, sizeof(response),
+ "The POP CAPA command failed; POP server does not "
+ "support SASL");
+ return NOTOK;
+ }
+
+ while ((status = multiline()) != DONE)
+ switch (status) {
+ case NOTOK:
+ return NOTOK;
+ break;
+ case DONE: /* Shouldn't be possible, but just in case */
+ break;
+ case OK:
+ if (strncasecmp(response, "SASL ", 5) == 0) {
+ /*
+ * We've seen the SASL capability. Grab the mech list
+ */
+ sasl_capability++;
+ strncpy(server_mechs, response + 5, sizeof(server_mechs));
+ }
+ break;
+ }
+
+ if (!sasl_capability) {
+ snprintf(response, sizeof(response), "POP server does not support "
+ "SASL");
+ return NOTOK;
+ }
+
+ /*
+ * If we received a preferred mechanism, see if the server supports it.
+ */
+
+ if (mech && stringdex(mech, server_mechs) == -1) {
+ snprintf(response, sizeof(response), "Requested SASL mech \"%s\" is "
+ "not in list of supported mechanisms:\n%s",
+ mech, server_mechs);
+ return NOTOK;
+ }
+
+ /*
+ * 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 = 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) {
+ status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL);
+ if (status != SASL_OK) {
+ snprintf(response, sizeof(response), "SASL base64 encode "
+ "failed: %s", sasl_errstring(status, NULL, NULL));
+ return NOTOK;
+ }
+
+ status = command("AUTH %s %s", chosen_mech, outbuf);
+ } else
+ status = command("AUTH %s", chosen_mech);
+
+ while (result == SASL_CONTINUE) {
+ if (status == NOTOK)
+ 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");
+ return NOTOK;
+ }
+
+ result = sasl_decode64(response + 2, strlen(response + 2),
+ outbuf, sizeof(outbuf), &outlen);
+
+ if (result != SASL_OK) {
+ command("*");
+ snprintf(response, sizeof(response), "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) {
+ command("*");
+ snprintf(response, sizeof(response), "SASL client negotiaton "
+ "failed: %s", sasl_errdetail(conn));
+ return NOTOK;
+ }
+
+ status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL);
+
+ if (status != SASL_OK) {
+ command("*");
+ snprintf(response, sizeof(response), "SASL base64 encode "
+ "failed: %s", sasl_errstring(status, NULL, NULL));
+ return NOTOK;
+ }
+
+ status = command(outbuf);