static JabberSaslState jabber_cyrus_handle_challenge(JabberStream *js, xmlnode *packet, xmlnode **reply, char **error) { char *enc_in = xmlnode_get_data(packet); unsigned char *dec_in; char *enc_out; const char *c_out; unsigned int clen; gsize declen; dec_in = purple_base64_decode(enc_in, &declen); js->sasl_state = sasl_client_step(js->sasl, (char*)dec_in, declen, NULL, &c_out, &clen); g_free(enc_in); g_free(dec_in); if (js->sasl_state != SASL_CONTINUE && js->sasl_state != SASL_OK) { gchar *tmp = g_strdup_printf(_("SASL error: %s"), sasl_errdetail(js->sasl)); purple_debug_error("jabber", "Error is %d : %s\n", js->sasl_state, sasl_errdetail(js->sasl)); *error = tmp; return JABBER_SASL_STATE_FAIL; } else { xmlnode *response = xmlnode_new("response"); xmlnode_set_namespace(response, NS_XMPP_SASL); if (clen > 0) { /* Cyrus SASL 2.1.22 appears to contain code to add the charset * to the response for DIGEST-MD5 but there is no possibility * it will be executed. * * My reading of the digestmd5 plugin indicates the username and * realm are always encoded in UTF-8 (they seem to be the values * we pass in), so we need to ensure charset=utf-8 is set. */ if (!purple_strequal(js->current_mech, "DIGEST-MD5") || strstr(c_out, ",charset=")) /* If we're not using DIGEST-MD5 or Cyrus SASL is fixed */ enc_out = purple_base64_encode((unsigned char*)c_out, clen); else { char *tmp = g_strdup_printf("%s,charset=utf-8", c_out); enc_out = purple_base64_encode((unsigned char*)tmp, clen + 14); g_free(tmp); } xmlnode_insert_data(response, enc_out, -1); g_free(enc_out); } *reply = response; return JABBER_SASL_STATE_CONTINUE; } }
char *virNetSASLSessionListMechanisms(virNetSASLSessionPtr sasl) { const char *mechlist; char *ret = NULL; int err; virMutexLock(&sasl->lock); err = sasl_listmech(sasl->conn, NULL, /* Don't need to set user */ "", /* Prefix */ ",", /* Separator */ "", /* Suffix */ &mechlist, NULL, NULL); if (err != SASL_OK) { virNetError(VIR_ERR_INTERNAL_ERROR, _("cannot list SASL mechanisms %d (%s)"), err, sasl_errdetail(sasl->conn)); goto cleanup; } if (!(ret = strdup(mechlist))) { virReportOOMError(); goto cleanup; } cleanup: virMutexUnlock(&sasl->lock); return ret; }
TSaslClient::TSaslClient(const string& mechanisms, const string& authenticationId, const string& protocol, const string& serverName, const map<string,string>& props, sasl_callback_t* callbacks) { conn = NULL; if (!props.empty()) { throw SaslServerImplException("Properties not yet supported"); } int result = sasl_client_new(protocol.c_str(), serverName.c_str(), NULL, NULL, callbacks, 0, &conn); if (result != SASL_OK) { if (conn) { throw SaslServerImplException(sasl_errdetail(conn)); } else { throw SaslServerImplException(sasl_errstring(result, NULL, NULL)); } } if (!authenticationId.empty()) { /* TODO: setup security property */ /* sasl_security_properties_t secprops; // populate secprops result = sasl_setprop(conn, SASL_AUTH_EXTERNAL, authenticationId.c_str()); */ } chosenMech = mechList = mechanisms; authComplete = false; clientStarted = false; }
/* Tell the client the authentication failed. This is only used during the authentication exchange (i.e. inside try_auth()). */ static svn_error_t * fail_auth(svn_ra_svn_conn_t *conn, apr_pool_t *pool, sasl_conn_t *sasl_ctx) { const char *msg = sasl_errdetail(sasl_ctx); SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w(c)", "failure", msg)); return svn_ra_svn_flush(conn, pool); }
size_t CyrusSecurityLayer::decode(const char* input, size_t size) { size_t inStart = 0; do { size_t inSize = std::min(size - inStart, maxInputSize); int result = sasl_decode(conn, input + inStart, inSize, &decrypted, &decryptedSize); if (result != SASL_OK) { throw framing::InternalErrorException(QPID_MSG("SASL decode error: " << sasl_errdetail(conn))); } inStart += inSize; size_t copied = 0; do { size_t count = std::min(decryptedSize - copied, decodeBuffer.size - decodeBuffer.position); ::memcpy(decodeBuffer.data + decodeBuffer.position, decrypted + copied, count); copied += count; decodeBuffer.position += count; size_t decodedSize = codec->decode(decodeBuffer.data, decodeBuffer.position); if (decodedSize < decodeBuffer.position) { ::memmove(decodeBuffer.data, decodeBuffer.data + decodedSize, decodeBuffer.position - decodedSize); } decodeBuffer.position -= decodedSize; } while (copied < decryptedSize); } while (inStart < size); return size; }
char *virNetSASLSessionListMechanisms(virNetSASLSessionPtr sasl) { const char *mechlist; char *ret = NULL; int err; virObjectLock(sasl); err = sasl_listmech(sasl->conn, NULL, /* Don't need to set user */ "", /* Prefix */ ",", /* Separator */ "", /* Suffix */ &mechlist, NULL, NULL); if (err != SASL_OK) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot list SASL mechanisms %d (%s)"), err, sasl_errdetail(sasl->conn)); goto cleanup; } ignore_value(VIR_STRDUP(ret, mechlist)); cleanup: virObjectUnlock(sasl); return ret; }
/* Used if we run into a SASL error outside try_auth(). */ static svn_error_t * fail_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, sasl_conn_t *sasl_ctx) { svn_error_t *err = svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL, sasl_errdetail(sasl_ctx)); SVN_ERR(write_failure(conn, pool, &err)); return svn_ra_svn_flush(conn, pool); }
static bool pni_check_sasl_result(sasl_conn_t *conn, int r, pn_transport_t *logger) { if (r!=SASL_OK) { if (logger->trace & PN_TRACE_DRV) pn_transport_logf(logger, "sasl error: %s", conn ? sasl_errdetail(conn) : sasl_errstring(r, NULL, NULL)); return false; } return true; }
int is_sasl_failure(sasl_conn_t *conn, int result, char **error_message) { if (result < 0) { spprintf(error_message, 0, "Authentication error: %s", sasl_errdetail(conn)); return 1; } return 0; }
CyrusSecurityLayer::CyrusSecurityLayer(sasl_conn_t* c, uint16_t maxFrameSize) : conn(c), decrypted(0), decryptedSize(0), encrypted(0), encryptedSize(0), codec(0), maxInputSize(0), decodeBuffer(maxFrameSize), encodeBuffer(maxFrameSize), encoded(0) { const void* value(0); int result = sasl_getprop(conn, SASL_MAXOUTBUF, &value); if (result != SASL_OK) { throw framing::InternalErrorException(QPID_MSG("SASL encode error: " << sasl_errdetail(conn))); } maxInputSize = *(reinterpret_cast<const unsigned*>(value)); }
uint8_t* TSasl::wrap(const uint8_t* outgoing, const int offset, const uint32_t len, uint32_t* outLen) { uint32_t outputlen; uint8_t* output; int result; result = sasl_encode(conn, (const char*)outgoing+offset, len, (const char**)&output, &outputlen); if (result != SASL_OK) { throw SaslException(sasl_errdetail(conn)); } *outLen = outputlen; return output; }
ssize_t pni_sasl_impl_decode(pn_transport_t *transport, pn_bytes_t in, pn_bytes_t *out) { if ( in.size==0 ) return 0; sasl_conn_t *cyrus_conn = (sasl_conn_t*)transport->sasl->impl_context; const char *output; unsigned int outlen; int r = sasl_decode(cyrus_conn, in.start, in.size, &output, &outlen); if (outlen==0) return 0; if ( r==SASL_OK ) { *out = pn_bytes(outlen, output); return outlen; } if (transport->trace & PN_TRACE_DRV) pn_transport_logf(transport, "SASL decode error: %s", sasl_errdetail(cyrus_conn)); return PN_ERR; }
int virNetSASLSessionClientStart(virNetSASLSessionPtr sasl, const char *mechlist, sasl_interact_t **prompt_need, const char **clientout, size_t *clientoutlen, const char **mech) { unsigned outlen = 0; int err; int ret = -1; VIR_DEBUG("sasl=%p mechlist=%s prompt_need=%p clientout=%p clientoutlen=%p mech=%p", sasl, mechlist, prompt_need, clientout, clientoutlen, mech); virMutexLock(&sasl->lock); err = sasl_client_start(sasl->conn, mechlist, prompt_need, clientout, &outlen, mech); *clientoutlen = outlen; switch (err) { case SASL_OK: if (virNetSASLSessionUpdateBufSize(sasl) < 0) goto cleanup; ret = VIR_NET_SASL_COMPLETE; break; case SASL_CONTINUE: ret = VIR_NET_SASL_CONTINUE; break; case SASL_INTERACT: ret = VIR_NET_SASL_INTERACT; break; default: virNetError(VIR_ERR_AUTH_FAILED, _("Failed to start SASL negotiation: %d (%s)"), err, sasl_errdetail(sasl->conn)); break; } cleanup: virMutexUnlock(&sasl->lock); return ret; }
int virNetSASLSessionClientStep(virNetSASLSessionPtr sasl, const char *serverin, size_t serverinlen, sasl_interact_t **prompt_need, const char **clientout, size_t *clientoutlen) { unsigned inlen = serverinlen; unsigned outlen = 0; int err; int ret = -1; VIR_DEBUG("sasl=%p serverin=%p serverinlen=%zu prompt_need=%p clientout=%p clientoutlen=%p", sasl, serverin, serverinlen, prompt_need, clientout, clientoutlen); virObjectLock(sasl); err = sasl_client_step(sasl->conn, serverin, inlen, prompt_need, clientout, &outlen); *clientoutlen = outlen; switch (err) { case SASL_OK: if (virNetSASLSessionUpdateBufSize(sasl) < 0) goto cleanup; ret = VIR_NET_SASL_COMPLETE; break; case SASL_CONTINUE: ret = VIR_NET_SASL_CONTINUE; break; case SASL_INTERACT: ret = VIR_NET_SASL_INTERACT; break; default: virReportError(VIR_ERR_AUTH_FAILED, _("Failed to step SASL negotiation: %d (%s)"), err, sasl_errdetail(sasl->conn)); break; } cleanup: virObjectUnlock(sasl); return ret; }
int virNetSASLSessionServerStart(virNetSASLSessionPtr sasl, const char *mechname, const char *clientin, size_t clientinlen, const char **serverout, size_t *serveroutlen) { unsigned inlen = clientinlen; unsigned outlen = 0; int err; int ret = -1; virObjectLock(sasl); err = sasl_server_start(sasl->conn, mechname, clientin, inlen, serverout, &outlen); *serveroutlen = outlen; switch (err) { case SASL_OK: if (virNetSASLSessionUpdateBufSize(sasl) < 0) goto cleanup; ret = VIR_NET_SASL_COMPLETE; break; case SASL_CONTINUE: ret = VIR_NET_SASL_CONTINUE; break; case SASL_INTERACT: ret = VIR_NET_SASL_INTERACT; break; default: virReportError(VIR_ERR_AUTH_FAILED, _("Failed to start SASL negotiation: %d (%s)"), err, sasl_errdetail(sasl->conn)); break; } cleanup: virObjectUnlock(sasl); return ret; }
TSaslServer::TSaslServer(const string& service, const string& serverFQDN, const string& userRealm, unsigned flags, sasl_callback_t* callbacks) { conn = NULL; int result = sasl_server_new(service.c_str(), serverFQDN.size() == 0 ? NULL : serverFQDN.c_str(), userRealm.size() == 0 ? NULL :userRealm.c_str(), NULL, NULL, callbacks, flags, &conn); if (result != SASL_OK) { if (conn) { throw SaslServerImplException(sasl_errdetail(conn)); } else { throw SaslServerImplException(sasl_errstring(result, NULL, NULL)); } } authComplete = false; serverStarted = false; }
/* Evaluates the challenge data and generates a response. */ uint8_t* TSaslClient::evaluateChallengeOrResponse( const uint8_t* challenge, const uint32_t len, uint32_t *resLen) { sasl_interact_t* client_interact=NULL; uint8_t* out=NULL; uint32_t outlen=0; uint32_t result; char* mechUsing; if (!clientStarted) { result=sasl_client_start(conn, mechList.c_str(), &client_interact, /* filled in if an interaction is needed */ (const char**)&out, /* filled in on success */ &outlen, /* filled in on success */ (const char**)&mechUsing); clientStarted = true; chosenMech = mechUsing; } else { if (len > 0) { result=sasl_client_step(conn, /* our context */ (const char*)challenge, /* the data from the server */ len, /* its length */ &client_interact, /* this should be unallocated and NULL */ (const char**)&out, /* filled in on success */ &outlen); /* filled in on success */ } else { result = SASL_CONTINUE; } } if (result == SASL_OK) { authComplete = true; } else if (result != SASL_CONTINUE) { throw SaslClientImplException(sasl_errdetail(conn)); } *resLen = outlen; return (uint8_t*)out; }
uint8_t* TSaslServer::evaluateChallengeOrResponse(const uint8_t* response, const uint32_t len, uint32_t* resLen) { uint8_t* out = NULL; uint32_t outlen = 0; uint32_t result; if (!serverStarted) { result = sasl_server_start(conn, (const char *)response, NULL, 0, (const char **)&out, &outlen); } else { result = sasl_server_step(conn, (const char*)response, len, (const char**)&out, &outlen); } if (result == SASL_OK) { authComplete = true; } else if (result != SASL_CONTINUE) { throw SaslServerImplException(sasl_errdetail(conn)); } serverStarted = true; *resLen = outlen; return out; }
size_t CyrusSecurityLayer::encode(const char* buffer, size_t size) { size_t processed = 0;//records how many bytes have been written to buffer do { if (!encrypted) { if (!encoded) { encodeBuffer.position = 0; encoded = codec->encode(encodeBuffer.data, encodeBuffer.size); if (!encoded) break;//nothing more to do } size_t encryptable = std::min(encoded, maxInputSize); int result = sasl_encode(conn, encodeBuffer.data + encodeBuffer.position, encryptable, &encrypted, &encryptedSize); if (result != SASL_OK) { throw framing::InternalErrorException(QPID_MSG("SASL encode error: " << sasl_errdetail(conn))); } encodeBuffer.position += encryptable; encoded -= encryptable; } size_t remaining = size - processed; if (remaining < encryptedSize) { //can't fit all encrypted data in the buffer we've //been given, copy in what we can and hold on to the //rest until the next call ::memcpy(const_cast<char*>(buffer + processed), encrypted, remaining); processed += remaining; encrypted += remaining; encryptedSize -= remaining; } else { ::memcpy(const_cast<char*>(buffer + processed), encrypted, encryptedSize); processed += encryptedSize; encrypted = 0; encryptedSize = 0; } } while (processed < size); return processed; }
static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t len) { uint32_t datalen = len; const char *serverout; unsigned int serveroutlen; int err; char *clientdata = NULL; /* NB, distinction of NULL vs "" is *critical* in SASL */ if (datalen) { clientdata = (char*)data; clientdata[datalen-1] = '\0'; /* Should be on wire, but make sure */ datalen--; /* Don't count NULL byte when passing to _start() */ } VNC_DEBUG("Start SASL auth with mechanism %s. Data %p (%d bytes)\n", vs->sasl.mechlist, clientdata, datalen); err = sasl_server_start(vs->sasl.conn, vs->sasl.mechlist, clientdata, datalen, &serverout, &serveroutlen); if (err != SASL_OK && err != SASL_CONTINUE) { VNC_DEBUG("sasl start failed %d (%s)\n", err, sasl_errdetail(vs->sasl.conn)); sasl_dispose(&vs->sasl.conn); vs->sasl.conn = NULL; goto authabort; } if (serveroutlen > SASL_DATA_MAX_LEN) { VNC_DEBUG("sasl start reply data too long %d\n", serveroutlen); sasl_dispose(&vs->sasl.conn); vs->sasl.conn = NULL; goto authabort; } VNC_DEBUG("SASL return data %d bytes, nil; %d\n", serveroutlen, serverout ? 0 : 1); if (serveroutlen) { vnc_write_u32(vs, serveroutlen + 1); vnc_write(vs, serverout, serveroutlen + 1); } else { vnc_write_u32(vs, 0); } /* Whether auth is complete */ vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1); if (err == SASL_CONTINUE) { VNC_DEBUG("%s", "Authentication must continue\n"); /* Wait for step length */ vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4); } else { if (!vnc_auth_sasl_check_ssf(vs)) { VNC_DEBUG("Authentication rejected for weak SSF %p\n", vs->ioc); goto authreject; } /* Check username whitelist ACL */ if (vnc_auth_sasl_check_access(vs) < 0) { VNC_DEBUG("Authentication rejected for ACL %p\n", vs->ioc); goto authreject; } VNC_DEBUG("Authentication successful %p\n", vs->ioc); vnc_write_u32(vs, 0); /* Accept auth */ start_client_init(vs); } return 0; authreject: vnc_write_u32(vs, 1); /* Reject auth */ vnc_write_u32(vs, sizeof("Authentication failed")); vnc_write(vs, "Authentication failed", sizeof("Authentication failed")); vnc_flush(vs); vnc_client_error(vs); return -1; authabort: vnc_client_error(vs); return -1; }
void start_auth_sasl(VncState *vs) { const char *mechlist = NULL; sasl_security_properties_t secprops; int err; char *localAddr, *remoteAddr; int mechlistlen; VNC_DEBUG("Initialize SASL auth %p\n", vs->ioc); /* Get local & remote client addresses in form IPADDR;PORT */ localAddr = vnc_socket_ip_addr_string(vs->sioc, true, NULL); if (!localAddr) { goto authabort; } remoteAddr = vnc_socket_ip_addr_string(vs->sioc, false, NULL); if (!remoteAddr) { g_free(localAddr); goto authabort; } err = sasl_server_new("vnc", NULL, /* FQDN - just delegates to gethostname */ NULL, /* User realm */ localAddr, remoteAddr, NULL, /* Callbacks, not needed */ SASL_SUCCESS_DATA, &vs->sasl.conn); g_free(localAddr); g_free(remoteAddr); localAddr = remoteAddr = NULL; if (err != SASL_OK) { VNC_DEBUG("sasl context setup failed %d (%s)", err, sasl_errstring(err, NULL, NULL)); vs->sasl.conn = NULL; goto authabort; } /* Inform SASL that we've got an external SSF layer from TLS/x509 */ if (vs->auth == VNC_AUTH_VENCRYPT && vs->subauth == VNC_AUTH_VENCRYPT_X509SASL) { Error *local_err = NULL; int keysize; sasl_ssf_t ssf; keysize = qcrypto_tls_session_get_key_size(vs->tls, &local_err); if (keysize < 0) { VNC_DEBUG("cannot TLS get cipher size: %s\n", error_get_pretty(local_err)); error_free(local_err); sasl_dispose(&vs->sasl.conn); vs->sasl.conn = NULL; goto authabort; } ssf = keysize * CHAR_BIT; /* tls key size is bytes, sasl wants bits */ err = sasl_setprop(vs->sasl.conn, SASL_SSF_EXTERNAL, &ssf); if (err != SASL_OK) { VNC_DEBUG("cannot set SASL external SSF %d (%s)\n", err, sasl_errstring(err, NULL, NULL)); sasl_dispose(&vs->sasl.conn); vs->sasl.conn = NULL; goto authabort; } } else { vs->sasl.wantSSF = 1; } memset (&secprops, 0, sizeof secprops); /* Inform SASL that we've got an external SSF layer from TLS. * * Disable SSF, if using TLS+x509+SASL only. TLS without x509 * is not sufficiently strong */ if (vs->vd->is_unix || (vs->auth == VNC_AUTH_VENCRYPT && vs->subauth == VNC_AUTH_VENCRYPT_X509SASL)) { /* If we've got TLS or UNIX domain sock, we don't care about SSF */ secprops.min_ssf = 0; secprops.max_ssf = 0; secprops.maxbufsize = 8192; secprops.security_flags = 0; } else { /* Plain TCP, better get an SSF layer */ secprops.min_ssf = 56; /* Good enough to require kerberos */ secprops.max_ssf = 100000; /* Arbitrary big number */ secprops.maxbufsize = 8192; /* Forbid any anonymous or trivially crackable auth */ secprops.security_flags = SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT; } err = sasl_setprop(vs->sasl.conn, SASL_SEC_PROPS, &secprops); if (err != SASL_OK) { VNC_DEBUG("cannot set SASL security props %d (%s)\n", err, sasl_errstring(err, NULL, NULL)); sasl_dispose(&vs->sasl.conn); vs->sasl.conn = NULL; goto authabort; } err = sasl_listmech(vs->sasl.conn, NULL, /* Don't need to set user */ "", /* Prefix */ ",", /* Separator */ "", /* Suffix */ &mechlist, NULL, NULL); if (err != SASL_OK) { VNC_DEBUG("cannot list SASL mechanisms %d (%s)\n", err, sasl_errdetail(vs->sasl.conn)); sasl_dispose(&vs->sasl.conn); vs->sasl.conn = NULL; goto authabort; } VNC_DEBUG("Available mechanisms for client: '%s'\n", mechlist); vs->sasl.mechlist = g_strdup(mechlist); mechlistlen = strlen(mechlist); vnc_write_u32(vs, mechlistlen); vnc_write(vs, mechlist, mechlistlen); vnc_flush(vs); VNC_DEBUG("Wait for client mechname length\n"); vnc_read_when(vs, protocol_client_auth_sasl_mechname_len, 4); return; authabort: vnc_client_error(vs); }
void saslerr(int why, const char *what, sasl_conn_t *conn) { fprintf(stderr, "%s: %s\n", what, sasl_errstring(why, NULL, NULL)); fprintf(stderr, " --> %s\n", conn?sasl_errdetail(conn):""); }
/* do the sasl negotiation; return -1 if it fails */ int mysasl_negotiate(FILE *in, FILE *out, sasl_conn_t *conn) { char buf[8192]; char chosenmech[128]; const char *data; int len; int r = SASL_FAIL; const char *userid; #ifdef HAVE_GSS_GET_NAME_ATTRIBUTE gss_name_t peer = GSS_C_NO_NAME; #endif /* generate the capability list */ if (mech) { dprintf(2, "forcing use of mechanism %s\n", mech); data = strdup(mech); len = strlen(data); } else { int count; dprintf(1, "generating client mechanism list... "); r = sasl_listmech(conn, NULL, NULL, " ", NULL, &data, (unsigned int *)&len, &count); if (r != SASL_OK) saslfail(r, "generating mechanism list"); dprintf(1, "%d mechanisms\n", count); } /* send capability list to client */ send_string(out, data, len); dprintf(1, "waiting for client mechanism...\n"); len = recv_string(in, chosenmech, sizeof chosenmech); if (len <= 0) { printf("client didn't choose mechanism\n"); fputc('N', out); /* send NO to client */ fflush(out); return -1; } if (mech && strcasecmp(mech, chosenmech)) { printf("client didn't choose mandatory mechanism\n"); fputc('N', out); /* send NO to client */ fflush(out); return -1; } len = recv_string(in, buf, sizeof(buf)); if(len != 1) { saslerr(r, "didn't receive first-send parameter correctly"); fprintf(stderr, "%s\n", sasl_errdetail(conn)); fputc('N', out); fflush(out); return -1; } if(buf[0] == 'Y') { /* receive initial response (if any) */ len = recv_string(in, buf, sizeof(buf)); /* start libsasl negotiation */ r = sasl_server_start(conn, chosenmech, buf, len, &data, (unsigned int *)&len); } else { r = sasl_server_start(conn, chosenmech, NULL, 0, &data, (unsigned int *)&len); } if (r != SASL_OK && r != SASL_CONTINUE) { saslerr(r, "starting SASL negotiation"); fprintf(stderr, "%s\n", sasl_errdetail(conn)); fputc('N', out); /* send NO to client */ fflush(out); return -1; } while (r == SASL_CONTINUE) { if (data) { dprintf(2, "sending response length %d...\n", len); fputc('C', out); /* send CONTINUE to client */ send_string(out, data, len); } else { dprintf(2, "sending null response...\n"); fputc('C', out); /* send CONTINUE to client */ send_string(out, "", 0); } dprintf(1, "waiting for client reply...\n"); len = recv_string(in, buf, sizeof buf); if (len < 0) { printf("client disconnected\n"); return -1; } r = sasl_server_step(conn, buf, len, &data, (unsigned int *)&len); if (r != SASL_OK && r != SASL_CONTINUE) { saslerr(r, "performing SASL negotiation"); fprintf(stderr, "%s\n", sasl_errdetail(conn)); fputc('N', out); /* send NO to client */ fflush(out); return -1; } } if (r != SASL_OK) { saslerr(r, "incorrect authentication"); fprintf(stderr, "%s\n", sasl_errdetail(conn)); fputc('N', out); /* send NO to client */ fflush(out); return -1; } fputc('O', out); /* send OK to client */ fflush(out); dprintf(1, "negotiation complete\n"); r = sasl_getprop(conn, SASL_USERNAME, (const void **) &userid); printf("successful authentication '%s'\n", userid); #ifdef HAVE_GSS_GET_NAME_ATTRIBUTE r = sasl_getprop(conn, SASL_GSS_PEER_NAME, (const void **) &peer); if (peer != GSS_C_NO_NAME) { OM_uint32 minor; enumerateAttributes(&minor, peer, 1); } #endif return 0; }
/** * Initialize and start SASL authentication. * * Returns 0 on successful init and -1 on error. * * Locality: broker thread */ int rd_kafka_sasl_client_new (rd_kafka_transport_t *rktrans, char *errstr, int errstr_size) { int r; rd_kafka_broker_t *rkb = rktrans->rktrans_rkb; rd_kafka_t *rk = rkb->rkb_rk; char *hostname, *t; sasl_callback_t callbacks[16] = { // { SASL_CB_GETOPT, (void *)rd_kafka_sasl_cb_getopt, rktrans }, { SASL_CB_LOG, (void *)rd_kafka_sasl_cb_log, rktrans }, { SASL_CB_AUTHNAME, (void *)rd_kafka_sasl_cb_getsimple, rktrans }, { SASL_CB_PASS, (void *)rd_kafka_sasl_cb_getsecret, rktrans }, { SASL_CB_ECHOPROMPT, (void *)rd_kafka_sasl_cb_chalprompt, rktrans }, { SASL_CB_GETREALM, (void *)rd_kafka_sasl_cb_getrealm, rktrans }, { SASL_CB_CANON_USER, (void *)rd_kafka_sasl_cb_canon, rktrans }, { SASL_CB_LIST_END } }; /* SASL_CB_USER is needed for PLAIN but breaks GSSAPI */ if (!strcmp(rk->rk_conf.sasl.service_name, "PLAIN")) { int endidx; /* Find end of callbacks array */ for (endidx = 0 ; callbacks[endidx].id != SASL_CB_LIST_END ; endidx++) ; callbacks[endidx].id = SASL_CB_USER; callbacks[endidx].proc = (void *)rd_kafka_sasl_cb_getsimple; endidx++; callbacks[endidx].id = SASL_CB_LIST_END; } rd_strdupa(&hostname, rktrans->rktrans_rkb->rkb_nodename); if ((t = strchr(hostname, ':'))) *t = '\0'; /* remove ":port" */ rd_rkb_dbg(rkb, SECURITY, "SASL", "Initializing SASL client: service name %s, " "hostname %s, mechanisms %s", rk->rk_conf.sasl.service_name, hostname, rk->rk_conf.sasl.mechanisms); /* Acquire or refresh ticket if kinit is configured */ rd_kafka_sasl_kinit_refresh(rkb); r = sasl_client_new(rk->rk_conf.sasl.service_name, hostname, NULL, NULL, /* no local & remote IP checks */ callbacks, 0, &rktrans->rktrans_sasl.conn); if (r != SASL_OK) { rd_snprintf(errstr, errstr_size, "%s", sasl_errstring(r, NULL, NULL)); return -1; } if (rk->rk_conf.debug & RD_KAFKA_DBG_SECURITY) { const char *avail_mechs; sasl_listmech(rktrans->rktrans_sasl.conn, NULL, NULL, " ", NULL, &avail_mechs, NULL, NULL); rd_rkb_dbg(rkb, SECURITY, "SASL", "My supported SASL mechanisms: %s", avail_mechs); } rd_kafka_transport_poll_set(rktrans, POLLIN); do { const char *out; unsigned int outlen; const char *mech = NULL; r = sasl_client_start(rktrans->rktrans_sasl.conn, rk->rk_conf.sasl.mechanisms, NULL, &out, &outlen, &mech); if (r >= 0) if (rd_kafka_sasl_send(rktrans, out, outlen, errstr, errstr_size)) return -1; } while (r == SASL_INTERACT); if (r == SASL_OK) { /* PLAIN is appearantly done here, but we still need to make sure * the PLAIN frame is sent and we get a response back (but we must * not pass the response to libsasl or it will fail). */ rktrans->rktrans_sasl.complete = 1; return 0; } else if (r != SASL_CONTINUE) { rd_snprintf(errstr, errstr_size, "SASL handshake failed (start (%d)): %s", r, sasl_errdetail(rktrans->rktrans_sasl.conn)); return -1; } return 0; }
/** * Handle received frame from broker. */ static int rd_kafka_sasl_handle_recv (rd_kafka_transport_t *rktrans, rd_kafka_buf_t *rkbuf, char *errstr, int errstr_size) { int r; rd_rkb_dbg(rktrans->rktrans_rkb, SECURITY, "SASL", "Received SASL frame from broker (%"PRIdsz" bytes)", rkbuf ? rkbuf->rkbuf_len : 0); if (rktrans->rktrans_sasl.complete && (!rkbuf || rkbuf->rkbuf_len == 0)) goto auth_successful; do { sasl_interact_t *interact = NULL; const char *out; unsigned int outlen; r = sasl_client_step(rktrans->rktrans_sasl.conn, rkbuf && rkbuf->rkbuf_len > 0 ? rkbuf->rkbuf_rbuf : NULL, rkbuf ? rkbuf->rkbuf_len : 0, &interact, &out, &outlen); if (rkbuf) { rd_kafka_buf_destroy(rkbuf); rkbuf = NULL; } if (r >= 0) { /* Note: outlen may be 0 here for an empty response */ if (rd_kafka_sasl_send(rktrans, out, outlen, errstr, errstr_size) == -1) return -1; } if (r == SASL_INTERACT) rd_rkb_dbg(rktrans->rktrans_rkb, SECURITY, "SASL", "SASL_INTERACT: %lu %s, %s, %s, %p", interact->id, interact->challenge, interact->prompt, interact->defresult, interact->result); } while (r == SASL_INTERACT); if (r == SASL_CONTINUE) return 0; /* Wait for more data from broker */ else if (r != SASL_OK) { rd_snprintf(errstr, errstr_size, "SASL handshake failed (step): %s", sasl_errdetail(rktrans->rktrans_sasl.conn)); return -1; } /* Authentication successful */ auth_successful: if (rktrans->rktrans_rkb->rkb_rk->rk_conf.debug & RD_KAFKA_DBG_SECURITY) { const char *user, *mech, *authsrc; if (sasl_getprop(rktrans->rktrans_sasl.conn, SASL_USERNAME, (const void **)&user) != SASL_OK) user = "******"; if (sasl_getprop(rktrans->rktrans_sasl.conn, SASL_MECHNAME, (const void **)&mech) != SASL_OK) mech = "(unknown)"; if (sasl_getprop(rktrans->rktrans_sasl.conn, SASL_AUTHSOURCE, (const void **)&authsrc) != SASL_OK) authsrc = "(unknown)"; rd_rkb_dbg(rktrans->rktrans_rkb, SECURITY, "SASL", "Authenticated as %s using %s (%s)", user, mech, authsrc); } rd_kafka_broker_connect_up(rktrans->rktrans_rkb); return 0; }
static int nsldapi_sasl_do_bind( LDAP *ld, const char *dn, const char *mechs, unsigned flags, LDAP_SASL_INTERACT_PROC *callback, void *defaults, LDAPControl **sctrl, LDAPControl **cctrl, LDAPControl ***rctrl ) { sasl_interact_t *prompts = NULL; sasl_conn_t *ctx = NULL; sasl_ssf_t *ssf = NULL; const char *mech = NULL; int saslrc, rc; struct berval ccred; unsigned credlen; int stepnum = 1; char *sasl_username = NULL; if (rctrl) { /* init to NULL so we can call ldap_controls_free below */ *rctrl = NULL; } if (NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3) { LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL ); return( LDAP_NOT_SUPPORTED ); } /* shouldn't happen */ if (callback == NULL) { return( LDAP_LOCAL_ERROR ); } if ( (rc = nsldapi_sasl_open(ld, NULL, &ctx, 0)) != LDAP_SUCCESS ) { return( rc ); } ccred.bv_val = NULL; ccred.bv_len = 0; LDAPDebug(LDAP_DEBUG_TRACE, "Starting SASL/%s authentication\n", (mechs ? mechs : ""), 0, 0 ); do { saslrc = sasl_client_start( ctx, mechs, &prompts, (const char **)&ccred.bv_val, &credlen, &mech ); LDAPDebug(LDAP_DEBUG_TRACE, "Doing step %d of client start for SASL/%s authentication\n", stepnum, (mech ? mech : ""), 0 ); stepnum++; if( saslrc == SASL_INTERACT && (callback)(ld, flags, defaults, prompts) != LDAP_SUCCESS ) { break; } } while ( saslrc == SASL_INTERACT ); ccred.bv_len = credlen; if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) { return( nsldapi_sasl_cvterrno( ld, saslrc, nsldapi_strdup( sasl_errdetail( ctx ) ) ) ); } stepnum = 1; do { struct berval *scred; int clientstepnum = 1; scred = NULL; if (rctrl) { /* if we're looping again, we need to free any controls set during the previous loop */ /* NOTE that this assumes we only care about the controls returned by the last call to nsldapi_sasl_bind_s - if we care about _all_ controls, we will have to figure out some way to append them each loop go round */ ldap_controls_free(*rctrl); *rctrl = NULL; } LDAPDebug(LDAP_DEBUG_TRACE, "Doing step %d of bind for SASL/%s authentication\n", stepnum, (mech ? mech : ""), 0 ); stepnum++; /* notify server of a sasl bind step */ rc = nsldapi_sasl_bind_s(ld, dn, mech, &ccred, sctrl, cctrl, &scred, rctrl); if ( ccred.bv_val != NULL ) { ccred.bv_val = NULL; } if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) { ber_bvfree( scred ); return( rc ); } if( rc == LDAP_SUCCESS && saslrc == SASL_OK ) { /* we're done, no need to step */ if( scred ) { if ( scred->bv_len == 0 ) { /* MS AD sends back empty screds */ LDAPDebug(LDAP_DEBUG_ANY, "SASL BIND complete - ignoring empty credential response\n", 0, 0, 0); ber_bvfree( scred ); } else { /* but server provided us with data! */ LDAPDebug(LDAP_DEBUG_TRACE, "SASL BIND complete but invalid because server responded with credentials - length [%u]\n", scred->bv_len, 0, 0); ber_bvfree( scred ); LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, "Error during SASL handshake - invalid server credential response" ); return( LDAP_LOCAL_ERROR ); } } break; } /* perform the next step of the sasl bind */ do { LDAPDebug(LDAP_DEBUG_TRACE, "Doing client step %d of bind step %d for SASL/%s authentication\n", clientstepnum, stepnum, (mech ? mech : "") ); clientstepnum++; saslrc = sasl_client_step( ctx, (scred == NULL) ? NULL : scred->bv_val, (scred == NULL) ? 0 : scred->bv_len, &prompts, (const char **)&ccred.bv_val, &credlen ); if( saslrc == SASL_INTERACT && (callback)(ld, flags, defaults, prompts) != LDAP_SUCCESS ) { break; } } while ( saslrc == SASL_INTERACT ); ccred.bv_len = credlen; ber_bvfree( scred ); if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) { return( nsldapi_sasl_cvterrno( ld, saslrc, nsldapi_strdup( sasl_errdetail( ctx ) ) ) ); } } while ( rc == LDAP_SASL_BIND_IN_PROGRESS ); if ( rc != LDAP_SUCCESS ) { return( rc ); } if ( saslrc != SASL_OK ) { return( nsldapi_sasl_cvterrno( ld, saslrc, nsldapi_strdup( sasl_errdetail( ctx ) ) ) ); } saslrc = sasl_getprop( ctx, SASL_USERNAME, (const void **) &sasl_username ); if ( (saslrc == SASL_OK) && sasl_username ) { LDAPDebug(LDAP_DEBUG_TRACE, "SASL identity: %s\n", sasl_username, 0, 0); } saslrc = sasl_getprop( ctx, SASL_SSF, (const void **) &ssf ); if( saslrc == SASL_OK ) { if( ssf && *ssf ) { LDAPDebug(LDAP_DEBUG_TRACE, "SASL install encryption, for SSF: %lu\n", (unsigned long) *ssf, 0, 0 ); nsldapi_sasl_install( ld, NULL ); } } return( rc ); }
bool reds_sasl_start_auth(RedsStream *stream, AsyncReadDone read_cb, void *opaque) { const char *mechlist = NULL; sasl_security_properties_t secprops; int err; char *localAddr, *remoteAddr; int mechlistlen; RedsSASL *sasl = &stream->priv->sasl; if (!(localAddr = reds_stream_get_local_address(stream))) { goto error; } if (!(remoteAddr = reds_stream_get_remote_address(stream))) { free(localAddr); goto error; } err = sasl_server_new("spice", NULL, /* FQDN - just delegates to gethostname */ NULL, /* User realm */ localAddr, remoteAddr, NULL, /* Callbacks, not needed */ SASL_SUCCESS_DATA, &sasl->conn); free(localAddr); free(remoteAddr); localAddr = remoteAddr = NULL; if (err != SASL_OK) { spice_warning("sasl context setup failed %d (%s)", err, sasl_errstring(err, NULL, NULL)); sasl->conn = NULL; goto error; } /* Inform SASL that we've got an external SSF layer from TLS */ if (stream->priv->ssl) { sasl_ssf_t ssf; ssf = SSL_get_cipher_bits(stream->priv->ssl, NULL); err = sasl_setprop(sasl->conn, SASL_SSF_EXTERNAL, &ssf); if (err != SASL_OK) { spice_warning("cannot set SASL external SSF %d (%s)", err, sasl_errstring(err, NULL, NULL)); goto error_dispose; } } else { sasl->wantSSF = 1; } memset(&secprops, 0, sizeof secprops); /* Inform SASL that we've got an external SSF layer from TLS */ if (stream->priv->ssl) { /* If we've got TLS (or UNIX domain sock), we don't care about SSF */ secprops.min_ssf = 0; secprops.max_ssf = 0; secprops.maxbufsize = 8192; secprops.security_flags = 0; } else { /* Plain TCP, better get an SSF layer */ secprops.min_ssf = 56; /* Good enough to require kerberos */ secprops.max_ssf = 100000; /* Arbitrary big number */ secprops.maxbufsize = 8192; /* Forbid any anonymous or trivially crackable auth */ secprops.security_flags = SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT; } err = sasl_setprop(sasl->conn, SASL_SEC_PROPS, &secprops); if (err != SASL_OK) { spice_warning("cannot set SASL security props %d (%s)", err, sasl_errstring(err, NULL, NULL)); goto error_dispose; } err = sasl_listmech(sasl->conn, NULL, /* Don't need to set user */ "", /* Prefix */ ",", /* Separator */ "", /* Suffix */ &mechlist, NULL, NULL); if (err != SASL_OK || mechlist == NULL) { spice_warning("cannot list SASL mechanisms %d (%s)", err, sasl_errdetail(sasl->conn)); goto error_dispose; } spice_debug("Available mechanisms for client: '%s'", mechlist); sasl->mechlist = spice_strdup(mechlist); mechlistlen = strlen(mechlist); if (!reds_stream_write_all(stream, &mechlistlen, sizeof(uint32_t)) || !reds_stream_write_all(stream, sasl->mechlist, mechlistlen)) { spice_warning("SASL mechanisms write error"); goto error; } spice_debug("Wait for client mechname length"); reds_stream_async_read(stream, (uint8_t *)&sasl->len, sizeof(uint32_t), read_cb, opaque); return true; error_dispose: sasl_dispose(&sasl->conn); sasl->conn = NULL; error: return false; }
RedsSaslError reds_sasl_handle_auth_start(RedsStream *stream, AsyncReadDone read_cb, void *opaque) { const char *serverout; unsigned int serveroutlen; int err; char *clientdata = NULL; RedsSASL *sasl = &stream->priv->sasl; uint32_t datalen = sasl->len; /* NB, distinction of NULL vs "" is *critical* in SASL */ if (datalen) { clientdata = sasl->data; clientdata[datalen - 1] = '\0'; /* Should be on wire, but make sure */ datalen--; /* Don't count NULL byte when passing to _start() */ } spice_debug("Start SASL auth with mechanism %s. Data %p (%d bytes)", sasl->mechlist, clientdata, datalen); err = sasl_server_start(sasl->conn, sasl->mechlist, clientdata, datalen, &serverout, &serveroutlen); if (err != SASL_OK && err != SASL_CONTINUE) { spice_warning("sasl start failed %d (%s)", err, sasl_errdetail(sasl->conn)); return REDS_SASL_ERROR_INVALID_DATA; } if (serveroutlen > SASL_DATA_MAX_LEN) { spice_warning("sasl start reply data too long %d", serveroutlen); return REDS_SASL_ERROR_INVALID_DATA; } spice_debug("SASL return data %d bytes, %p", serveroutlen, serverout); if (serveroutlen) { serveroutlen += 1; reds_stream_write_all(stream, &serveroutlen, sizeof(uint32_t)); reds_stream_write_all(stream, serverout, serveroutlen); } else { reds_stream_write_all(stream, &serveroutlen, sizeof(uint32_t)); } /* Whether auth is complete */ reds_stream_write_u8(stream, err == SASL_CONTINUE ? 0 : 1); if (err == SASL_CONTINUE) { spice_debug("%s", "Authentication must continue (start)"); /* Wait for step length */ reds_stream_async_read(stream, (uint8_t *)&sasl->len, sizeof(uint32_t), read_cb, opaque); return REDS_SASL_ERROR_CONTINUE; } else { int ssf; if (auth_sasl_check_ssf(sasl, &ssf) == 0) { spice_warning("Authentication rejected for weak SSF"); goto authreject; } spice_debug("Authentication successful"); reds_stream_write_u32(stream, SPICE_LINK_ERR_OK); /* Accept auth */ /* * Delay writing in SSF encoded until now */ sasl->runSSF = ssf; reds_stream_disable_writev(stream); /* make sure writev isn't called directly anymore */ return REDS_SASL_ERROR_OK; } authreject: reds_stream_write_u32(stream, 1); /* Reject auth */ reds_stream_write_u32(stream, sizeof("Authentication failed")); reds_stream_write_all(stream, "Authentication failed", sizeof("Authentication failed")); return REDS_SASL_ERROR_AUTH_FAILED; }
int auth_cyrus_sasl_server(auth_instance *ablock, uschar *data) { auth_cyrus_sasl_options_block *ob = (auth_cyrus_sasl_options_block *)(ablock->options_block); uschar *output, *out2, *input, *clear, *hname; uschar *debug = NULL; /* Stops compiler complaining */ sasl_callback_t cbs[] = {{SASL_CB_LIST_END, NULL, NULL}}; sasl_conn_t *conn; char * realm_expanded = NULL; int rc, firsttime = 1, clen, *negotiated_ssf_ptr = NULL, negotiated_ssf; unsigned int inlen, outlen; input = data; inlen = Ustrlen(data); HDEBUG(D_auth) debug = string_copy(data); hname = expand_string(ob->server_hostname); if (hname && ob->server_realm) realm_expanded = CS expand_string(ob->server_realm); if (!hname || !realm_expanded && ob->server_realm) { auth_defer_msg = expand_string_message; return DEFER; } if (inlen) { if ((clen = b64decode(input, &clear)) < 0) return BAD64; input = clear; inlen = clen; } if ((rc = sasl_server_init(cbs, "exim")) != SASL_OK) { auth_defer_msg = US"couldn't initialise Cyrus SASL library"; return DEFER; } rc = sasl_server_new(CS ob->server_service, CS hname, realm_expanded, NULL, NULL, NULL, 0, &conn); HDEBUG(D_auth) debug_printf("Initialised Cyrus SASL server connection; service=\"%s\" fqdn=\"%s\" realm=\"%s\"\n", ob->server_service, hname, realm_expanded); if (rc != SASL_OK ) { auth_defer_msg = US"couldn't initialise Cyrus SASL connection"; sasl_done(); return DEFER; } 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); } 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) { struct sockaddr_storage ss; int (*query)(int, struct sockaddr *, socklen_t *); int propnum, port; const uschar *label; uschar *address, *address_port; const char *s_err; socklen_t sslen; if (i) { query = &getpeername; propnum = SASL_IPREMOTEPORT; label = CUS"peer"; } else { query = &getsockname; propnum = SASL_IPLOCALPORT; label = CUS"local"; } sslen = sizeof(ss); if ((rc = query(fileno(smtp_in), (struct sockaddr *) &ss, &sslen)) < 0) { HDEBUG(D_auth) debug_printf("Failed to get %s address information: %s\n", label, strerror(errno)); break; } address = host_ntoa(-1, &ss, NULL, &port); address_port = string_sprintf("%s;%d", address, port); if ((rc = sasl_setprop(conn, propnum, address_port)) != SASL_OK) { s_err = sasl_errdetail(conn); HDEBUG(D_auth) 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); } for (rc = SASL_CONTINUE; rc == SASL_CONTINUE; ) { if (firsttime) { firsttime = 0; HDEBUG(D_auth) debug_printf("Calling sasl_server_start(%s,\"%s\")\n", ob->server_mech, debug); rc = sasl_server_start(conn, CS ob->server_mech, inlen?CS input:NULL, inlen, (const char **)(&output), &outlen); } else { /* make sure that we have a null-terminated string */ out2 = string_copyn(output, outlen); if ((rc = auth_get_data(&input, out2, outlen)) != OK) { /* we couldn't get the data, so free up the library before * returning whatever error we get */ sasl_dispose(&conn); sasl_done(); return rc; } inlen = Ustrlen(input); HDEBUG(D_auth) debug = string_copy(input); if (inlen) { if ((clen = b64decode(input, &clear)) < 0) { sasl_dispose(&conn); sasl_done(); return BAD64; } input = clear; inlen = clen; } HDEBUG(D_auth) debug_printf("Calling sasl_server_step(\"%s\")\n", debug); rc = sasl_server_step(conn, CS input, inlen, (const char **)(&output), &outlen); } if (rc == SASL_BADPROT) { sasl_dispose(&conn); sasl_done(); return UNEXPECTED; } if (rc == SASL_CONTINUE) continue; /* Get the username and copy it into $auth1 and $1. The former is now the preferred variable; the latter is the original variable. */ if ((sasl_getprop(conn, SASL_USERNAME, (const void **)(&out2))) != SASL_OK) { HDEBUG(D_auth) debug_printf("Cyrus SASL library will not tell us the username: %s\n", sasl_errstring(rc, NULL, NULL)); log_write(0, LOG_REJECT, "%s authenticator (%s):\n " "Cyrus SASL username fetch problem: %s", ablock->name, ob->server_mech, sasl_errstring(rc, NULL, NULL)); sasl_dispose(&conn); sasl_done(); return FAIL; } auth_vars[0] = expand_nstring[1] = string_copy(out2); expand_nlength[1] = Ustrlen(out2); expand_nmax = 1; switch (rc) { case SASL_FAIL: case SASL_BUFOVER: case SASL_BADMAC: case SASL_BADAUTH: case SASL_NOAUTHZ: case SASL_ENCRYPT: case SASL_EXPIRED: case SASL_DISABLED: case SASL_NOUSER: /* these are considered permanent failure codes */ HDEBUG(D_auth) debug_printf("Cyrus SASL permanent failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL)); log_write(0, LOG_REJECT, "%s authenticator (%s):\n " "Cyrus SASL permanent failure: %s", ablock->name, ob->server_mech, sasl_errstring(rc, NULL, NULL)); sasl_dispose(&conn); sasl_done(); return FAIL; case SASL_NOMECH: /* this is a temporary failure, because the mechanism is not * available for this user. If it wasn't available at all, we * shouldn't have got here in the first place... */ HDEBUG(D_auth) debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL)); auth_defer_msg = string_sprintf("Cyrus SASL: mechanism %s not available", ob->server_mech); sasl_dispose(&conn); sasl_done(); return DEFER; case SASL_OK: HDEBUG(D_auth) debug_printf("Cyrus SASL %s authentication succeeded for %s\n", ob->server_mech, auth_vars[0]); if ((rc = sasl_getprop(conn, SASL_SSF, (const void **)(&negotiated_ssf_ptr)))!= SASL_OK) { HDEBUG(D_auth) debug_printf("Cyrus SASL library will not tell us the SSF: %s\n", sasl_errstring(rc, NULL, NULL)); log_write(0, LOG_REJECT, "%s authenticator (%s):\n " "Cyrus SASL SSF value not available: %s", ablock->name, ob->server_mech, sasl_errstring(rc, NULL, NULL)); sasl_dispose(&conn); sasl_done(); return FAIL; } negotiated_ssf = *negotiated_ssf_ptr; HDEBUG(D_auth) debug_printf("Cyrus SASL %s negotiated SSF: %d\n", ob->server_mech, negotiated_ssf); if (negotiated_ssf > 0) { HDEBUG(D_auth) debug_printf("Exim does not implement SASL wrapping (needed for SSF %d).\n", negotiated_ssf); log_write(0, LOG_REJECT, "%s authenticator (%s):\n " "Cyrus SASL SSF %d not supported by Exim", ablock->name, ob->server_mech, negotiated_ssf); sasl_dispose(&conn); sasl_done(); return FAIL; } /* close down the connection, freeing up library's memory */ sasl_dispose(&conn); sasl_done(); /* Expand server_condition as an authorization check */ return auth_check_serv_cond(ablock); default: /* Anything else is a temporary failure, and we'll let SASL print out * the error string for us */ HDEBUG(D_auth) debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL)); auth_defer_msg = string_sprintf("Cyrus SASL: %s", sasl_errstring(rc, NULL, NULL)); sasl_dispose(&conn); sasl_done(); return DEFER; } } /* NOTREACHED */ return 0; /* Stop compiler complaints */ }
static DWORD _VmDirSASLGetCtxProps( PVDIR_SASL_BIND_INFO pSaslBindInfo ) { DWORD dwError = 0; PCSTR pszBindUPN = NULL; PCSTR pszBindRealm = NULL; sasl_ssf_t* pLocalSaslSSF = NULL; // pSaslCtx owns pszBindUPN dwError = sasl_getprop(pSaslBindInfo->pSaslCtx, SASL_USERNAME, (const void**)&pszBindUPN); BAIL_ON_SASL_ERROR(dwError); // pSaslCtx owns pszBindRealm dwError = sasl_getprop(pSaslBindInfo->pSaslCtx, SASL_DEFUSERREALM, (const void**)&pszBindRealm); BAIL_ON_SASL_ERROR(dwError); ////////////////////////////////////////////////////////////////////////////////////////////// // BUGBUG, Not clear how to get the realm part in GSSAPI bind. // BUGBUG, In this case SASL_USERNAME has NO realm portion and SASL_DEFUSERREALM is NULL. // BUGBUG, Thus, use gVmdirKrbGlobals.pszRealm for now. // BUGBUG, This will not work if we have to support multiple realms scenario. ////////////////////////////////////////////////////////////////////////////////////////////// // In SRP case, SASL_USERNAME has full UPN and SASL_DEFUSERREALM is NULL. ////////////////////////////////////////////////////////////////////////////////////////////// if ( pszBindRealm == NULL && VmDirStringCompareA( pSaslBindInfo->bvMechnism.lberbv.bv_val, SASL_GSSAPI_MECH, FALSE ) == 0 ) { pszBindRealm = gVmdirKrbGlobals.pszRealm; } VMDIR_SAFE_FREE_MEMORY( pSaslBindInfo->pszBindUserName ); dwError = VmDirAllocateStringAVsnprintf( &pSaslBindInfo->pszBindUserName, "%s%s%s", VDIR_SAFE_STRING(pszBindUPN), pszBindRealm ? "@" : "", VDIR_SAFE_STRING(pszBindRealm)); BAIL_ON_VMDIR_ERROR(dwError); dwError = sasl_getprop( pSaslBindInfo->pSaslCtx, SASL_SSF, (const void**)&pLocalSaslSSF ); BAIL_ON_SASL_ERROR(dwError); pSaslBindInfo->saslSSF = *pLocalSaslSSF; cleanup: return dwError; error: goto cleanup; sasl_error: VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "_VmDirSASLGetCtxProps: sasl error (%d)(%s)", dwError, VDIR_SAFE_STRING(sasl_errdetail(pSaslBindInfo->pSaslCtx)) ); dwError = _VmDirSASLToLDAPError(dwError); goto error; }