/* mutt_sasl_setup_conn: replace connection methods, sockdata with * SASL wrappers, for protection layers. Also get ssf, as a fastpath * for the read/write methods. */ void mutt_sasl_setup_conn (CONNECTION * conn, sasl_conn_t * saslconn) { SASL_DATA *sasldata = (SASL_DATA *) mem_malloc (sizeof (SASL_DATA)); sasldata->saslconn = saslconn; /* get ssf so we know whether we have to (en|de)code read/write */ sasl_getprop (saslconn, SASL_SSF, (const void **) &sasldata->ssf); debug_print (3, ("SASL protection strength: %u\n", *sasldata->ssf)); /* Add SASL SSF to transport SSF */ conn->ssf += *sasldata->ssf; sasl_getprop (saslconn, SASL_MAXOUTBUF, (const void **) &sasldata->pbufsize); debug_print (3, ("SASL protection buffer size: %u\n", *sasldata->pbufsize)); /* clear input buffer */ sasldata->buf = NULL; sasldata->bpos = 0; sasldata->blen = 0; /* preserve old functions */ sasldata->sockdata = conn->sockdata; sasldata->msasl_open = conn->conn_open; sasldata->msasl_close = conn->conn_close; sasldata->msasl_read = conn->conn_read; sasldata->msasl_write = conn->conn_write; /* and set up new functions */ conn->sockdata = sasldata; conn->conn_open = mutt_sasl_conn_open; conn->conn_close = mutt_sasl_conn_close; conn->conn_read = mutt_sasl_conn_read; conn->conn_write = mutt_sasl_conn_write; }
void getssfparams() { const int *ssfp; int r = sasl_getprop(con, SASL_SSF, (const void **)&ssfp); if(r == SASL_OK) ssf = *ssfp; sasl_getprop(con, SASL_MAXOUTBUF, (const void **)&maxoutbuf); }
static int _sx_sasl_wio(sx_t s, sx_plugin_t p, sx_buf_t buf) { sasl_conn_t *sasl; int *x, len, pos, reslen, maxbuf; char *out, *result; int sasl_ret; sx_error_t sxe; sasl = ((_sx_sasl_data_t) s->plugin_data[p->index])->sasl; /* if there's no security layer, don't bother */ sasl_getprop(sasl, SASL_SSF, (const void **) &x); if(*x == 0) return 1; _sx_debug(ZONE, "doing sasl encode"); /* can only encode x bytes at a time */ sasl_getprop(sasl, SASL_MAXOUTBUF, (const void **) &x); maxbuf = *x; /* encode the output */ pos = 0; result = NULL; reslen = 0; while(pos < buf->len) { if((buf->len - pos) < maxbuf) maxbuf = buf->len - pos; sasl_ret = sasl_encode(sasl, &buf->data[pos], maxbuf, (const char **) &out, &len); if (sasl_ret != SASL_OK) { _sx_gen_error(sxe, SX_ERR_STREAM, "Stream error", "sasl_encode failed, closing stream"); _sx_event(s, event_ERROR, (void *) &sxe); _sx_state(s, state_CLOSING); return 1; } result = (char *) realloc(result, sizeof(char) * (reslen + len)); memcpy(&result[reslen], out, len); reslen += len; pos += maxbuf; } /* replace the buffer */ _sx_buffer_set(buf, result, reslen, result); _sx_debug(ZONE, "%d bytes encoded for sasl channel", buf->len); return 1; }
static int _sx_sasl_rio(sx_t s, sx_plugin_t p, sx_buf_t buf) { sasl_conn_t *sasl; sx_error_t sxe; int *x, len; char *out; sasl = ((_sx_sasl_data_t) s->plugin_data[p->index])->sasl; /* if there's no security layer, don't bother */ sasl_getprop(sasl, SASL_SSF, (const void **) &x); if(*x == 0) return 1; _sx_debug(ZONE, "doing sasl decode"); /* decode the input */ if (sasl_decode(sasl, buf->data, buf->len, (const char **) &out, &len) != SASL_OK) { /* Fatal error */ _sx_gen_error(sxe, SX_ERR_STREAM, "Stream error", "sasl_decode failed, closing stream"); _sx_event(s, event_ERROR, (void *) &sxe); _sx_state(s, state_CLOSING); return -1; } /* replace the buffer */ _sx_buffer_set(buf, out, len, NULL); _sx_debug(ZONE, "%d bytes decoded from sasl channel", len); return 1; }
string TSasl::getUsername() { const char* username; int result = sasl_getprop(conn, SASL_USERNAME, reinterpret_cast<const void **>(&username)); if (result != SASL_OK) { stringstream ss; ss << "Error getting SASL_USERNAME property: " << sasl_errstring(result, NULL, NULL); throw SaslException(ss.str().c_str()); } // Copy the username and return it to the caller. There is no cleanup/delete call for // calls to sasl_getprops, the sasl layer handles the cleanup internally. string ret(username); // Temporary fix to auth_to_local-style lowercase mapping from // USER_NAME/[email protected] -> user_name/[email protected] // // TODO: The right fix is probably to use UserGroupInformation in the frontend which // will use auth_to_local rules to do this. if (FLAGS_force_lowercase_usernames) { vector<string> components; split(components, ret, is_any_of("@")); if (components.size() > 0 ) { to_lower(components[0]); ret = join(components, "@"); } } return ret; }
static int __pmAuthServerSetAttributes(sasl_conn_t *conn, __pmHashCtl *attrs) { const void *property = NULL; char *username; int sts; sts = sasl_getprop(conn, SASL_USERNAME, &property); username = (char *)property; if (sts == SASL_OK && username) { int len = strlen(username); pmNotifyErr(LOG_INFO, "Successful authentication for user \"%s\"\n", username); if ((username = strdup(username)) == NULL) { pmNoMem("__pmAuthServerSetAttributes", len, PM_RECOV_ERR); return -ENOMEM; } } else { pmNotifyErr(LOG_ERR, "Authentication complete, but no username\n"); return -ESRCH; } if ((sts = __pmHashAdd(PCP_ATTR_USERNAME, username, attrs)) < 0) return sts; return __pmSetUserGroupAttributes(username, attrs); }
static int vnc_auth_sasl_check_ssf(VncState *vs) { const void *val; int err, ssf; if (!vs->sasl.wantSSF) return 1; err = sasl_getprop(vs->sasl.conn, SASL_SSF, &val); if (err != SASL_OK) return 0; ssf = *(const int *)val; VNC_DEBUG("negotiated an SSF of %d\n", ssf); if (ssf < 56) return 0; /* 56 is good for Kerberos */ /* Only setup for read initially, because we're about to send an RPC * reply which must be in plain text. When the next incoming RPC * arrives, we'll switch on writes too * * cf qemudClientReadSASL in qemud.c */ vs->sasl.runSSF = 1; /* We have a SSF that's good enough */ return 1; }
static void pni_process_server_result(pn_transport_t *transport, int result) { pni_sasl_t *sasl = transport->sasl; sasl_conn_t *cyrus_conn = (sasl_conn_t*)sasl->impl_context; switch (result) { case SASL_OK: // Authenticated sasl->outcome = PN_SASL_OK; transport->authenticated = true; // Get username from SASL const void* value; sasl_getprop(cyrus_conn, SASL_USERNAME, &value); sasl->username = (const char*) value; if (transport->trace & PN_TRACE_DRV) pn_transport_logf(transport, "Authenticated user: %s with mechanism %s", sasl->username, sasl->selected_mechanism); pni_sasl_set_desired_state(transport, SASL_POSTED_OUTCOME); break; case SASL_CONTINUE: // Need to send a challenge pni_sasl_set_desired_state(transport, SASL_POSTED_CHALLENGE); break; default: pni_check_sasl_result(cyrus_conn, result, transport); // Failed to authenticate sasl->outcome = PN_SASL_AUTH; pni_sasl_set_desired_state(transport, SASL_POSTED_OUTCOME); break; } }
static int vnc_auth_sasl_check_access(VncState *vs) { const void *val; int err; int allow; err = sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val); if (err != SASL_OK) { VNC_DEBUG("cannot query SASL username on connection %d (%s), denying access\n", err, sasl_errstring(err, NULL, NULL)); return -1; } if (val == NULL) { VNC_DEBUG("no client username was found, denying access\n"); return -1; } VNC_DEBUG("SASL client username %s\n", (const char *)val); vs->sasl.username = g_strdup((const char*)val); if (vs->vd->sasl.acl == NULL) { VNC_DEBUG("no ACL activated, allowing access\n"); return 0; } allow = qemu_acl_party_is_allowed(vs->vd->sasl.acl, vs->sasl.username); VNC_DEBUG("SASL client %s %s by ACL\n", vs->sasl.username, allow ? "allowed" : "denied"); return allow ? 0 : -1; }
static int auth_sasl_check_ssf(RedsSASL *sasl, int *runSSF) { const void *val; int err, ssf; *runSSF = 0; if (!sasl->wantSSF) { return 1; } err = sasl_getprop(sasl->conn, SASL_SSF, &val); if (err != SASL_OK) { return 0; } ssf = *(const int *)val; spice_debug("negotiated an SSF of %d", ssf); if (ssf < 56) { return 0; /* 56 is good for Kerberos */ } *runSSF = 1; /* We have a SSF that's good enough */ return 1; }
const char *virNetSASLSessionGetIdentity(virNetSASLSessionPtr sasl) { const void *val = NULL; int err; virObjectLock(sasl); err = sasl_getprop(sasl->conn, SASL_USERNAME, &val); if (err != SASL_OK) { virReportError(VIR_ERR_AUTH_FAILED, _("cannot query SASL username on connection %d (%s)"), err, sasl_errstring(err, NULL, NULL)); val = NULL; goto cleanup; } if (val == NULL) { virReportError(VIR_ERR_AUTH_FAILED, "%s", _("no client username was found")); goto cleanup; } VIR_DEBUG("SASL client username %s", (const char *)val); cleanup: virObjectUnlock(sasl); return (const char*)val; }
static JabberSaslState jabber_cyrus_handle_success(JabberStream *js, xmlnode *packet, char **error) { const void *x; /* The SASL docs say that if the client hasn't returned OK yet, we * should try one more round against it */ if (js->sasl_state != SASL_OK) { char *enc_in = xmlnode_get_data(packet); unsigned char *dec_in = NULL; const char *c_out; unsigned int clen; gsize declen = 0; if(enc_in != NULL) 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_OK) { /* This happens when the server sends back jibberish * in the "additional data with success" case. * Seen with Wildfire 3.0.1. */ *error = g_strdup(_("Invalid response from server")); return JABBER_SASL_STATE_FAIL; } } /* If we've negotiated a security layer, we need to enable it */ if (js->sasl) { sasl_getprop(js->sasl, SASL_SSF, &x); if (*(int *)x > 0) { sasl_getprop(js->sasl, SASL_MAXOUTBUF, &x); js->sasl_maxbuf = *(int *)x; } } return JABBER_SASL_STATE_OK; }
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)); }
bool pni_sasl_impl_can_encrypt(pn_transport_t *transport) { if (!transport->sasl->impl_context) return false; sasl_conn_t *cyrus_conn = (sasl_conn_t*)transport->sasl->impl_context; // Get SSF to find out if we need to encrypt or not const void* value; int r = sasl_getprop(cyrus_conn, SASL_SSF, &value); if (r != SASL_OK) { // TODO: Should log an error here too, maybe assert here return false; } int ssf = *(int *) value; if (ssf > 0) { return true; } return false; }
ssize_t pni_sasl_impl_max_encrypt_size(pn_transport_t *transport) { if (!transport->sasl->impl_context) return PN_ERR; sasl_conn_t *cyrus_conn = (sasl_conn_t*)transport->sasl->impl_context; const void* value; int r = sasl_getprop(cyrus_conn, SASL_MAXOUTBUF, &value); if (r != SASL_OK) { // TODO: Should log an error here too, maybe assert here return PN_ERR; } int outbuf_size = *(int *) value; return outbuf_size - // XXX: this is a clientside workaround/hack to make GSSAPI work as the Cyrus SASL // GSSAPI plugin seems to return an incorrect value for the buffer size on the client // side, which is greater than the value returned on the server side. Actually using // the entire client side buffer will cause a server side error due to a buffer overrun. (transport->sasl->client? 60 : 0); }
static int virNetSASLSessionUpdateBufSize(virNetSASLSessionPtr sasl) { union { unsigned *maxbufsize; const void *ptr; } u; int err; err = sasl_getprop(sasl->conn, SASL_MAXOUTBUF, &u.ptr); if (err != SASL_OK) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot get security props %d (%s)"), err, sasl_errstring(err, NULL, NULL)); return -1; } VIR_DEBUG("Negotiated bufsize is %u vs requested size %zu", *u.maxbufsize, sasl->maxbufsize); sasl->maxbufsize = *u.maxbufsize; return 0; }
int virNetSASLSessionGetKeySize(virNetSASLSessionPtr sasl) { int err; int ssf; const void *val; virObjectLock(sasl); err = sasl_getprop(sasl->conn, SASL_SSF, &val); if (err != SASL_OK) { virReportError(VIR_ERR_AUTH_FAILED, _("cannot query SASL ssf on connection %d (%s)"), err, sasl_errstring(err, NULL, NULL)); ssf = -1; goto cleanup; } ssf = *(const int *)val; cleanup: virObjectUnlock(sasl); return ssf; }
static const char *xsasl_cyrus_server_get_username(XSASL_SERVER *xp) { const char *myname = "xsasl_cyrus_server_get_username"; XSASL_CYRUS_SERVER *server = (XSASL_CYRUS_SERVER *) xp; VOID_SERVEROUT_TYPE serverout = 0; int sasl_status; /* * XXX Do not free(serverout). */ sasl_status = sasl_getprop(server->sasl_conn, SASL_USERNAME, &serverout); if (sasl_status != SASL_OK || serverout == 0) { msg_warn("%s: sasl_getprop SASL_USERNAME botch: %s", myname, xsasl_cyrus_strerror(sasl_status)); return (0); } if (server->username) myfree(server->username); server->username = mystrdup(serverout); printable(server->username, '?'); return (server->username); }
struct backend *backend_connect(struct backend *ret_backend, const char *server, struct protocol_t *prot, const char *userid, sasl_callback_t *cb, const char **auth_status) { /* need to (re)establish connection to server or create one */ int sock = -1; int r; int err = -1; int ask = 1; /* should we explicitly ask for capabilities? */ struct addrinfo hints, *res0 = NULL, *res; struct sockaddr_un sunsock; char buf[2048]; struct sigaction action; struct backend *ret; char rsessionid[MAX_SESSIONID_SIZE]; if (!ret_backend) { ret = xzmalloc(sizeof(struct backend)); strlcpy(ret->hostname, server, sizeof(ret->hostname)); ret->timeout = NULL; } else ret = ret_backend; if (server[0] == '/') { /* unix socket */ res0 = &hints; memset(res0, 0, sizeof(struct addrinfo)); res0->ai_family = PF_UNIX; res0->ai_socktype = SOCK_STREAM; res0->ai_addr = (struct sockaddr *) &sunsock; res0->ai_addrlen = sizeof(sunsock.sun_family) + strlen(server) + 1; #ifdef SIN6_LEN res0->ai_addrlen += sizeof(sunsock.sun_len); sunsock.sun_len = res0->ai_addrlen; #endif sunsock.sun_family = AF_UNIX; strlcpy(sunsock.sun_path, server, sizeof(sunsock.sun_path)); /* XXX set that we are preauthed */ /* change hostname to 'config_servername' */ strlcpy(ret->hostname, config_servername, sizeof(ret->hostname)); } else { /* inet socket */ memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; err = getaddrinfo(server, prot->service, &hints, &res0); if (err) { syslog(LOG_ERR, "getaddrinfo(%s) failed: %s", server, gai_strerror(err)); goto error; } } /* Setup timeout */ timedout = 0; action.sa_flags = 0; action.sa_handler = timed_out; sigemptyset(&action.sa_mask); if(sigaction(SIGALRM, &action, NULL) < 0) { syslog(LOG_ERR, "Setting timeout in backend_connect failed: sigaction: %m"); /* continue anyway */ } for (res = res0; res; res = res->ai_next) { sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sock < 0) continue; alarm(config_getint(IMAPOPT_CLIENT_TIMEOUT)); if (connect(sock, res->ai_addr, res->ai_addrlen) >= 0) break; if(errno == EINTR && timedout == 1) errno = ETIMEDOUT; close(sock); sock = -1; } /* Remove timeout code */ alarm(0); signal(SIGALRM, SIG_IGN); if (sock < 0) { if (res0 != &hints) freeaddrinfo(res0); syslog(LOG_ERR, "connect(%s) failed: %m", server); goto error; } memcpy(&ret->addr, res->ai_addr, res->ai_addrlen); if (res0 != &hints) freeaddrinfo(res0); ret->in = prot_new(sock, 0); ret->out = prot_new(sock, 1); ret->sock = sock; prot_setflushonread(ret->in, ret->out); ret->prot = prot; /* use literal+ to send literals */ prot_setisclient(ret->in, 1); prot_setisclient(ret->out, 1); if (prot->banner.auto_capa) { /* try to get the capabilities from the banner */ r = ask_capability(ret, /*dobanner*/1, AUTO_CAPA_BANNER); if (r) { /* found capabilities in banner -> don't ask */ ask = 0; } } else { do { /* read the initial greeting */ if (!prot_fgets(buf, sizeof(buf), ret->in)) { syslog(LOG_ERR, "backend_connect(): couldn't read initial greeting: %s", ret->in->error ? ret->in->error : "(null)"); goto error; } } while (strncasecmp(buf, prot->banner.resp, strlen(prot->banner.resp))); strncpy(ret->banner, buf, 2048); } if (ask) { /* get the capabilities */ ask_capability(ret, /*dobanner*/0, AUTO_CAPA_NO); } /* now need to authenticate to backend server, unless we're doing LMTP/CSYNC on a UNIX socket (deliver/sync_client) */ if ((server[0] != '/') || (strcmp(prot->sasl_service, "lmtp") && strcmp(prot->sasl_service, "csync"))) { char *old_mechlist = backend_get_cap_params(ret, CAPA_AUTH); const char *my_status; if ((r = backend_authenticate(ret, userid, cb, &my_status))) { syslog(LOG_ERR, "couldn't authenticate to backend server: %s", sasl_errstring(r, NULL, NULL)); free(old_mechlist); goto error; } else { const void *ssf; sasl_getprop(ret->saslconn, SASL_SSF, &ssf); if (*((sasl_ssf_t *) ssf)) { /* if we have a SASL security layer, compare SASL mech lists before/after AUTH to check for a MITM attack */ char *new_mechlist; int auto_capa = (prot->sasl_cmd.auto_capa == AUTO_CAPA_AUTH_SSF); if (!strcmp(prot->service, "sieve")) { /* XXX Hack to handle ManageSieve servers. * No way to tell from protocol if server will * automatically send capabilities, so we treat it * as optional. */ char ch; /* wait and probe for possible auto-capability response */ usleep(250000); prot_NONBLOCK(ret->in); if ((ch = prot_getc(ret->in)) != EOF) { prot_ungetc(ch, ret->in); } else { auto_capa = AUTO_CAPA_AUTH_NO; } prot_BLOCK(ret->in); } ask_capability(ret, /*dobanner*/0, auto_capa); new_mechlist = backend_get_cap_params(ret, CAPA_AUTH); if (new_mechlist && old_mechlist && strcmp(new_mechlist, old_mechlist)) { syslog(LOG_ERR, "possible MITM attack:" "list of available SASL mechanisms changed"); free(new_mechlist); free(old_mechlist); goto error; } free(new_mechlist); } else if (prot->sasl_cmd.auto_capa == AUTO_CAPA_AUTH_OK) { /* try to get the capabilities from the AUTH success response */ forget_capabilities(ret); parse_capability(ret, my_status); post_parse_capability(ret); } if (!(strcmp(prot->service, "imap") && (strcmp(prot->service, "pop3")))) { parse_sessionid(my_status, rsessionid); syslog(LOG_NOTICE, "proxy %s sessionid=<%s> remote=<%s>", userid, session_id(), rsessionid); } } if (auth_status) *auth_status = my_status; free(old_mechlist); } /* start compression if requested and both client/server support it */ if (config_getswitch(IMAPOPT_PROXY_COMPRESS) && ret && CAPA(ret, CAPA_COMPRESS) && prot->compress_cmd.cmd && do_compress(ret, &prot->compress_cmd)) { syslog(LOG_ERR, "couldn't enable compression on backend server"); goto error; } return ret; error: forget_capabilities(ret); if (ret->in) { prot_free(ret->in); ret->in = NULL; } if (ret->out) { prot_free(ret->out); ret->out = NULL; } if (sock >= 0) close(sock); if (ret->saslconn) { sasl_dispose(&ret->saslconn); ret->saslconn = NULL; } if (!ret_backend) free(ret); return NULL; }
/** move the stream to the auth state */ void _sx_sasl_open(sx_t s, sasl_conn_t *sasl, sx_plugin_t p) { char *method = NULL; char *buf = NULL; char *c; char *authzid = NULL; char *username = NULL; char *realm = NULL; size_t len; int *ssf; /* get the method */ sasl_getprop(sasl, SASL_MECHNAME, (const void **) &buf); if (s->type == type_CLIENT) { static int first_time = 1; if (first_time) { first_time = 0; sasl_switch_hit_register_apple_digest_md5(); } } method = (char *) malloc(sizeof(char) * (strlen(buf) + 17)); sprintf(method, "SASL/%s", buf); /* get the ssf */ if(s->ssf == 0) { sasl_getprop(sasl, SASL_SSF, (const void **) &ssf); s->ssf = *ssf; } /* and the authenticated id */ buf = NULL; sasl_getprop(sasl, SASL_USERNAME, (const void **) &buf); if (buf != NULL) { username = (char *) malloc(sizeof(char) * (strlen(buf)+1)); strncpy(username, buf, strlen(buf)+1); } if (s->type == type_SERVER) { /* Now, we need to turn the id into a JID * (if it isn't already) * * XXX - This will break with s2s SASL, where the authzid is a domain */ len = strlen(username); if (s->req_to) len+=strlen(s->req_to) + 2; authzid = malloc(len + 1); strcpy(authzid, username); buf = NULL; sasl_getprop(sasl, SASL_DEFUSERREALM, (const void **) &buf); if (buf != NULL) { realm = (char *) malloc(sizeof(char) * (strlen(buf)+1)); strncpy(realm, buf, strlen(buf)+1); } c = strrchr(authzid, '@'); if (c && realm && strcmp(c+1, realm) == 0) *c = '\0'; if (s->req_to && strchr(authzid, '@') == 0) { strcat(authzid, "@"); strcat(authzid, s->req_to); } /* schwing! */ sx_auth(s, method, authzid); free(authzid); } else { sx_auth(s, method, username); } if (method != NULL) free(method); if (username != NULL) free(username); if (realm != NULL) free(realm); }
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 int backend_login(struct backend *ret, const char *userid, sasl_callback_t *cb, const char **auth_status, int noauth) { int r = 0; int ask = 1; /* should we explicitly ask for capabilities? */ char buf[2048]; struct protocol_t *prot = ret->prot; if (prot->type != TYPE_STD) return -1; if (prot->u.std.banner.auto_capa) { /* try to get the capabilities from the banner */ r = ask_capability(ret, /*dobanner*/1, AUTO_CAPA_BANNER); if (r) { /* found capabilities in banner -> don't ask */ ask = 0; } } else { do { /* read the initial greeting */ if (!prot_fgets(buf, sizeof(buf), ret->in)) { syslog(LOG_ERR, "backend_login(): couldn't read initial greeting: %s", ret->in->error ? ret->in->error : "(null)"); return -1; } } while (strncasecmp(buf, prot->u.std.banner.resp, strlen(prot->u.std.banner.resp))); xstrncpy(ret->banner, buf, 2048); } if (ask) { /* get the capabilities */ ask_capability(ret, /*dobanner*/0, AUTO_CAPA_NO); } /* now need to authenticate to backend server, unless we're doing LMTP/CSYNC on a UNIX socket (deliver/sync_client) */ if (!noauth) { char *old_mechlist = backend_get_cap_params(ret, CAPA_AUTH); const char *my_status; if ((r = backend_authenticate(ret, userid, cb, &my_status))) { syslog(LOG_ERR, "couldn't authenticate to backend server: %s", sasl_errstring(r, NULL, NULL)); free(old_mechlist); return -1; } else { const void *ssf; sasl_getprop(ret->saslconn, SASL_SSF, &ssf); if (*((sasl_ssf_t *) ssf)) { /* if we have a SASL security layer, compare SASL mech lists before/after AUTH to check for a MITM attack */ char *new_mechlist; int auto_capa = (prot->u.std.sasl_cmd.auto_capa == AUTO_CAPA_AUTH_SSF); if (!strcmp(prot->service, "sieve")) { /* XXX Hack to handle ManageSieve servers. * No way to tell from protocol if server will * automatically send capabilities, so we treat it * as optional. */ char ch; /* wait and probe for possible auto-capability response */ usleep(250000); prot_NONBLOCK(ret->in); if ((ch = prot_getc(ret->in)) != EOF) { prot_ungetc(ch, ret->in); } else { auto_capa = AUTO_CAPA_AUTH_NO; } prot_BLOCK(ret->in); } ask_capability(ret, /*dobanner*/0, auto_capa); new_mechlist = backend_get_cap_params(ret, CAPA_AUTH); if (new_mechlist && old_mechlist && strcmp(new_mechlist, old_mechlist)) { syslog(LOG_ERR, "possible MITM attack:" "list of available SASL mechanisms changed"); if (new_mechlist) free(new_mechlist); if (old_mechlist) free(old_mechlist); return -1; } free(new_mechlist); } else if (prot->u.std.sasl_cmd.auto_capa == AUTO_CAPA_AUTH_OK) { /* try to get the capabilities from the AUTH success response */ forget_capabilities(ret); parse_capability(ret, my_status); post_parse_capability(ret); } if (!(strcmp(prot->service, "imap") && (strcmp(prot->service, "pop3")))) { char rsessionid[MAX_SESSIONID_SIZE]; parse_sessionid(my_status, rsessionid); syslog(LOG_NOTICE, "auditlog: proxy %s sessionid=<%s> remote=<%s>", userid, session_id(), rsessionid); } } if (auth_status) *auth_status = my_status; free(old_mechlist); } /* start compression if requested and both client/server support it */ if (config_getswitch(IMAPOPT_PROXY_COMPRESS) && CAPA(ret, CAPA_COMPRESS) && prot->u.std.compress_cmd.cmd) { r = do_compress(ret, &prot->u.std.compress_cmd); if (r) { syslog(LOG_NOTICE, "couldn't enable compression on backend server: %s", error_message(r)); r = 0; /* not a fail-level error */ } } return 0; }
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; }
static int cyrussasl_getprop(lua_State *l) { struct _sasl_ctx *ctx = NULL; unsigned *maxbufsize; const char *strdata; sasl_ssf_t *ssf; int propnum; int ret; int numargs = lua_gettop(l); if (numargs != 2) { lua_pushstring(l, "usage: user = cyrussasl.get_prop(conn, property)"); lua_error(l); return 0; } ctx = get_context(l, 1); propnum = tointeger(l, 2); switch (propnum) { /* strings */ case SASL_USERNAME: case SASL_DEFUSERREALM: case SASL_SERVICE: case SASL_SERVERFQDN: case SASL_AUTHSOURCE: case SASL_MECHNAME: case SASL_PLUGERR: case SASL_IPLOCALPORT: case SASL_IPREMOTEPORT: ret = sasl_getprop(ctx->conn, propnum, (const void **)&strdata); // return strdata lua_pushstring(l, strdata); lua_pushnumber(l, ret); return 2; case SASL_SSF: // sasl_ssf_t* ret = sasl_getprop(ctx->conn, propnum, (const void **)&ssf); // return *ssf lua_pushnumber(l, *ssf); lua_pushnumber(l, ret); return 2; case SASL_MAXOUTBUF: // unsigned ret = sasl_getprop(ctx->conn, propnum, (const void **)&maxbufsize); // return *maxbufsize lua_pushnumber(l, *maxbufsize); lua_pushnumber(l, ret); return 2; /* I'm not sure how SASL_GETOPTCTX would be useful to a Lua * caller, so I'm not including it for the moment. If you're * reading this and have a good use case, drop me a line and we'll * figure out how to integrate it. */ default: lua_pushstring(l, "Unsupported property passed to cyrussasl.getprop()"); lua_error(l); return 0; } /* NOTREACHED */ }
/* * file in the message structure 'm' from 'pin', assuming a dot-stuffed * stream a la lmtp. * * returns 0 on success, imap error code on failure */ static int savemsg(struct clientdata *cd, const struct lmtp_func *func, message_data_t *m) { FILE *f; struct stat sbuf; const char **body; int r; int nrcpts = m->rcpt_num; time_t now = time(NULL); static unsigned msgid_count = 0; char datestr[RFC822_DATETIME_MAX+1], tls_info[250] = ""; const char *skipheaders[] = { "Return-Path", /* need to remove (we add our own) */ NULL }; char *addbody, *fold[5], *p; int addlen, nfold, i; /* Copy to spool file */ f = func->spoolfile(m); if (!f) { prot_printf(cd->pout, "451 4.3.%c cannot create temporary file: %s\r\n", ( #ifdef EDQUOT errno == EDQUOT || #endif errno == ENOSPC) ? '1' : '2', error_message(errno)); return IMAP_IOERROR; } prot_printf(cd->pout, "354 go ahead\r\n"); if (m->return_path && func->addretpath) { /* add the return path */ char *rpath = m->return_path; const char *hostname = 0; clean_retpath(rpath); /* Append our hostname if there's no domain in address */ hostname = NULL; if (!strchr(rpath, '@') && strlen(rpath) > 0) { hostname = config_servername; } addlen = 2 + strlen(rpath) + (hostname ? 1 + strlen(hostname) : 0); addbody = xmalloc(addlen + 1); sprintf(addbody, "<%s%s%s>", rpath, hostname ? "@" : "", hostname ? hostname : ""); fprintf(f, "Return-Path: %s\r\n", addbody); spool_cache_header(xstrdup("Return-Path"), addbody, m->hdrcache); } /* add a received header */ time_to_rfc822(now, datestr, sizeof(datestr)); addlen = 8 + strlen(cd->lhlo_param) + strlen(cd->clienthost); if (m->authuser) addlen += 28 + strlen(m->authuser) + 5; /* +5 for ssf */ addlen += 25 + strlen(config_servername) + strlen(cyrus_version()); #ifdef HAVE_SSL if (cd->tls_conn) { addlen += 3 + tls_get_info(cd->tls_conn, tls_info, sizeof(tls_info)); } #endif addlen += 2 + strlen(datestr); p = addbody = xmalloc(addlen + 1); nfold = 0; p += sprintf(p, "from %s (%s)", cd->lhlo_param, cd->clienthost); fold[nfold++] = p; if (m->authuser) { const void *ssfp; sasl_ssf_t ssf; sasl_getprop(cd->conn, SASL_SSF, &ssfp); ssf = *((sasl_ssf_t *) ssfp); p += sprintf(p, " (authenticated user=%s bits=%d)", m->authuser, ssf); fold[nfold++] = p; } /* We are always atleast "with LMTPA" -- no unauth delivery */ p += sprintf(p, " by %s", config_servername); if (config_serverinfo == IMAP_ENUM_SERVERINFO_ON) { p += sprintf(p, " (Cyrus %s)", cyrus_version()); } p += sprintf(p, " with LMTP%s%s", cd->starttls_done ? "S" : "", cd->authenticated != NOAUTH ? "A" : ""); if (*tls_info) { fold[nfold++] = p; p += sprintf(p, " (%s)", tls_info); } strcat(p++, ";"); fold[nfold++] = p; p += sprintf(p, " %s", datestr); fprintf(f, "Received: "); for (i = 0, p = addbody; i < nfold; p = fold[i], i++) { fprintf(f, "%.*s\r\n\t", (int) (fold[i] - p), p); } fprintf(f, "%s\r\n", p); spool_cache_header(xstrdup("Received"), addbody, m->hdrcache); /* add any requested headers */ if (func->addheaders) { struct addheader *h; for (h = func->addheaders; h && h->name; h++) { fprintf(f, "%s: %s\r\n", h->name, h->body); spool_cache_header(xstrdup(h->name), xstrdup(h->body), m->hdrcache); } } /* fill the cache */ r = spool_fill_hdrcache(cd->pin, f, m->hdrcache, skipheaders); /* now, using our header cache, fill in the data that we want */ /* first check resent-message-id */ if ((body = msg_getheader(m, "resent-message-id")) && body[0][0]) { m->id = xstrdup(body[0]); } else if ((body = msg_getheader(m, "message-id")) && body[0][0]) { m->id = xstrdup(body[0]); } else if (body) { r = IMAP_MESSAGE_BADHEADER; /* empty message-id */ } else { /* no message-id, create one */ pid_t p = getpid(); m->id = xmalloc(40 + strlen(config_servername)); sprintf(m->id, "<cmu-lmtpd-%d-%d-%u@%s>", p, (int) now, msgid_count++, config_servername); fprintf(f, "Message-ID: %s\r\n", m->id); spool_cache_header(xstrdup("Message-ID"), xstrdup(m->id), m->hdrcache); } /* get date */ if (!(body = spool_getheader(m->hdrcache, "date"))) { /* no date, create one */ addbody = xstrdup(datestr); m->date = xstrdup(datestr); fprintf(f, "Date: %s\r\n", addbody); spool_cache_header(xstrdup("Date"), addbody, m->hdrcache); } else { m->date = xstrdup(body[0]); } if (!m->return_path && (body = msg_getheader(m, "return-path"))) { /* let's grab return_path */ m->return_path = xstrdup(body[0]); clean822space(m->return_path); clean_retpath(m->return_path); } r |= spool_copy_msg(cd->pin, f); if (r) { fclose(f); if (func->removespool) { /* remove the spool'd message */ func->removespool(m); } while (nrcpts--) { send_lmtp_error(cd->pout, r); } return r; } fflush(f); if (ferror(f)) { while (nrcpts--) { prot_printf(cd->pout, "451 4.3.%c cannot copy message to temporary file: %s\r\n", ( #ifdef EDQUOT errno == EDQUOT || #endif errno == ENOSPC) ? '1' : '2', error_message(errno)); } fclose(f); if (func->removespool) func->removespool(m); return IMAP_IOERROR; } if (fstat(fileno(f), &sbuf) == -1) { while (nrcpts--) { prot_printf(cd->pout, "451 4.3.2 cannot stat message temporary file: %s\r\n", error_message(errno)); } fclose(f); if (func->removespool) func->removespool(m); return IMAP_IOERROR; } m->size = sbuf.st_size; m->f = f; m->data = prot_new(fileno(f), 0); return 0; }
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; int rc, firsttime=1, clen; unsigned int inlen, outlen; input=data; inlen=Ustrlen(data); HDEBUG(D_auth) debug=string_copy(data); hname=expand_string(ob->server_hostname); if(hname == NULL) { auth_defer_msg = expand_string_message; return DEFER; } if(inlen) { clen=auth_b64decode(input, &clear); if(clen < 0) { return BAD64; } input=clear; inlen=clen; } rc=sasl_server_init(cbs, "exim"); if (rc != SASL_OK) { auth_defer_msg = US"couldn't initialise Cyrus SASL library"; return DEFER; } rc=sasl_server_new(CS ob->server_service, CS hname, CS ob->server_realm, NULL, NULL, NULL, 0, &conn); if( rc != SASL_OK ) { auth_defer_msg = US"couldn't initialise Cyrus SASL connection"; sasl_done(); return DEFER; } rc=SASL_CONTINUE; while(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=store_get(outlen+1); memcpy(out2,output,outlen); out2[outlen]='\0'; 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) { clen=auth_b64decode(input, &clear); if(clen < 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; } else if( rc==SASL_FAIL || rc==SASL_BUFOVER || rc==SASL_BADMAC || rc==SASL_BADAUTH || rc==SASL_NOAUTHZ || rc==SASL_ENCRYPT || rc==SASL_EXPIRED || rc==SASL_DISABLED || rc==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; } else if(rc==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; } else if(!(rc==SASL_OK || rc==SASL_CONTINUE)) { /* 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; } else if(rc==SASL_OK) { /* Get the username and copy it into $auth1 and $1. The former is now the preferred variable; the latter is the original variable. */ rc = sasl_getprop(conn, SASL_USERNAME, (const void **)(&out2)); auth_vars[0] = expand_nstring[1] = string_copy(out2); expand_nlength[1] = Ustrlen(expand_nstring[1]); expand_nmax = 1; HDEBUG(D_auth) debug_printf("Cyrus SASL %s authentication succeeded for %s\n", ob->server_mech, out2); /* 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); } } /* NOTREACHED */ return 0; /* Stop compiler complaints */ }
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 ); }
/* 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; }
/** * 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; }
svn_error_t *cyrus_auth_request(svn_ra_svn_conn_t *conn, apr_pool_t *pool, server_baton_t *b, enum access_type required, svn_boolean_t needs_username) { sasl_conn_t *sasl_ctx; apr_pool_t *subpool; apr_status_t apr_err; const char *localaddrport = NULL, *remoteaddrport = NULL; const char *mechlist, *val; char hostname[APRMAXHOSTLEN + 1]; sasl_security_properties_t secprops; svn_boolean_t success, no_anonymous; int mech_count, result = SASL_OK; SVN_ERR(svn_ra_svn__get_addresses(&localaddrport, &remoteaddrport, conn, pool)); apr_err = apr_gethostname(hostname, sizeof(hostname), pool); if (apr_err) { svn_error_t *err = svn_error_wrap_apr(apr_err, _("Can't get hostname")); SVN_ERR(write_failure(conn, pool, &err)); return svn_ra_svn_flush(conn, pool); } /* Create a SASL context. SASL_SUCCESS_DATA tells SASL that the protocol supports sending data along with the final "success" message. */ result = sasl_server_new(SVN_RA_SVN_SASL_NAME, hostname, b->realm, localaddrport, remoteaddrport, NULL, SASL_SUCCESS_DATA, &sasl_ctx); if (result != SASL_OK) { svn_error_t *err = svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL, sasl_errstring(result, NULL, NULL)); SVN_ERR(write_failure(conn, pool, &err)); return svn_ra_svn_flush(conn, pool); } /* Make sure the context is always destroyed. */ apr_pool_cleanup_register(b->pool, sasl_ctx, sasl_dispose_cb, apr_pool_cleanup_null); /* Initialize security properties. */ svn_ra_svn__default_secprops(&secprops); /* Don't allow ANONYMOUS if a username is required. */ no_anonymous = needs_username || get_access(b, UNAUTHENTICATED) < required; if (no_anonymous) secprops.security_flags |= SASL_SEC_NOANONYMOUS; svn_config_get(b->cfg, &val, SVN_CONFIG_SECTION_SASL, SVN_CONFIG_OPTION_MIN_SSF, "0"); SVN_ERR(svn_cstring_atoui(&secprops.min_ssf, val)); svn_config_get(b->cfg, &val, SVN_CONFIG_SECTION_SASL, SVN_CONFIG_OPTION_MAX_SSF, "256"); SVN_ERR(svn_cstring_atoui(&secprops.max_ssf, val)); /* Set security properties. */ result = sasl_setprop(sasl_ctx, SASL_SEC_PROPS, &secprops); if (result != SASL_OK) return fail_cmd(conn, pool, sasl_ctx); /* SASL needs to know if we are externally authenticated. */ if (b->tunnel_user) result = sasl_setprop(sasl_ctx, SASL_AUTH_EXTERNAL, b->tunnel_user); if (result != SASL_OK) return fail_cmd(conn, pool, sasl_ctx); /* Get the list of mechanisms. */ result = sasl_listmech(sasl_ctx, NULL, NULL, " ", NULL, &mechlist, NULL, &mech_count); if (result != SASL_OK) return fail_cmd(conn, pool, sasl_ctx); if (mech_count == 0) { svn_error_t *err = svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL, _("Could not obtain the list" " of SASL mechanisms")); SVN_ERR(write_failure(conn, pool, &err)); return svn_ra_svn_flush(conn, pool); } /* Send the list of mechanisms and the realm to the client. */ SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "(w)c", mechlist, b->realm)); /* The main authentication loop. */ subpool = svn_pool_create(pool); do { svn_pool_clear(subpool); SVN_ERR(try_auth(conn, sasl_ctx, subpool, b, &success)); } while (!success); svn_pool_destroy(subpool); SVN_ERR(svn_ra_svn__enable_sasl_encryption(conn, sasl_ctx, pool)); if (no_anonymous) { char *p; const void *user; /* Get the authenticated username. */ result = sasl_getprop(sasl_ctx, SASL_USERNAME, &user); if (result != SASL_OK) return fail_cmd(conn, pool, sasl_ctx); if ((p = strchr(user, '@')) != NULL) { /* Drop the realm part. */ b->user = apr_pstrndup(b->pool, user, p - (const char *)user); } else { svn_error_t *err; err = svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL, _("Couldn't obtain the authenticated" " username")); SVN_ERR(write_failure(conn, pool, &err)); return svn_ra_svn_flush(conn, pool); } } return SVN_NO_ERROR; }