23 #include "asterisk/res_pjsip.h"
35 static char default_realm[AST_SIP_AUTH_MAX_REALM_LENGTH + 1];
45 static int digest_requires_authentication(
struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
52 static void auth_store_cleanup(
void *data)
107 static int remove_auth(
void)
149 static pj_status_t digest_lookup(pj_pool_t *
pool,
const pj_str_t *
realm,
150 const pj_str_t *acc_name, pjsip_cred_info *info)
156 return PJSIP_SC_FORBIDDEN;
159 if (auth->
type == AST_SIP_AUTH_TYPE_ARTIFICIAL) {
160 return PJSIP_SC_FORBIDDEN;
163 if (pj_strcmp2(realm, auth->
realm)) {
164 return PJSIP_SC_FORBIDDEN;
166 if (pj_strcmp2(acc_name, auth->
auth_user)) {
167 return PJSIP_SC_FORBIDDEN;
170 pj_strdup2(pool, &info->realm, auth->
realm);
171 pj_strdup2(pool, &info->username, auth->
auth_user);
173 switch (auth->
type) {
174 case AST_SIP_AUTH_TYPE_USER_PASS:
175 pj_strdup2(pool, &info->data, auth->
auth_pass);
176 info->data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
178 case AST_SIP_AUTH_TYPE_MD5:
179 pj_strdup2(pool, &info->data, auth->
md5_creds);
180 info->data_type = PJSIP_CRED_DATA_DIGEST;
183 return PJSIP_SC_FORBIDDEN;
205 static int build_nonce(
struct ast_str **nonce,
const char *timestamp,
const pjsip_rx_data *rdata,
const char *realm)
207 struct ast_str *str = ast_str_alloca(256);
237 static int check_nonce(
const char *candidate,
const pjsip_rx_data *rdata,
const struct ast_sip_auth *auth)
240 char *timestamp = strsep(©,
"/");
242 time_t now = time(NULL);
243 struct ast_str *calculated = ast_str_alloca(64);
250 if (sscanf(timestamp,
"%30d", ×tamp_int) != 1) {
258 build_nonce(&calculated, timestamp, rdata, auth->
realm);
266 static int find_challenge(
const pjsip_rx_data *rdata,
const struct ast_sip_auth *auth)
268 struct pjsip_authorization_hdr *auth_hdr = (pjsip_authorization_hdr *) &rdata->msg_info.msg->hdr;
269 int challenge_found = 0;
272 while ((auth_hdr = (pjsip_authorization_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_AUTHORIZATION, auth_hdr->next))) {
273 ast_copy_pj_str(nonce, &auth_hdr->credential.digest.nonce,
sizeof(nonce));
274 if (check_nonce(nonce, rdata, auth) && !pj_strcmp2(&auth_hdr->credential.digest.realm, auth->
realm)) {
280 return challenge_found;
286 static void setup_auth_srv(pj_pool_t *pool, pjsip_auth_srv *auth_server,
const char *realm)
289 pj_cstr(&realm_str, realm);
291 pjsip_auth_srv_init(pool, auth_server, &realm_str, digest_lookup, 0);
297 enum digest_verify_result {
308 static char *verify_result_str[] = {
323 static int verify(
const struct ast_sip_auth *auth, pjsip_rx_data *rdata, pj_pool_t *pool)
327 pjsip_auth_srv auth_server;
331 if (!find_challenge(rdata, auth)) {
338 setup_auth_srv(pool, &auth_server, auth->
realm);
341 authed = pjsip_auth_srv_verify(&auth_server, rdata, &response_code);
344 if (authed == PJ_SUCCESS) {
352 if (authed == PJSIP_EAUTHNOAUTH) {
356 ast_debug(3,
"Realm: %s Username: %s Result: %s\n",
376 static void challenge(
const char *realm, pjsip_tx_data *tdata,
const pjsip_rx_data *rdata,
int is_stale)
380 pjsip_auth_srv auth_server;
381 struct ast_str *nonce = ast_str_alloca(256);
383 time_t timestamp = time(NULL);
384 snprintf(time_buf,
sizeof(time_buf),
"%d", (
int) timestamp);
386 build_nonce(&nonce, time_buf, rdata, realm);
388 setup_auth_srv(tdata->pool, &auth_server, realm);
391 pj_cstr(&qop,
"auth");
392 pjsip_auth_srv_challenge(&auth_server, &qop, &pj_nonce, NULL, is_stale ? PJ_TRUE : PJ_FALSE, tdata);
404 static enum ast_sip_check_auth_result digest_check_auth(
struct ast_sip_endpoint *endpoint,
405 pjsip_rx_data *rdata, pjsip_tx_data *tdata)
409 enum digest_verify_result *verify_res;
411 enum ast_sip_check_auth_result res;
418 ast_assert(0 < auth_size);
420 auths =
ast_alloca(auth_size *
sizeof(*auths));
421 verify_res =
ast_alloca(auth_size *
sizeof(*verify_res));
423 artificial_endpoint = ast_sip_get_artificial_endpoint();
424 if (!artificial_endpoint) {
426 return AST_SIP_AUTHENTICATION_ERROR;
429 is_artificial = endpoint == artificial_endpoint;
430 ao2_ref(artificial_endpoint, -1);
432 ast_assert(auth_size == 1);
433 auths[0] = ast_sip_get_artificial_auth();
436 return AST_SIP_AUTHENTICATION_ERROR;
439 memset(auths, 0, auth_size *
sizeof(*auths));
440 if (ast_sip_retrieve_auths(&endpoint->
inbound_auths, auths)) {
441 res = AST_SIP_AUTHENTICATION_ERROR;
447 if (ast_strlen_zero(default_realm)) {
448 auths_shallow = auths;
454 auths_shallow =
ast_alloca(auth_size *
sizeof(*auths_shallow));
455 for (idx = 0; idx < auth_size; ++idx) {
456 if (ast_strlen_zero(auths[idx]->realm)) {
466 auths_shallow[idx] =
ast_alloca(
sizeof(**auths_shallow));
467 memcpy(auths_shallow[idx], auths[idx],
sizeof(**auths_shallow));
468 *((
char **) (&auths_shallow[idx]->realm)) = default_realm;
469 ast_debug(3,
"Using default realm '%s' on incoming auth '%s'.\n",
472 auths_shallow[idx] = auths[idx];
477 for (idx = 0; idx < auth_size; ++idx) {
478 verify_res[idx] = verify(auths_shallow[idx], rdata, tdata->pool);
479 if (verify_res[idx] == AUTH_SUCCESS) {
480 res = AST_SIP_AUTHENTICATION_SUCCESS;
483 if (verify_res[idx] == AUTH_FAIL) {
488 for (idx = 0; idx < auth_size; ++idx) {
489 challenge(auths_shallow[idx]->realm, tdata, rdata, verify_res[idx] == AUTH_STALE);
492 if (failures == auth_size) {
493 res = AST_SIP_AUTHENTICATION_FAILED;
495 res = AST_SIP_AUTHENTICATION_CHALLENGE;
499 ast_sip_cleanup_auths(auths, auth_size);
505 .check_authentication = digest_check_auth,
508 static int build_entity_id(
void)
512 eid = ao2_alloc(AST_UUID_STR_LEN, NULL);
523 static void global_loaded(
const char *object_type)
525 ast_sip_get_default_realm(default_realm,
sizeof(default_realm));
533 static int reload_module(
void)
535 if (build_entity_id()) {
541 static int load_module(
void)
543 if (build_entity_id()) {
550 if (ast_sip_register_authenticator(&digest_authenticator)) {
557 static int unload_module(
void)
560 ast_sip_unregister_authenticator(&digest_authenticator);
565 AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER,
"PJSIP authentication resource",
566 .support_level = AST_MODULE_SUPPORT_CORE,
568 .unload = unload_module,
569 .reload = reload_module,
571 .requires =
"res_pjsip",
Asterisk main include file. File version handling, generic pbx functions.
void * ast_threadstorage_get(struct ast_threadstorage *ts, size_t init_size)
Retrieve thread storage.
String manipulation functions.
const ast_string_field md5_creds
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
static pj_pool_t * pool
Global memory pool for configuration and timers.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
#define ao2_global_obj_ref(holder)
Get a reference to the object stored in the global holder.
static int copy(char *infile, char *outfile)
Utility function to copy a file.
static void cleanup(void)
Clean up any old apps that we don't need any more.
#define ast_strdupa(s)
duplicate a string in memory from the stack
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
unsigned int nonce_lifetime
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
int(* requires_authentication)(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
Check if a request requires authentication See ast_sip_requires_authentication for more details...
#define ast_debug(level,...)
Log a DEBUG message.
An entity with which Asterisk communicates.
int ast_sorcery_observer_add(const struct ast_sorcery *sorcery, const char *type, const struct ast_sorcery_observer *callbacks)
Add an observer to a specific object type.
#define ast_test_suite_event_notify(s, f,...)
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
struct ast_sip_auth_vector inbound_auths
Support for dynamic strings.
const ast_string_field realm
Interface for a sorcery object type observer.
#define ao2_global_obj_release(holder)
Release the ao2 object held in the global holder.
char * ast_uuid_generate_str(char *buf, size_t size)
Generate a UUID string.
const ast_string_field auth_pass
enum ast_sip_auth_type type
Support for logging to various files, console and syslog Configuration in file logger.conf.
Module has failed to load, may be in an inconsistent state.
void(* loaded)(const char *object_type)
Callback for when an object type is loaded/reloaded.
#define AST_THREADSTORAGE_CUSTOM(a, b, c)
Define a thread storage variable, with custom initialization and cleanup.
#define ao2_global_obj_replace_unref(holder, obj)
Replace an ao2 object in the global holder, throwing away any old object.
void ast_sorcery_observer_remove(const struct ast_sorcery *sorcery, const char *type, const struct ast_sorcery_observer *callbacks)
Remove an observer from a specific object type.
An interchangeable way of handling digest authentication for SIP.
void ast_md5_hash(char *output, const char *input)
Produces MD5 hash based on input string.
#define AO2_GLOBAL_OBJ_STATIC(name)
Define a global object holder to be used to hold an ao2 object, statically initialized.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Asterisk module definitions.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
const ast_string_field auth_user
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
void ast_sorcery_reload_object(const struct ast_sorcery *sorcery, const char *type)
Inform any wizards of a specific object type to reload persistent objects.