+/*
+ * Set various SASL protocol parameters
+ */
+
+int
+netsec_set_sasl_params(netsec_context *nsc, const char *hostname,
+ const char *service, const char *mechanism,
+ netsec_sasl_callback callback, char **errstr)
+{
+#ifdef CYRUS_SASL
+ sasl_callback_t *sasl_cbs;
+ int retval;
+
+ if (! sasl_initialized) {
+ retval = sasl_client_init(NULL);
+ if (retval != SASL_OK) {
+ *errstr = netsec_errstr("SASL client initialization failed: %s",
+ sasl_errstring(retval, NULL, NULL));
+ return NOTOK;
+ }
+ sasl_initialized++;
+ }
+
+ /*
+ * Allocate an array of SASL callbacks for this connection.
+ * Right now we just allocate an array of four callbacks.
+ */
+
+ sasl_cbs = mh_xmalloc(sizeof(*sasl_cbs) * 4);
+
+ sasl_cbs[0].id = SASL_CB_USER;
+ sasl_cbs[0].proc = (sasl_callback_ft) netsec_get_user;
+ sasl_cbs[0].context = nsc;
+
+ sasl_cbs[1].id = SASL_CB_AUTHNAME;
+ sasl_cbs[1].proc = (sasl_callback_ft) netsec_get_user;
+ sasl_cbs[1].context = nsc;
+
+ sasl_cbs[2].id = SASL_CB_PASS;
+ sasl_cbs[2].proc = (sasl_callback_ft) netsec_get_password;
+ sasl_cbs[2].context = nsc;
+
+ sasl_cbs[3].id = SASL_CB_LIST_END;
+ sasl_cbs[3].proc = NULL;
+ sasl_cbs[3].context = NULL;
+
+ nsc->sasl_cbs = sasl_cbs;
+
+ retval = sasl_client_new(service, hostname, NULL, NULL, nsc->sasl_cbs, 0,
+ &nsc->sasl_conn);
+
+ if (retval) {
+ *errstr = netsec_errstr("SASL new client allocation failed: %s",
+ sasl_errstring(retval, NULL, NULL));
+ return NOTOK;
+ }
+
+ nsc->sasl_mech = mechanism ? getcpy(mechanism) : NULL;
+ nsc->sasl_proto_cb = callback;
+
+ return OK;
+#else /* CYRUS_SASL */
+ *errstr = netsec_errstr("SASL is not supported");
+
+ return NOTOK;
+#endif /* CYRUS_SASL */
+}
+
+#ifdef CYRUS_SASL
+/*
+ * Our userid callback; return the specified username to the SASL
+ * library when asked.
+ */
+
+int netsec_get_user(void *context, int id, const char **result,
+ unsigned int *len)
+{
+ netsec_context *nsc = (netsec_context *) context;
+
+ if (! result || (id != SASL_CB_USER && id != SASL_CB_AUTHNAME))
+ return SASL_BADPARAM;
+
+ if (nsc->ns_userid == NULL) {
+ /*
+ * Pass the 1 third argument to nmh_get_credentials() so that
+ * a defauly user if the -user switch wasn't supplied, 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 (nsc->sasl_creds == NULL) {
+ nsc->sasl_creds = mh_xmalloc(sizeof(*nsc->sasl_creds));
+ nsc->sasl_creds->user = NULL;
+ nsc->sasl_creds->password = NULL;
+ }
+
+ if (nmh_get_credentials(nsc->ns_hostname, nsc->ns_userid, 1,
+ nsc->sasl_creds) != OK)
+ return SASL_BADPARAM;
+
+ if (nsc->ns_userid != nsc->sasl_creds->user) {
+ if (nsc->ns_userid)
+ free(nsc->ns_userid);
+ nsc->ns_userid = getcpy(nsc->sasl_creds->user);
+ }
+ }
+
+ *result = nsc->ns_userid;
+ if (len)
+ *len = strlen(nsc->ns_userid);
+
+ return SASL_OK;
+}
+
+/*
+ * Retrieve a password and return it to SASL
+ */
+
+static int
+netsec_get_password(sasl_conn_t *conn, void *context, int id,
+ sasl_secret_t **psecret)
+{
+ netsec_context *nsc = (netsec_context *) context;
+ int len;
+
+ NMH_UNUSED(conn);
+
+ if (! psecret || id != SASL_CB_PASS)
+ return SASL_BADPARAM;
+
+ if (nsc->sasl_creds == NULL) {
+ nsc->sasl_creds = mh_xmalloc(sizeof(*nsc->sasl_creds));
+ nsc->sasl_creds->user = NULL;
+ nsc->sasl_creds->password = NULL;
+ }
+
+ if (nsc->sasl_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(nsc->ns_hostname, nsc->ns_userid, 0,
+ nsc->sasl_creds) != OK) {
+ return SASL_BADPARAM;
+ }
+ }
+
+ len = strlen(nsc->sasl_creds->password);
+
+ /*
+ * sasl_secret_t includes 1 bytes for "data" already, so that leaves
+ * us room for a terminating NUL
+ */
+
+ *psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len);
+
+ if (! *psecret)
+ return SASL_NOMEM;
+
+ (*psecret)->len = len;
+ strcpy((char *) (*psecret)->data, nsc->sasl_creds->password);
+
+ nsc->sasl_secret = *psecret;
+
+ return SASL_OK;
+}
+#endif /* CYRUS_SASL */
+
+/*
+ * Negotiate SASL on this connection
+ */
+
+int
+netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr)
+{
+#ifdef CYRUS_SASL
+ sasl_security_properties_t secprops;
+ char *chosen_mech;
+ sasl_ssf_t *ssf;
+ int rc;
+
+ return OK;
+#else
+ *errstr = netsec_errstr("SASL not supported");
+
+ return NOTOK;
+#endif /* CYRUS_SASL */
+}
+
+/*
+ * Retrieve our chosen SASL mechanism
+ */
+
+char *
+netsec_get_sasl_mechanism(netsec_context *nsc)
+{
+#ifdef CYRUS_SASL
+ return nsc->sasl_chosen_mech;
+#else /* CYRUS_SASL */
+ return NULL;
+#endif /* CYRUS_SASL */
+}
+