int mailesmtp_auth_sasl(mailsmtp * session, const char * auth_type, const char * server_fqdn, const char * local_ip_port, const char * remote_ip_port, const char * login, const char * auth_name, const char * password, const char * realm) { #ifdef USE_SASL int r; char command[SMTP_STRING_SIZE]; sasl_callback_t sasl_callback[5]; const char * sasl_out; unsigned sasl_out_len; const char * mechusing; sasl_secret_t * secret; int res; size_t len; char * encoded; unsigned int encoded_len; unsigned int max_encoded; sasl_callback[0].id = SASL_CB_GETREALM; sasl_callback[0].proc = (int (*)(void)) sasl_getrealm; sasl_callback[0].context = session; sasl_callback[1].id = SASL_CB_USER; sasl_callback[1].proc = (int (*)(void)) sasl_getsimple; sasl_callback[1].context = session; sasl_callback[2].id = SASL_CB_AUTHNAME; sasl_callback[2].proc = (int (*)(void)) sasl_getsimple; sasl_callback[2].context = session; sasl_callback[3].id = SASL_CB_PASS; sasl_callback[3].proc = (int (*)(void)) sasl_getsecret; sasl_callback[3].context = session; sasl_callback[4].id = SASL_CB_LIST_END; sasl_callback[4].proc = NULL; sasl_callback[4].context = NULL; len = strlen(password); secret = malloc(sizeof(* secret) + len); if (secret == NULL) { res = MAILSMTP_ERROR_MEMORY; goto err; } secret->len = len; memcpy(secret->data, password, len + 1); session->smtp_sasl.sasl_server_fqdn = server_fqdn; session->smtp_sasl.sasl_login = login; session->smtp_sasl.sasl_auth_name = auth_name; session->smtp_sasl.sasl_password = password; session->smtp_sasl.sasl_realm = realm; session->smtp_sasl.sasl_secret = secret; /* init SASL */ if (session->smtp_sasl.sasl_conn != NULL) { sasl_dispose((sasl_conn_t **) &session->smtp_sasl.sasl_conn); session->smtp_sasl.sasl_conn = NULL; } else { mailsasl_ref(); } r = sasl_client_new("smtp", server_fqdn, local_ip_port, remote_ip_port, sasl_callback, 0, (sasl_conn_t **) &session->smtp_sasl.sasl_conn); if (r != SASL_OK) { res = MAILSMTP_ERROR_AUTH_LOGIN; goto free_secret; } r = sasl_client_start(session->smtp_sasl.sasl_conn, auth_type, NULL, &sasl_out, &sasl_out_len, &mechusing); if ((r != SASL_CONTINUE) && (r != SASL_OK)) { res = MAILSMTP_ERROR_AUTH_LOGIN; goto free_sasl_conn; } if (sasl_out_len != 0) { max_encoded = ((sasl_out_len + 2) / 3) * 4; encoded = malloc(max_encoded + 1); if (encoded == NULL) { res = MAILSMTP_ERROR_MEMORY; goto free_sasl_conn; } r = sasl_encode64(sasl_out, sasl_out_len, encoded, max_encoded + 1, &encoded_len); if (r != SASL_OK) { free(encoded); res = MAILSMTP_ERROR_MEMORY; goto free_sasl_conn; } snprintf(command, SMTP_STRING_SIZE, "AUTH %s %s\r\n", auth_type, encoded); free(encoded); } else { snprintf(command, SMTP_STRING_SIZE, "AUTH %s\r\n", auth_type); } r = send_command_private(session, command, 0); if (r == -1) { res = MAILSMTP_ERROR_STREAM; goto free_sasl_conn; } while (1) { r = read_response(session); switch (r) { case 220: case 235: res = MAILSMTP_NO_ERROR; goto free_sasl_conn; case 535: res = MAILSMTP_ERROR_AUTH_LOGIN; goto free_sasl_conn; case 553: case 554: res = MAILSMTP_ERROR_AUTH_AUTHENTICATION_FAILED; goto free_sasl_conn; case 334: { size_t response_len; char * decoded; unsigned int decoded_len; unsigned int max_decoded; char * p; p = strchr(session->response, '\r'); if (p != NULL) { * p = '\0'; } p = strchr(session->response, '\n'); if (p != NULL) { * p = '\0'; } response_len = strlen(session->response); max_decoded = response_len * 3 / 4; decoded = malloc(max_decoded + 1); if (decoded == NULL) { res = MAILSMTP_ERROR_MEMORY; goto free_sasl_conn; } r = sasl_decode64(session->response, response_len, decoded, max_decoded + 1, &decoded_len); if (r != SASL_OK) { free(decoded); res = MAILSMTP_ERROR_MEMORY; goto free_sasl_conn; } r = sasl_client_step(session->smtp_sasl.sasl_conn, decoded, decoded_len, NULL, &sasl_out, &sasl_out_len); free(decoded); if ((r != SASL_CONTINUE) && (r != SASL_OK)) { res = MAILSMTP_ERROR_AUTH_LOGIN; goto free_sasl_conn; } max_encoded = ((sasl_out_len + 2) / 3) * 4; encoded = malloc(max_encoded + 1); if (encoded == NULL) { res = MAILSMTP_ERROR_MEMORY; goto free_sasl_conn; } r = sasl_encode64(sasl_out, sasl_out_len, encoded, max_encoded + 1, &encoded_len); if (r != SASL_OK) { free(encoded); res = MAILSMTP_ERROR_MEMORY; goto free_sasl_conn; } snprintf(command, SMTP_STRING_SIZE, "%s\r\n", encoded); r = send_command(session, command); free(encoded); if (r == -1) { res = MAILSMTP_ERROR_STREAM; goto free_sasl_conn; } } break; default: res = auth_map_errors(r); goto free_sasl_conn; } } res = MAILSMTP_NO_ERROR; free_sasl_conn: sasl_dispose((sasl_conn_t **) &session->smtp_sasl.sasl_conn); session->smtp_sasl.sasl_conn = NULL; mailsasl_unref(); free_secret: free(session->smtp_sasl.sasl_secret); session->smtp_sasl.sasl_secret = NULL; err: return res; #else return MAILSMTP_ERROR_NOT_IMPLEMENTED; #endif }
static JabberSaslState jabber_auth_start_cyrus(JabberStream *js, xmlnode **reply, char **error) { PurpleAccount *account; const char *clientout = NULL; char *enc_out; unsigned coutlen = 0; sasl_security_properties_t secprops; gboolean again; gboolean plaintext = TRUE; /* Set up security properties and options */ secprops.min_ssf = 0; secprops.security_flags = SASL_SEC_NOANONYMOUS; account = purple_connection_get_account(js->gc); if (!jabber_stream_is_ssl(js)) { secprops.max_ssf = -1; secprops.maxbufsize = 4096; plaintext = purple_account_get_bool(account, "auth_plain_in_clear", FALSE); if (!plaintext) secprops.security_flags |= SASL_SEC_NOPLAINTEXT; } else { secprops.max_ssf = 0; secprops.maxbufsize = 0; plaintext = TRUE; } secprops.property_names = 0; secprops.property_values = 0; do { again = FALSE; js->sasl_state = sasl_client_new("xmpp", js->serverFQDN, NULL, NULL, js->sasl_cb, 0, &js->sasl); if (js->sasl_state==SASL_OK) { sasl_setprop(js->sasl, SASL_SEC_PROPS, &secprops); purple_debug_info("sasl", "Mechs found: %s\n", js->sasl_mechs->str); js->sasl_state = sasl_client_start(js->sasl, js->sasl_mechs->str, NULL, &clientout, &coutlen, &js->current_mech); } switch (js->sasl_state) { /* Success */ case SASL_OK: case SASL_CONTINUE: break; case SASL_NOMECH: /* No mechanisms have offered to help */ /* Firstly, if we don't have a password try * to get one */ if (!purple_account_get_password(account)) { purple_account_request_password(account, G_CALLBACK(auth_pass_cb), G_CALLBACK(auth_no_pass_cb), js->gc); return JABBER_SASL_STATE_CONTINUE; /* If we've got a password, but aren't sending * it in plaintext, see if we can turn on * plaintext auth */ /* XXX Should we just check for PLAIN/LOGIN being offered mechanisms? */ } else if (!plaintext) { char *msg = g_strdup_printf(_("%s may require plaintext authentication over an unencrypted connection. Allow this and continue authentication?"), purple_account_get_username(account)); purple_request_yes_no(js->gc, _("Plaintext Authentication"), _("Plaintext Authentication"), msg, 1, account, NULL, NULL, account, allow_cyrus_plaintext_auth, disallow_plaintext_auth); g_free(msg); return JABBER_SASL_STATE_CONTINUE; } else js->auth_fail_count++; if (js->auth_fail_count == 1 && (js->sasl_mechs->str && g_str_equal(js->sasl_mechs->str, "GSSAPI"))) { /* If we tried GSSAPI first, it failed, and it was the only method we had to try, try jabber:iq:auth * for compatibility with iChat 10.5 Server and other jabberd based servers. * * iChat Server 10.5 and certain other corporate servers offer SASL GSSAPI by default, which is often * not configured on the client side, and expects a fallback to jabber:iq:auth when it (predictably) fails. * * Note: xep-0078 points out that using jabber:iq:auth after a sasl failure is wrong. However, * I believe this refers to actual authentication failure, not a simple lack of concordant mechanisms. * Doing otherwise means that simply compiling with SASL support renders the client unable to connect to servers * which would connect without issue otherwise. -evands */ js->auth_mech = NULL; jabber_auth_start_old(js); return JABBER_SASL_STATE_CONTINUE; } break; /* Fatal errors. Give up and go home */ case SASL_BADPARAM: case SASL_NOMEM: *error = g_strdup(_("SASL authentication failed")); break; /* For everything else, fail the mechanism and try again */ default: purple_debug_info("sasl", "sasl_state is %d, failing the mech and trying again\n", js->sasl_state); js->auth_fail_count++; /* * DAA: is this right? * The manpage says that "mech" will contain the chosen mechanism on success. * Presumably, if we get here that isn't the case and we shouldn't try again? * I suspect that this never happens. */ /* * SXW: Yes, this is right. What this handles is the situation where a * mechanism, say GSSAPI, is tried. If that mechanism fails, it may be * due to mechanism specific issues, so we want to try one of the other * supported mechanisms. This code handles that case */ if (js->current_mech && *js->current_mech) { char *pos; if ((pos = strstr(js->sasl_mechs->str, js->current_mech))) { g_string_erase(js->sasl_mechs, pos-js->sasl_mechs->str, strlen(js->current_mech)); } /* Remove space which separated this mech from the next */ if ((js->sasl_mechs->str)[0] == ' ') { g_string_erase(js->sasl_mechs, 0, 1); } again = TRUE; } sasl_dispose(&js->sasl); } } while (again); if (js->sasl_state == SASL_CONTINUE || js->sasl_state == SASL_OK) { xmlnode *auth = xmlnode_new("auth"); xmlnode_set_namespace(auth, NS_XMPP_SASL); xmlnode_set_attrib(auth, "mechanism", js->current_mech); xmlnode_set_attrib(auth, "xmlns:ga", "http://www.google.com/talk/protocol/auth"); xmlnode_set_attrib(auth, "ga:client-uses-full-bind-result", "true"); if (clientout) { if (coutlen == 0) { xmlnode_insert_data(auth, "=", -1); } else { enc_out = purple_base64_encode((unsigned char*)clientout, coutlen); xmlnode_insert_data(auth, enc_out, -1); g_free(enc_out); } } *reply = auth; return JABBER_SASL_STATE_CONTINUE; } else { return JABBER_SASL_STATE_FAIL; } }
static int login(struct backend *s, const char *userid, sasl_callback_t *cb, const char **status, int noauth __attribute__((unused))) { int r = 0; socklen_t addrsize; struct sockaddr_storage saddr_l, saddr_r; char remoteip[60], localip[60]; static struct buf buf = BUF_INITIALIZER; sasl_security_properties_t secprops = { 0, 0xFF, PROT_BUFSIZE, 0, NULL, NULL }; /* default secprops */ const char *mech_conf, *pass, *clientout = NULL; struct auth_scheme_t *scheme = NULL; unsigned need_tls = 0, tls_done = 0, auth_done = 0, clientoutlen; hdrcache_t hdrs = NULL; if (status) *status = NULL; /* set the IP addresses */ addrsize = sizeof(struct sockaddr_storage); if (getpeername(s->sock, (struct sockaddr *) &saddr_r, &addrsize) || iptostring((struct sockaddr *) &saddr_r, addrsize, remoteip, 60)) { if (status) *status = "Failed to get remote IP address"; return SASL_FAIL; } addrsize = sizeof(struct sockaddr_storage); if (getsockname(s->sock, (struct sockaddr *) &saddr_l, &addrsize) || iptostring((struct sockaddr *) &saddr_l, addrsize, localip, 60)) { if (status) *status = "Failed to get local IP address"; return SASL_FAIL; } /* Create callbacks, if necessary */ if (!cb) { buf_setmap(&buf, s->hostname, strcspn(s->hostname, ".")); buf_appendcstr(&buf, "_password"); pass = config_getoverflowstring(buf_cstring(&buf), NULL); if (!pass) pass = config_getstring(IMAPOPT_PROXY_PASSWORD); cb = mysasl_callbacks(NULL, /* userid */ config_getstring(IMAPOPT_PROXY_AUTHNAME), config_getstring(IMAPOPT_PROXY_REALM), pass); s->sasl_cb = cb; } /* Create SASL context */ r = sasl_client_new(s->prot->sasl_service, s->hostname, localip, remoteip, cb, SASL_USAGE_FLAGS, &s->saslconn); if (r != SASL_OK) goto done; r = sasl_setprop(s->saslconn, SASL_SEC_PROPS, &secprops); if (r != SASL_OK) goto done; /* Get SASL mechanism list. We can force a particular mechanism using a <shorthost>_mechs option */ buf_setmap(&buf, s->hostname, strcspn(s->hostname, ".")); buf_appendcstr(&buf, "_mechs"); if (!(mech_conf = config_getoverflowstring(buf_cstring(&buf), NULL))) { mech_conf = config_getstring(IMAPOPT_FORCE_SASL_CLIENT_MECH); } do { unsigned code; const char **hdr, *errstr, *serverin; char base64[BASE64_BUF_SIZE+1]; unsigned int serverinlen; struct body_t resp_body; #ifdef SASL_HTTP_REQUEST sasl_http_request_t httpreq = { "OPTIONS", /* Method */ "*", /* URI */ (u_char *) "", /* Empty body */ 0, /* Zero-length body */ 0 }; /* Persistent cxn? */ #endif /* Base64 encode any client response, if necessary */ if (clientout && scheme && (scheme->flags & AUTH_BASE64)) { r = sasl_encode64(clientout, clientoutlen, base64, BASE64_BUF_SIZE, &clientoutlen); if (r != SASL_OK) break; clientout = base64; } /* Send Authorization and/or Upgrade request to server */ prot_puts(s->out, "OPTIONS * HTTP/1.1\r\n"); prot_printf(s->out, "Host: %s\r\n", s->hostname); prot_printf(s->out, "User-Agent: %s\r\n", buf_cstring(&serverinfo)); if (scheme) { prot_printf(s->out, "Authorization: %s %s\r\n", scheme->name, clientout ? clientout : ""); prot_printf(s->out, "Authorize-As: %s\r\n", userid ? userid : "anonymous"); } else { prot_printf(s->out, "Upgrade: %s\r\n", TLS_VERSION); if (need_tls) { prot_puts(s->out, "Connection: Upgrade\r\n"); need_tls = 0; } prot_puts(s->out, "Authorization: \r\n"); } prot_puts(s->out, "\r\n"); prot_flush(s->out); serverin = clientout = NULL; serverinlen = clientoutlen = 0; /* Read response(s) from backend until final response or error */ do { resp_body.flags = BODY_DISCARD; r = http_read_response(s, METH_OPTIONS, &code, NULL, &hdrs, &resp_body, &errstr); if (r) { if (status) *status = errstr; break; } if (code == 101) { /* Switching Protocols */ if (tls_done) { r = HTTP_BAD_GATEWAY; if (status) *status = "TLS already active"; break; } else if (backend_starttls(s, NULL, NULL, NULL)) { r = HTTP_SERVER_ERROR; if (status) *status = "Unable to start TLS"; break; } else tls_done = 1; } } while (code < 200); switch (code) { default: /* Failure */ if (!r) { r = HTTP_BAD_GATEWAY; if (status) { buf_reset(&buf); buf_printf(&buf, "Unexpected status code from backend: %u", code); *status = buf_cstring(&buf); } } break; case 426: /* Upgrade Required */ if (tls_done) { r = HTTP_BAD_GATEWAY; if (status) *status = "TLS already active"; } else need_tls = 1; break; case 200: /* OK */ if (scheme->recv_success && (serverin = scheme->recv_success(hdrs))) { /* Process success data */ serverinlen = strlen(serverin); goto challenge; } break; case 401: /* Unauthorized */ if (auth_done) { r = SASL_BADAUTH; break; } if (!serverin) { int i = 0; hdr = spool_getheader(hdrs, "WWW-Authenticate"); if (!scheme) { unsigned avail_auth_schemes = 0; const char *mech = NULL; size_t len; /* Compare authentication schemes offered in * WWW-Authenticate header(s) to what we support */ buf_reset(&buf); for (i = 0; hdr && hdr[i]; i++) { len = strcspn(hdr[i], " "); for (scheme = auth_schemes; scheme->name; scheme++) { if (!strncmp(scheme->name, hdr[i], len) && !((scheme->flags & AUTH_NEED_PERSIST) && (resp_body.flags & BODY_CLOSE))) { /* Tag the scheme as available */ avail_auth_schemes |= (1 << scheme->idx); /* Add SASL-based schemes to SASL mech list */ if (scheme->saslmech) { if (buf_len(&buf)) buf_putc(&buf, ' '); buf_appendcstr(&buf, scheme->saslmech); } break; } } } /* If we have a mech_conf, use it */ if (mech_conf && buf_len(&buf)) { char *conf = xstrdup(mech_conf); char *newmechlist = intersect_mechlists(conf, (char *) buf_cstring(&buf)); if (newmechlist) { buf_setcstr(&buf, newmechlist); free(newmechlist); } else { syslog(LOG_DEBUG, "%s did not offer %s", s->hostname, mech_conf); buf_reset(&buf); } free(conf); } #ifdef SASL_HTTP_REQUEST /* Set HTTP request as specified above (REQUIRED) */ httpreq.non_persist = (resp_body.flags & BODY_CLOSE); sasl_setprop(s->saslconn, SASL_HTTP_REQUEST, &httpreq); #endif /* Try to start SASL exchange using available mechs */ r = sasl_client_start(s->saslconn, buf_cstring(&buf), NULL, /* no prompts */ NULL, NULL, /* no initial resp */ &mech); if (mech) { /* Find auth scheme associated with chosen SASL mech */ for (scheme = auth_schemes; scheme->name; scheme++) { if (scheme->saslmech && !strcmp(scheme->saslmech, mech)) break; } } else { /* No matching SASL mechs - try Basic */ scheme = &auth_schemes[AUTH_BASIC]; if (!(avail_auth_schemes & (1 << scheme->idx))) { need_tls = !tls_done; break; /* case 401 */ } } /* Find the associated WWW-Authenticate header */ for (i = 0; hdr && hdr[i]; i++) { len = strcspn(hdr[i], " "); if (!strncmp(scheme->name, hdr[i], len)) break; } } /* Get server challenge, if any */ if (hdr) { const char *p = strchr(hdr[i], ' '); serverin = p ? ++p : ""; serverinlen = strlen(serverin); } } challenge: if (serverin) { /* Perform the next step in the auth exchange */ if (scheme->idx == AUTH_BASIC) { /* Don't care about "realm" in server challenge */ const char *authid = callback_getdata(s->saslconn, cb, SASL_CB_AUTHNAME); pass = callback_getdata(s->saslconn, cb, SASL_CB_PASS); buf_reset(&buf); buf_printf(&buf, "%s:%s", authid, pass); clientout = buf_cstring(&buf); clientoutlen = buf_len(&buf); auth_done = 1; } else { /* Base64 decode any server challenge, if necessary */ if (serverin && (scheme->flags & AUTH_BASE64)) { r = sasl_decode64(serverin, serverinlen, base64, BASE64_BUF_SIZE, &serverinlen); if (r != SASL_OK) break; /* case 401 */ serverin = base64; } /* SASL mech (Digest, Negotiate, NTLM) */ r = sasl_client_step(s->saslconn, serverin, serverinlen, NULL, /* no prompts */ &clientout, &clientoutlen); if (r == SASL_OK) auth_done = 1; } } break; /* case 401 */ } } while (need_tls || clientout); done: if (hdrs) spool_free_hdrcache(hdrs); if (r && status && !*status) *status = sasl_errstring(r, NULL, NULL); return r; }
static bool _mongoc_cyrus_start (mongoc_cyrus_t *sasl, uint8_t *outbuf, uint32_t outbufmax, uint32_t *outbuflen, bson_error_t *error) { const char *service_name = "mongodb"; const char *service_host = ""; const char *mechanism = NULL; const char *raw = NULL; unsigned raw_len = 0; int status; BSON_ASSERT (sasl); BSON_ASSERT (outbuf); BSON_ASSERT (outbufmax); BSON_ASSERT (outbuflen); if (sasl->credentials.service_name) { service_name = sasl->credentials.service_name; } if (sasl->credentials.service_host) { service_host = sasl->credentials.service_host; } status = sasl_client_new ( service_name, service_host, NULL, NULL, sasl->callbacks, 0, &sasl->conn); TRACE ("Created new sasl client %s", status == SASL_OK ? "successfully" : "UNSUCCESSFULLY"); if (_mongoc_cyrus_is_failure (status, error)) { return false; } status = sasl_client_start (sasl->conn, sasl->credentials.mechanism, &sasl->interact, &raw, &raw_len, &mechanism); TRACE ("Started the sasl client %s", status == SASL_CONTINUE ? "successfully" : "UNSUCCESSFULLY"); if (_mongoc_cyrus_is_failure (status, error)) { return false; } if ((0 != strcasecmp (mechanism, "GSSAPI")) && (0 != strcasecmp (mechanism, "PLAIN"))) { bson_set_error (error, MONGOC_ERROR_SASL, SASL_NOMECH, "SASL Failure: invalid mechanism \"%s\"", mechanism); return false; } status = sasl_encode64 (raw, raw_len, (char *) outbuf, outbufmax, outbuflen); if (_mongoc_cyrus_is_failure (status, error)) { return false; } return true; }
static int backend_authenticate(struct backend *s, const char *userid, sasl_callback_t *cb, const char **status) { struct protocol_t *prot = s->prot; int r; char *mechlist; sasl_security_properties_t secprops = { 0, 0xFF, PROT_BUFSIZE, 0, NULL, NULL }; /* default secprops */ struct sockaddr_storage saddr_l, saddr_r; char remoteip[60], localip[60]; socklen_t addrsize; char buf[2048], optstr[128], *p; const char *mech_conf, *pass; if (prot->type != TYPE_STD) return SASL_FAIL; /* set the IP addresses */ addrsize = sizeof(struct sockaddr_storage); if (getpeername(s->sock, (struct sockaddr *)&saddr_r, &addrsize) != 0) return SASL_FAIL; if (iptostring((struct sockaddr *)&saddr_r, addrsize, remoteip, 60) != 0) return SASL_FAIL; addrsize = sizeof(struct sockaddr_storage); if (getsockname(s->sock, (struct sockaddr *)&saddr_l, &addrsize)!=0) return SASL_FAIL; if (iptostring((struct sockaddr *)&saddr_l, addrsize, localip, 60) != 0) return SASL_FAIL; if (!cb) { strlcpy(optstr, s->hostname, sizeof(optstr)); p = strchr(optstr, '.'); if (p) *p = '\0'; strlcat(optstr, "_password", sizeof(optstr)); pass = config_getoverflowstring(optstr, NULL); if(!pass) pass = config_getstring(IMAPOPT_PROXY_PASSWORD); cb = mysasl_callbacks(userid, config_getstring(IMAPOPT_PROXY_AUTHNAME), config_getstring(IMAPOPT_PROXY_REALM), pass); s->sasl_cb = cb; } /* Require proxying if we have an "interesting" userid (authzid) */ r = sasl_client_new(prot->sasl_service, s->hostname, localip, remoteip, cb, (userid && *userid ? SASL_NEED_PROXY : 0) | (prot->u.std.sasl_cmd.parse_success ? SASL_SUCCESS_DATA : 0), &s->saslconn); if (r != SASL_OK) return r; r = sasl_setprop(s->saslconn, SASL_SEC_PROPS, &secprops); if (r != SASL_OK) return r; /* Get SASL mechanism list. We can force a particular mechanism using a <shorthost>_mechs option */ strcpy(buf, s->hostname); p = strchr(buf, '.'); if (p) *p = '\0'; strcat(buf, "_mechs"); mech_conf = config_getoverflowstring(buf, NULL); if (!mech_conf) mech_conf = config_getstring(IMAPOPT_FORCE_SASL_CLIENT_MECH); #ifdef HAVE_SSL strlcpy(optstr, s->hostname, sizeof(optstr)); p = strchr(optstr, '.'); if (p) *p = '\0'; strlcat(optstr, "_client_cert", sizeof(optstr)); const char *c_cert_file = config_getoverflowstring(optstr, NULL); if (!c_cert_file) { c_cert_file = config_getstring(IMAPOPT_TLS_CLIENT_CERT); } strlcpy(optstr, s->hostname, sizeof(optstr)); p = strchr(optstr, '.'); if (p) *p = '\0'; strlcat(optstr, "_client_key", sizeof(optstr)); const char *c_key_file = config_getoverflowstring(optstr, NULL); if (!c_key_file) { c_key_file = config_getstring(IMAPOPT_TLS_CLIENT_KEY); } #else const char *c_cert_file = NULL; const char *c_key_file = NULL; #endif mechlist = backend_get_cap_params(s, CAPA_AUTH); do { /* If we have a mech_conf, use it */ if (mech_conf && mechlist) { char *conf = xstrdup(mech_conf); char *newmechlist = intersect_mechlists( conf, mechlist ); if ( newmechlist == NULL ) { syslog( LOG_INFO, "%s did not offer %s", s->hostname, mech_conf ); } free(conf); free(mechlist); mechlist = newmechlist; } if (mechlist) { /* we now do the actual SASL exchange */ saslclient(s->saslconn, &prot->u.std.sasl_cmd, mechlist, s->in, s->out, &r, status); /* garbage collect */ free(mechlist); mechlist = NULL; } else r = SASL_NOMECH; /* If we don't have a usable mech, do TLS and try again */ } while (r == SASL_NOMECH && CAPA(s, CAPA_STARTTLS) && backend_starttls(s, &prot->u.std.tls_cmd, c_cert_file, c_key_file) != -1 && (mechlist = backend_get_cap_params(s, CAPA_AUTH))); if (r == SASL_OK) { prot_setsasl(s->in, s->saslconn); prot_setsasl(s->out, s->saslconn); } if (mechlist) free(mechlist); return SASL_OK; }
/* mutt_sasl_client_new: wrapper for sasl_client_new which also sets various * security properties. If this turns out to be fine for POP too we can * probably stop exporting mutt_sasl_get_callbacks(). */ int mutt_sasl_client_new (CONNECTION* conn, sasl_conn_t** saslconn) { sasl_security_properties_t secprops; struct sockaddr_storage local, remote; socklen_t size; char iplocalport[IP_PORT_BUFLEN], ipremoteport[IP_PORT_BUFLEN]; char* plp = NULL; char* prp = NULL; const char* service; int rc; if (mutt_sasl_start () != SASL_OK) return -1; switch (conn->account.type) { case M_ACCT_TYPE_IMAP: service = "imap"; break; case M_ACCT_TYPE_POP: service = "pop"; break; case M_ACCT_TYPE_SMTP: service = "smtp"; break; default: mutt_error (_("Unknown SASL profile")); return -1; } size = sizeof (local); if (!getsockname (conn->fd, (struct sockaddr *)&local, &size)) { if (iptostring((struct sockaddr *)&local, size, iplocalport, IP_PORT_BUFLEN) == SASL_OK) plp = iplocalport; else dprint (2, (debugfile, "SASL failed to parse local IP address\n")); } else dprint (2, (debugfile, "SASL failed to get local IP address\n")); size = sizeof (remote); if (!getpeername (conn->fd, (struct sockaddr *)&remote, &size)){ if (iptostring((struct sockaddr *)&remote, size, ipremoteport, IP_PORT_BUFLEN) == SASL_OK) prp = ipremoteport; else dprint (2, (debugfile, "SASL failed to parse remote IP address\n")); } else dprint (2, (debugfile, "SASL failed to get remote IP address\n")); dprint (2, (debugfile, "SASL local ip: %s, remote ip:%s\n", NONULL(plp), NONULL(prp))); rc = sasl_client_new (service, conn->account.host, plp, prp, mutt_sasl_get_callbacks (&conn->account), 0, saslconn); if (rc != SASL_OK) { mutt_error (_("Error allocating SASL connection")); mutt_sleep (2); return -1; } memset (&secprops, 0, sizeof (secprops)); /* Work around a casting bug in the SASL krb4 module */ secprops.max_ssf = 0x7fff; secprops.maxbufsize = M_SASL_MAXBUF; if (sasl_setprop (*saslconn, SASL_SEC_PROPS, &secprops) != SASL_OK) { mutt_error (_("Error setting SASL security properties")); return -1; } if (conn->ssf) { /* I'm not sure this actually has an effect, at least with SASLv2 */ dprint (2, (debugfile, "External SSF: %d\n", conn->ssf)); if (sasl_setprop (*saslconn, SASL_SSF_EXTERNAL, &(conn->ssf)) != SASL_OK) { mutt_error (_("Error setting SASL external security strength")); return -1; } } if (conn->account.user[0]) { dprint (2, (debugfile, "External authentication name: %s\n", conn->account.user)); if (sasl_setprop (*saslconn, SASL_AUTH_EXTERNAL, conn->account.user) != SASL_OK) { mutt_error (_("Error setting SASL external user name")); return -1; } } return 0; }
/* * Initialize SASL and set necessary options */ int init_sasl(isieve_t *obj, int ssf, sasl_callback_t *callbacks) { static int sasl_started = 0; int saslresult = SASL_OK; sasl_security_properties_t *secprops=NULL; socklen_t addrsize=sizeof(struct sockaddr_storage); struct sockaddr_storage saddr_l, saddr_r; char localip[60], remoteip[60]; /* attempt to start sasl */ if(!sasl_started) { saslresult=sasl_client_init(NULL); obj->conn = NULL; sasl_started = 1; } /* Save the callbacks array */ obj->callbacks = callbacks; if (saslresult!=SASL_OK) return -1; addrsize=sizeof(struct sockaddr_storage); if (getpeername(obj->sock,(struct sockaddr *)&saddr_r,&addrsize)!=0) return -1; addrsize=sizeof(struct sockaddr_storage); if (getsockname(obj->sock,(struct sockaddr *)&saddr_l,&addrsize)!=0) return -1; #if 0 /* XXX The following line causes problems with KERBEROS_V4 decoding. * We're not sure why its an issue, but this code isn't used in any of * our other client code (imtest.c, backend.c), so we're removing it. */ /* set the port manually since getsockname is stupid and doesn't */ ((struct sockaddr_in *)&saddr_l)->sin_port = htons(obj->port); #endif if (iptostring((struct sockaddr *)&saddr_r, addrsize, remoteip, 60)) return -1; if (iptostring((struct sockaddr *)&saddr_l, addrsize, localip, 60)) return -1; if(obj->conn) sasl_dispose(&obj->conn); /* client new connection */ saslresult=sasl_client_new(SIEVE_SERVICE_NAME, obj->serverFQDN, localip, remoteip, callbacks, SASL_SUCCESS_DATA, &obj->conn); if (saslresult!=SASL_OK) return -1; /* create a security structure and give it to sasl */ secprops = make_secprops(0, ssf); if (secprops != NULL) { sasl_setprop(obj->conn, SASL_SEC_PROPS, secprops); free(secprops); } return 0; }
/* mutt_sasl_client_new: wrapper for sasl_client_new which also sets various * security properties. If this turns out to be fine for POP too we can * probably stop exporting mutt_sasl_get_callbacks(). */ int mutt_sasl_client_new (CONNECTION * conn, sasl_conn_t ** saslconn) { sasl_security_properties_t secprops; struct sockaddr_storage local, remote; socklen_t size; char iplocalport[IP_PORT_BUFLEN], ipremoteport[IP_PORT_BUFLEN]; const char *service; int rc; if (mutt_sasl_start () != SASL_OK) return -1; switch (conn->account.type) { case M_ACCT_TYPE_IMAP: service = "imap"; break; case M_ACCT_TYPE_POP: service = "pop"; break; default: debug_print (1, ("account type unset\n")); return -1; } size = sizeof (local); if (getsockname (conn->fd, (struct sockaddr *) &local, &size)) { debug_print (1, ("getsockname for local failed\n")); return -1; } else if (iptostring ((struct sockaddr *) &local, size, iplocalport, IP_PORT_BUFLEN) != SASL_OK) { debug_print (1, ("iptostring for local failed\n")); return -1; } size = sizeof (remote); if (getpeername (conn->fd, (struct sockaddr *) &remote, &size)) { debug_print (1, ("getsockname for remote failed\n")); return -1; } else if (iptostring ((struct sockaddr *) &remote, size, ipremoteport, IP_PORT_BUFLEN) != SASL_OK) { debug_print (1, ("iptostring for remote failed\n")); return -1; } debug_print (1, ("local ip: %s, remote ip:%s\n", iplocalport, ipremoteport)); rc = sasl_client_new (service, conn->account.host, iplocalport, ipremoteport, mutt_sasl_get_callbacks (&conn->account), 0, saslconn); if (rc != SASL_OK) { debug_print (1, ("Error allocating SASL connection\n")); return -1; } /*** set sasl IP properties, necessary for use with krb4 ***/ /* Do we need to fail if this fails? I would assume having these unset * would just disable KRB4. Who wrote this code? */ { struct sockaddr_in local, remote; socklen_t size; size = sizeof (local); if (getsockname (conn->fd, (struct sockaddr *) &local, &size)) return -1; size = sizeof (remote); if (getpeername (conn->fd, (struct sockaddr *) &remote, &size)) return -1; #ifdef SASL_IP_LOCAL if (sasl_setprop (*saslconn, SASL_IP_LOCAL, &local) != SASL_OK) { debug_print (1, ("Error setting local IP address\n")); return -1; } #endif #ifdef SASL_IP_REMOTE if (sasl_setprop (*saslconn, SASL_IP_REMOTE, &remote) != SASL_OK) { debug_print (1, ("Error setting remote IP address\n")); return -1; } #endif } /* set security properties. We use NOPLAINTEXT globally, since we can * just fall back to LOGIN in the IMAP case anyway. If that doesn't * work for POP, we can make it a flag or move this code into * imap/auth_sasl.c */ memset (&secprops, 0, sizeof (secprops)); /* Work around a casting bug in the SASL krb4 module */ secprops.max_ssf = 0x7fff; secprops.maxbufsize = M_SASL_MAXBUF; secprops.security_flags |= SASL_SEC_NOPLAINTEXT; if (sasl_setprop (*saslconn, SASL_SEC_PROPS, &secprops) != SASL_OK) { debug_print (1, ("Error setting security properties\n")); return -1; } if (conn->ssf) { debug_print (2, ("External SSF: %d\n", conn->ssf)); if (sasl_setprop (*saslconn, SASL_SSF_EXTERNAL, &(conn->ssf)) != SASL_OK) { debug_print (1, ("Error setting external properties\n")); return -1; } debug_print (2, ("External authentication name: %s\n", conn->account.user)); if (sasl_setprop (*saslconn, SASL_AUTH_EXTERNAL, conn->account.user) != SASL_OK) { debug_print (1, ("Error setting external properties\n")); return -1; } } return 0; }
int mailpop3_auth(mailpop3 * f, const char * auth_type, const char * server_fqdn, const char * local_ip_port, const char * remote_ip_port, const char * login, const char * auth_name, const char * password, const char * realm) { #ifdef USE_SASL int r; char command[POP3_STRING_SIZE]; sasl_callback_t sasl_callback[5]; const char * sasl_out; unsigned sasl_out_len; const char * mechusing; sasl_secret_t * secret; int res; size_t len; char * encoded; unsigned int encoded_len; unsigned int max_encoded; sasl_callback[0].id = SASL_CB_GETREALM; sasl_callback[0].proc = sasl_getrealm; sasl_callback[0].context = f; sasl_callback[1].id = SASL_CB_USER; sasl_callback[1].proc = sasl_getsimple; sasl_callback[1].context = f; sasl_callback[2].id = SASL_CB_AUTHNAME; sasl_callback[2].proc = sasl_getsimple; sasl_callback[2].context = f; sasl_callback[3].id = SASL_CB_PASS; sasl_callback[3].proc = sasl_getsecret; sasl_callback[3].context = f; sasl_callback[4].id = SASL_CB_LIST_END; sasl_callback[4].proc = NULL; sasl_callback[4].context = NULL; len = strlen(password); secret = malloc(sizeof(* secret) + len); if (secret == NULL) { res = MAILPOP3_ERROR_MEMORY; goto err; } secret->len = len; memcpy(secret->data, password, len + 1); f->pop3_sasl.sasl_server_fqdn = server_fqdn; f->pop3_sasl.sasl_login = login; f->pop3_sasl.sasl_auth_name = auth_name; f->pop3_sasl.sasl_password = password; f->pop3_sasl.sasl_realm = realm; f->pop3_sasl.sasl_secret = secret; /* init SASL */ if (f->pop3_sasl.sasl_conn != NULL) { sasl_dispose((sasl_conn_t **) &f->pop3_sasl.sasl_conn); f->pop3_sasl.sasl_conn = NULL; } else { mailsasl_ref(); } r = sasl_client_new("pop", server_fqdn, local_ip_port, remote_ip_port, sasl_callback, 0, (sasl_conn_t **) &f->pop3_sasl.sasl_conn); if (r != SASL_OK) { res = MAILPOP3_ERROR_BAD_USER; goto free_secret; } r = sasl_client_start(f->pop3_sasl.sasl_conn, auth_type, NULL, &sasl_out, &sasl_out_len, &mechusing); if ((r != SASL_CONTINUE) && (r != SASL_OK)) { res = MAILPOP3_ERROR_BAD_USER; goto free_sasl_conn; } snprintf(command, POP3_STRING_SIZE, "AUTH %s\r\n", auth_type); r = send_command(f, command); if (r == -1) { res = MAILPOP3_ERROR_STREAM; goto free_sasl_conn; } while (1) { char * response; response = read_line(f); r = parse_auth(f, response); switch (r) { case RESPONSE_OK: f->pop3_state = POP3_STATE_TRANSACTION; res = MAILPOP3_NO_ERROR; goto free_sasl_conn; case RESPONSE_ERR: res = MAILPOP3_ERROR_BAD_USER; goto free_sasl_conn; case RESPONSE_AUTH_CONT: { size_t response_len; char * decoded; unsigned int decoded_len; unsigned int max_decoded; int got_response; got_response = 1; if (* f->pop3_response == '\0') got_response = 0; if (got_response) { char * p; p = strchr(f->pop3_response, '\r'); if (p != NULL) { * p = '\0'; } p = strchr(f->pop3_response, '\n'); if (p != NULL) { * p = '\0'; } response_len = strlen(f->pop3_response); max_decoded = response_len * 3 / 4; decoded = malloc(max_decoded + 1); if (decoded == NULL) { res = MAILPOP3_ERROR_MEMORY; goto free_sasl_conn; } r = sasl_decode64(f->pop3_response, response_len, decoded, max_decoded + 1, &decoded_len); if (r != SASL_OK) { free(decoded); res = MAILPOP3_ERROR_MEMORY; goto free_sasl_conn; } r = sasl_client_step(f->pop3_sasl.sasl_conn, decoded, decoded_len, NULL, &sasl_out, &sasl_out_len); free(decoded); if ((r != SASL_CONTINUE) && (r != SASL_OK)) { res = MAILPOP3_ERROR_BAD_USER; goto free_sasl_conn; } } max_encoded = ((sasl_out_len + 2) / 3) * 4; encoded = malloc(max_encoded + 1); if (encoded == NULL) { res = MAILPOP3_ERROR_MEMORY; goto free_sasl_conn; } r = sasl_encode64(sasl_out, sasl_out_len, encoded, max_encoded + 1, &encoded_len); if (r != SASL_OK) { free(encoded); res = MAILPOP3_ERROR_MEMORY; goto free_sasl_conn; } snprintf(command, POP3_STRING_SIZE, "%s\r\n", encoded); r = send_command(f, command); free(encoded); if (r == -1) { res = MAILPOP3_ERROR_STREAM; goto free_sasl_conn; } } break; } } f->pop3_state = POP3_STATE_TRANSACTION; res = MAILPOP3_NO_ERROR; free_sasl_conn: sasl_dispose((sasl_conn_t **) &f->pop3_sasl.sasl_conn); f->pop3_sasl.sasl_conn = NULL; mailsasl_unref(); free_secret: free(f->pop3_sasl.sasl_secret); f->pop3_sasl.sasl_secret = NULL; err: return res; #else return MAILPOP3_ERROR_BAD_USER; #endif }
/** * 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 } }; /* Verify broker support: * - RD_KAFKA_FEATURE_SASL_GSSAPI - GSSAPI supported * - RD_KAFKA_FEATURE_SASL_HANDSHAKE - GSSAPI, PLAIN and possibly * other mechanisms supported. */ if (!strcmp(rk->rk_conf.sasl.mechanisms, "GSSAPI")) { if (!(rkb->rkb_features & RD_KAFKA_FEATURE_SASL_GSSAPI)) { rd_snprintf(errstr, errstr_size, "SASL GSSAPI authentication not supported " "by broker"); return -1; } } else if (!(rkb->rkb_features & RD_KAFKA_FEATURE_SASL_HANDSHAKE)) { rd_snprintf(errstr, errstr_size, "SASL Handshake not supported by broker " "(required by mechanism %s)", rk->rk_conf.sasl.mechanisms); return -1; } /* SASL_CB_USER is needed for PLAIN but breaks GSSAPI */ if (strcmp(rk->rk_conf.sasl.mechanisms, "GSSAPI")) { 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; callbacks[endidx].context = rktrans; 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; }