* options. If \b any of the incoming Authorization headers result in successful
* authentication, then authentication is considered successful.
*
+ * \warning The return code from the function is used by the distributor to
+ * determine which log messages (if any) are emitted. Many admins will be
+ * using log parsers like fail2ban to block IPs that are repeatedly failing
+ * to authenticate so changing the return code could have unintended
+ * consequences.
+ *
+ * \retval AST_SIP_AUTHENTICATION_SUCCESS There was an Authorization header
+ * in the request and it verified successfully with at least one auth object
+ * on the endpoint. No further challenges sent.
+ *
+ * \retval AST_SIP_AUTHENTICATION_CHALLENGE There was NO Authorization header
+ * in the incoming request. We sent a 401 with one or more challenges.
+ *
+ * \retval AST_SIP_AUTHENTICATION_FAILED There were one or more Authorization
+ * headers in the request but they all failed to verify with any auth object
+ * on the endpoint. We sent a 401 with one or more challenges.
+ *
+ * \retval AST_SIP_AUTHENTICATION_ERROR An internal error occurred. No challenges
+ * were sent.
+ *
* \see ast_sip_check_authentication
*/
static enum ast_sip_check_auth_result digest_check_auth(struct ast_sip_endpoint *endpoint,
}
/*
+ * Verify any Authorization headers in the incoming request against the
+ * auth objects on the endpoint. If there aren't any Authorization headers
+ * verify() will return AUTH_NOAUTH.
+ *
* NOTE: The only reason to use multiple auth objects as a UAS might
* be to send challenges for multiple realms however we currently don't
* know of anyone actually doing this.
*/
for (idx = 0; idx < auth_size; ++idx) {
- int i = 0;
struct ast_sip_auth *auth = auths[idx];
- const char *realm = S_OR(auth->realm, default_realm);
const char *auth_id = ast_sorcery_object_get_id(auth);
- SCOPE_ENTER(4, "%s:%s:%s: Verifying\n", endpoint_id, auth_id, src_name);
+ SCOPE_ENTER(4, "%s:%s:%s: Auth %d of %d: Verifying\n",
+ endpoint_id, auth_id, src_name, idx + 1, (int)auth_size);
+
+ verify_res[idx] = SCOPE_CALL_WITH_RESULT(-1, int, verify, endpoint_id, auth, rdata, tdata->pool);
+ switch((int)verify_res[idx]) {
+ case AUTH_SUCCESS:
+ res = AST_SIP_AUTHENTICATION_SUCCESS;
+ break;
+ case AUTH_FAIL:
+ failures++;
+ break;
+ case AUTH_NOAUTH:
+ case AUTH_STALE:
+ break;
+ }
+
+ SCOPE_EXIT("%s:%s:%s: Auth %d of %d: Result: %s Failure count: %d\n",
+ endpoint_id, auth_id, src_name, idx + 1, (int)auth_size,
+ verify_result_str[verify_res[idx]], failures);
/*
- * Artificial auth objects are used for the purpose of
- * sending challenges. We don't need to verify them.
+ * If there was a success or there was no Authorization header in the
+ * incoming request, we can stop verifying the rest of the auth objects.
*/
- if (auth->type == AST_SIP_AUTH_TYPE_ARTIFICIAL) {
- ast_trace(-1, "%s:%s:%s: Skipping verification on artificial endpoint\n", endpoint_id, auth_id, src_name);
- verify_res[idx] = AUTH_NOAUTH;
- } else {
- verify_res[idx] = SCOPE_CALL_WITH_RESULT(-1, int, verify, endpoint_id, auth, rdata, tdata->pool);
- if (verify_res[idx] == AUTH_SUCCESS) {
- res = AST_SIP_AUTHENTICATION_SUCCESS;
- SCOPE_EXIT_EXPR(break, "%s:%s:%s: success\n", endpoint_id, auth_id, src_name);
- }
- if (verify_res[idx] == AUTH_FAIL) {
- ast_trace(-1, "%s:%s:%s: fail\n", endpoint_id, auth_id, src_name);
- failures++;
- }
+ if (verify_res[idx] == AUTH_SUCCESS || verify_res[idx] == AUTH_NOAUTH) {
+ break;
}
+ }
+
+ if (res == AST_SIP_AUTHENTICATION_SUCCESS) {
+ ast_sip_cleanup_auths(auths, auth_size);
+ SCOPE_EXIT_RTN_VALUE(res, "%s:%s: Result: %s\n",
+ endpoint_id, src_name,
+ check_auth_result_str[res]);
+ }
+ ast_trace(-1, "%s:%s: Done with verification. Failures: %d of %d\n",
+ endpoint_id, src_name, failures, (int)auth_size);
+
+ /*
+ * If none of the Authorization headers in the incoming request were
+ * successfully verified, or there were no Authorization headers in the
+ * request, we need to send challenges for each auth object
+ * on the endpoint.
+ */
+ for (idx = 0; idx < auth_size; ++idx) {
+ int i = 0;
+ struct ast_sip_auth *auth = auths[idx];
+ const char *realm = S_OR(auth->realm, default_realm);
+ const char *auth_id = ast_sorcery_object_get_id(auth);
+ SCOPE_ENTER(4, "%s:%s:%s: Auth %d of %d: Sending challenges\n",
+ endpoint_id, auth_id, src_name, idx + 1, (int)auth_size);
for (i = 0; i < AST_VECTOR_SIZE(&auth->supported_algorithms_uas); i++) {
pjsip_auth_algorithm_type algorithm_type = AST_VECTOR_GET(&auth->supported_algorithms_uas, i);
const pjsip_auth_algorithm *algorithm = ast_sip_auth_get_algorithm_by_type(algorithm_type);
pjsip_www_authenticate_hdr *auth_hdr = NULL;
int already_sent_challenge = 0;
- SCOPE_ENTER(5, "%s:%s:%s: Challenging with " PJSTR_PRINTF_SPEC "\n",
- endpoint_id, auth_id, src_name, PJSTR_PRINTF_VAR(algorithm->iana_name));
+ SCOPE_ENTER(5, "%s:%s:%s: Auth %d of %d: Challenging with " PJSTR_PRINTF_SPEC "\n",
+ endpoint_id, auth_id, src_name, idx + 1, (int)auth_size,
+ PJSTR_PRINTF_VAR(algorithm->iana_name));
/*
* Per RFC 7616, if we've already sent a challenge for this realm
PJSIP_H_WWW_AUTHENTICATE, auth_hdr ? auth_hdr->next : NULL))) {
if (pj_strcmp2(&auth_hdr->challenge.common.realm, realm) == 0 &&
!pj_stricmp(&auth_hdr->challenge.digest.algorithm, &algorithm->iana_name)) {
- ast_trace(-1, "%s:%s:%s: Not sending duplicate challenge for realm: %s algorithm: "
+ ast_trace(-1, "%s:%s:%s: Auth %d of %d: Not sending duplicate challenge for realm: %s algorithm: "
PJSTR_PRINTF_SPEC "\n",
- endpoint_id, auth_id, src_name, realm, PJSTR_PRINTF_VAR(algorithm->iana_name));
+ endpoint_id, auth_id, src_name, idx + 1, (int)auth_size,
+ realm, PJSTR_PRINTF_VAR(algorithm->iana_name));
already_sent_challenge = 1;
}
}
verify_res[idx] == AUTH_STALE, algorithm);
res = AST_SIP_AUTHENTICATION_CHALLENGE;
- SCOPE_EXIT("%s:%s:%s: Challenged with " PJSTR_PRINTF_SPEC "\n",
- endpoint_id, auth_id, src_name, PJSTR_PRINTF_VAR(algorithm->iana_name));
+ SCOPE_EXIT("%s:%s:%s: Auth %d of %d: Challenged with " PJSTR_PRINTF_SPEC "\n",
+ endpoint_id, auth_id, src_name, idx + 1, (int)auth_size,
+ PJSTR_PRINTF_VAR(algorithm->iana_name));
}
- SCOPE_EXIT("%s:%s:%s: Done with auth challenge\n", endpoint_id, auth_id, src_name);
+ SCOPE_EXIT("%s:%s:%s: Auth %d of %d: Done with challenges\n",
+ endpoint_id, auth_id, src_name, idx + 1, (int)auth_size);
}
/*