+if (tls_in.cipher)
+ {
+ if ((rc = sasl_setprop(conn, SASL_SSF_EXTERNAL, (sasl_ssf_t *) &tls_in.bits)) != SASL_OK)
+ {
+ HDEBUG(D_auth) debug_printf("Cyrus SASL EXTERNAL SSF set %d failed: %s\n",
+ tls_in.bits, sasl_errstring(rc, NULL, NULL));
+ auth_defer_msg = US"couldn't set Cyrus SASL EXTERNAL SSF";
+ sasl_done();
+ return DEFER;
+ }
+ else
+ HDEBUG(D_auth) debug_printf("Cyrus SASL set EXTERNAL SSF to %d\n", tls_in.bits);
+
+ /*XXX Set channel-binding here with sasl_channel_binding_t / SASL_CHANNEL_BINDING
+ Unclear what the "name" element does though, ditto the "critical" flag. */
+ }
+else
+ HDEBUG(D_auth) debug_printf("Cyrus SASL: no TLS, no EXTERNAL SSF set\n");
+
+/* So sasl_setprop() documents non-shorted IPv6 addresses which is incredibly
+annoying; looking at cyrus-imapd-2.3.x source, the IP address is constructed
+with their iptostring() function, which just wraps
+getnameinfo(..., NI_NUMERICHOST|NI_NUMERICSERV), which is equivalent to the
+inet_ntop which we wrap in our host_ntoa() function.
+
+So the docs are too strict and we shouldn't worry about :: contractions. */
+
+/* Set properties for remote and local host-ip;port */
+for (int i = 0; i < 2; ++i)
+ {
+ int propnum;
+ const uschar * label;
+ uschar * address_port;
+ const char *s_err;
+
+ if (i)
+ {
+ propnum = SASL_IPREMOTEPORT;
+ label = CUS"peer";
+ address_port = string_sprintf("%s;%d",
+ sender_host_address, sender_host_port);
+ }
+ else
+ {
+ propnum = SASL_IPLOCALPORT;
+ label = CUS"local";
+ address_port = string_sprintf("%s;%d", interface_address, interface_port);
+ }
+
+ if ((rc = sasl_setprop(conn, propnum, address_port)) != SASL_OK)
+ {
+ HDEBUG(D_auth)
+ {
+ s_err = sasl_errdetail(conn);
+ debug_printf("Failed to set %s SASL property: [%d] %s\n",
+ label, rc, s_err ? s_err : "<unknown reason>");
+ }
+ break;
+ }
+ HDEBUG(D_auth) debug_printf("Cyrus SASL set %s hostport to: %s\n",
+ label, address_port);
+ }