Asterisk - The Open Source Telephony Project GIT-master-7e7a603
res_pjsip_authenticator_digest.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2013, Digium, Inc.
5 *
6 * Mark Michelson <mmichelson@digium.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19#include "asterisk.h"
20
21#include <pjsip.h>
22
23#include "asterisk/res_pjsip.h"
24#include "asterisk/logger.h"
25#include "asterisk/module.h"
26#include "asterisk/strings.h"
27#include "asterisk/test.h"
28
29/*** MODULEINFO
30 <depend>pjproject</depend>
31 <depend>res_pjsip</depend>
32 <support_level>core</support_level>
33 ***/
34
36
38
39/*!
40 * \brief Determine if authentication is required
41 *
42 * Authentication is required if the endpoint has at least one auth
43 * section specified
44 */
45static int digest_requires_authentication(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
46{
48
49 return endpoint == artificial || AST_VECTOR_SIZE(&endpoint->inbound_auths) > 0;
50}
51
52static void auth_store_cleanup(void *data)
53{
54 struct ast_sip_auth **auth = data;
55
56 ao2_cleanup(*auth);
57 ast_free(data);
58}
59
60/*!
61 * \brief Thread-local storage for \ref ast_sip_auth
62 *
63 * The PJSIP authentication API is a bit annoying. When you set
64 * up an authentication server, you specify a lookup callback to
65 * call into when verifying incoming credentials. The problem
66 * with this callback is that it only gives you the realm and
67 * authentication username. In 2.0.5, there is a new version of
68 * the callback you can use that gives the pjsip_rx_data in
69 * addition.
70 *
71 * Unfortunately, the data we actually \b need is the
72 * \ref ast_sip_auth we are currently observing. So we have two
73 * choices:
74 * 1) Use the current PJSIP API and use thread-local storage
75 * to temporarily store our SIP authentication information. Then
76 * in the callback, we can retrieve the authentication info and
77 * use as needed. Given our threading model, this is safe.
78 * 2) Use the 2.0.5 API and temporarily store the authentication
79 * information in the rdata's endpoint_info. Then in the callback,
80 * we can retrieve the authentication info from the rdata.
81 *
82 * I've chosen option 1 since it does not require backporting
83 * any APIs from future versions of PJSIP, plus I feel the
84 * thread-local option is a bit cleaner.
85 */
87
88/*!
89 * \brief Store shallow copy authentication information in thread-local storage
90 */
91static int store_auth(const struct ast_sip_auth *auth)
92{
93 const struct ast_sip_auth **pointing;
94
95 pointing = ast_threadstorage_get(&auth_store, sizeof(pointing));
96 if (!pointing) {
97 return -1;
98 }
99
100 *pointing = auth;
101 return 0;
102}
103
104/*!
105 * \brief Remove shallow copy authentication information from thread-local storage
106 */
107static int remove_auth(void)
108{
109 struct ast_sip_auth **pointing;
110
111 pointing = ast_threadstorage_get(&auth_store, sizeof(pointing));
112 if (!pointing) {
113 return -1;
114 }
115
116 *pointing = NULL;
117 return 0;
118}
119
120/*!
121 * \brief Retrieve shallow copy authentication information from thread-local storage
122 */
123static const struct ast_sip_auth *get_auth(void)
124{
125 struct ast_sip_auth **auth;
126
127 auth = ast_threadstorage_get(&auth_store, sizeof(auth));
128 if (auth) {
129 return *auth;
130 }
131 return NULL;
132}
133
134/*!
135 * \brief Lookup callback for authentication verification
136 *
137 * This function is called when we call pjsip_auth_srv_verify(). It
138 * expects us to verify that the realm and account name from the
139 * Authorization header is correct. We are then supposed to supply
140 * a password or MD5 sum of credentials.
141 *
142 * \param pool A memory pool we can use for allocations
143 * \param realm The realm from the Authorization header
144 * \param acc_name the user from the Authorization header
145 * \param[out] info The credentials we need to fill in
146 * \retval PJ_SUCCESS Successful authentication
147 * \retval other Unsuccessful
148 */
149static 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)
151{
152 const struct ast_sip_auth *auth;
153
154 auth = get_auth();
155 if (!auth) {
156 return PJSIP_SC_FORBIDDEN;
157 }
158
159 if (auth->type == AST_SIP_AUTH_TYPE_ARTIFICIAL) {
160 return PJSIP_SC_FORBIDDEN;
161 }
162
163 if (pj_strcmp2(realm, auth->realm)) {
164 return PJSIP_SC_FORBIDDEN;
165 }
166 if (pj_strcmp2(acc_name, auth->auth_user)) {
167 return PJSIP_SC_FORBIDDEN;
168 }
169
170 pj_strdup2(pool, &info->realm, auth->realm);
171 pj_strdup2(pool, &info->username, auth->auth_user);
172
173 switch (auth->type) {
175 pj_strdup2(pool, &info->data, auth->auth_pass);
176 info->data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
177 break;
179 pj_strdup2(pool, &info->data, auth->md5_creds);
180 info->data_type = PJSIP_CRED_DATA_DIGEST;
181 break;
182 default:
183 return PJSIP_SC_FORBIDDEN;
184 }
185 return PJ_SUCCESS;
186}
187
188/*!
189 * \brief Calculate a nonce
190 *
191 * We use this in order to create authentication challenges. We also use this in order
192 * to verify that an incoming request with credentials could be in response to one
193 * of our challenges.
194 *
195 * The nonce is calculated from a timestamp, the source IP address, the source port, a
196 * unique ID for us, and the realm. This helps to ensure that the incoming request
197 * is from the same source that the nonce was calculated for. Including the realm
198 * ensures that multiple challenges to the same request have different nonces.
199 *
200 * \param nonce
201 * \param timestamp A UNIX timestamp expressed as a string
202 * \param rdata The incoming request
203 * \param realm The realm for which authentication should occur
204 */
205static int build_nonce(struct ast_str **nonce, const char *timestamp, const pjsip_rx_data *rdata, const char *realm)
206{
207 struct ast_str *str = ast_str_alloca(256);
208 RAII_VAR(char *, eid, ao2_global_obj_ref(entity_id), ao2_cleanup);
209 char hash[33];
210
211 /*
212 * Note you may be tempted to think why not include the port. The reason
213 * is that when using TCP the port can potentially differ from before.
214 */
215 ast_str_append(&str, 0, "%s", timestamp);
216 ast_str_append(&str, 0, ":%s", rdata->pkt_info.src_name);
217 ast_str_append(&str, 0, ":%s", eid);
218 ast_str_append(&str, 0, ":%s", realm);
220
221 ast_str_append(nonce, 0, "%s/%s", timestamp, hash);
222 return 0;
223}
224
225/*!
226 * \brief Ensure that a nonce on an incoming request is sane.
227 *
228 * The nonce in an incoming Authorization header needs to pass some scrutiny in order
229 * for us to consider accepting it. What we do is re-build a nonce based on request
230 * data and a realm and see if it matches the nonce they sent us.
231 * \param candidate The nonce on an incoming request
232 * \param rdata The incoming request
233 * \param auth The auth credentials we are trying to match against.
234 * \retval 0 Nonce does not pass validity checks
235 * \retval 1 Nonce passes validity check
236 */
237static int check_nonce(const char *candidate, const pjsip_rx_data *rdata, const struct ast_sip_auth *auth)
238{
239 char *copy = ast_strdupa(candidate);
240 char *timestamp = strsep(&copy, "/");
241 int timestamp_int;
242 time_t now = time(NULL);
243 struct ast_str *calculated = ast_str_alloca(64);
244
245 if (!copy) {
246 /* Clearly a bad nonce! */
247 return 0;
248 }
249
250 if (sscanf(timestamp, "%30d", &timestamp_int) != 1) {
251 return 0;
252 }
253
254 if ((int) now - timestamp_int > auth->nonce_lifetime) {
255 return 0;
256 }
257
258 build_nonce(&calculated, timestamp, rdata, auth->realm);
259 ast_debug(3, "Calculated nonce %s. Actual nonce is %s\n", ast_str_buffer(calculated), candidate);
260 if (strcmp(ast_str_buffer(calculated), candidate)) {
261 return 0;
262 }
263 return 1;
264}
265
266static int find_challenge(const pjsip_rx_data *rdata, const struct ast_sip_auth *auth)
267{
268 struct pjsip_authorization_hdr *auth_hdr = (pjsip_authorization_hdr *) &rdata->msg_info.msg->hdr;
269 int challenge_found = 0;
270 char nonce[64];
271
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)) {
275 challenge_found = 1;
276 break;
277 }
278 }
279
280 return challenge_found;
281}
282
283/*!
284 * \brief Common code for initializing a pjsip_auth_srv
285 */
286static void setup_auth_srv(pj_pool_t *pool, pjsip_auth_srv *auth_server, const char *realm)
287{
288 pj_str_t realm_str;
289 pj_cstr(&realm_str, realm);
290
291 pjsip_auth_srv_init(pool, auth_server, &realm_str, digest_lookup, 0);
292}
293
294/*!
295 * \brief Result of digest verification
296 */
298 /*! Authentication credentials incorrect */
300 /*! Authentication credentials correct */
302 /*! Authentication credentials correct but nonce mismatch */
304 /*! Authentication credentials were not provided */
306};
307
308static char *verify_result_str[] = {
309 "FAIL",
310 "SUCCESS",
311 "STALE",
312 "NOAUTH"
313};
314/*!
315 * \brief astobj2 callback for verifying incoming credentials
316 *
317 * \param auth The ast_sip_auth to check against
318 * \param rdata The incoming request
319 * \param pool A pool to use for the auth server
320 * \return CMP_MATCH on successful authentication
321 * \return 0 on failed authentication
322 */
323static int verify(const struct ast_sip_auth *auth, pjsip_rx_data *rdata, pj_pool_t *pool)
324{
325 pj_status_t authed;
326 int response_code;
327 pjsip_auth_srv auth_server;
328 int stale = 0;
329 int res = AUTH_FAIL;
330
331 if (!find_challenge(rdata, auth)) {
332 /* Couldn't find a challenge with a sane nonce.
333 * Nonce mismatch may just be due to staleness.
334 */
335 stale = 1;
336 }
337
338 setup_auth_srv(pool, &auth_server, auth->realm);
339
340 store_auth(auth);
341 authed = pjsip_auth_srv_verify(&auth_server, rdata, &response_code);
342 remove_auth();
343
344 if (authed == PJ_SUCCESS) {
345 if (stale) {
346 res = AUTH_STALE;
347 } else {
348 res = AUTH_SUCCESS;
349 }
350 }
351
352 if (authed == PJSIP_EAUTHNOAUTH) {
353 res = AUTH_NOAUTH;
354 }
355
356 ast_debug(3, "Realm: %s Username: %s Result: %s\n",
357 auth->realm, auth->auth_user, verify_result_str[res]);
358
359 ast_test_suite_event_notify("INCOMING_AUTH_VERIFY_RESULT",
360 "Realm: %s\r\n"
361 "Username: %s\r\n"
362 "Status: %s",
363 auth->realm, auth->auth_user, verify_result_str[res]);
364
365 return res;
366}
367
368/*!
369 * \brief astobj2 callback for adding digest challenges to responses
370 *
371 * \param realm An auth's realm to build a challenge from
372 * \param tdata The response to add the challenge to
373 * \param rdata The request the challenge is in response to
374 * \param is_stale Indicates whether nonce on incoming request was stale
375 */
376static void challenge(const char *realm, pjsip_tx_data *tdata, const pjsip_rx_data *rdata, int is_stale)
377{
378 pj_str_t qop;
379 pj_str_t pj_nonce;
380 pjsip_auth_srv auth_server;
381 struct ast_str *nonce = ast_str_alloca(256);
382 char time_buf[32];
383 time_t timestamp = time(NULL);
384 snprintf(time_buf, sizeof(time_buf), "%d", (int) timestamp);
385
386 build_nonce(&nonce, time_buf, rdata, realm);
387
388 setup_auth_srv(tdata->pool, &auth_server, realm);
389
390 pj_cstr(&pj_nonce, ast_str_buffer(nonce));
391 pj_cstr(&qop, "auth");
392 pjsip_auth_srv_challenge(&auth_server, &qop, &pj_nonce, NULL, is_stale ? PJ_TRUE : PJ_FALSE, tdata);
393}
394
395/*!
396 * \brief Check authentication using Digest scheme
397 *
398 * This function will check an incoming message against configured authentication
399 * options. If \b any of the incoming Authorization headers result in successful
400 * authentication, then authentication is considered successful.
401 *
402 * \see ast_sip_check_authentication
403 */
405 pjsip_rx_data *rdata, pjsip_tx_data *tdata)
406{
407 struct ast_sip_auth **auths;
408 struct ast_sip_auth **auths_shallow;
409 enum digest_verify_result *verify_res;
412 int idx;
413 int is_artificial;
414 int failures = 0;
415 size_t auth_size;
416
417 auth_size = AST_VECTOR_SIZE(&endpoint->inbound_auths);
418 ast_assert(0 < auth_size);
419
420 auths = ast_alloca(auth_size * sizeof(*auths));
421 verify_res = ast_alloca(auth_size * sizeof(*verify_res));
422
424 if (!artificial_endpoint) {
425 /* Should not happen except possibly if we are shutting down. */
427 }
428
429 is_artificial = endpoint == artificial_endpoint;
431 if (is_artificial) {
432 ast_assert(auth_size == 1);
433 auths[0] = ast_sip_get_artificial_auth();
434 if (!auths[0]) {
435 /* Should not happen except possibly if we are shutting down. */
437 }
438 } else {
439 memset(auths, 0, auth_size * sizeof(*auths));
440 if (ast_sip_retrieve_auths(&endpoint->inbound_auths, auths)) {
442 goto cleanup;
443 }
444 }
445
446 /* Setup shallow copy of auths */
448 auths_shallow = auths;
449 } else {
450 /*
451 * Set default realm on a shallow copy of the authentication
452 * objects that don't have a realm set.
453 */
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)) {
457 /*
458 * Make a shallow copy and set the default realm on it.
459 *
460 * The stack allocation is OK here. Normally this will
461 * loop one time. If you have multiple auths then you
462 * shouldn't need more auths than the normal complement
463 * of fingers and toes. Otherwise, you should check
464 * your sanity for setting up your system up that way.
465 */
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",
470 default_realm, ast_sorcery_object_get_id(auths_shallow[idx]));
471 } else {
472 auths_shallow[idx] = auths[idx];
473 }
474 }
475 }
476
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) {
481 goto cleanup;
482 }
483 if (verify_res[idx] == AUTH_FAIL) {
484 failures++;
485 }
486 }
487
488 for (idx = 0; idx < auth_size; ++idx) {
489 challenge(auths_shallow[idx]->realm, tdata, rdata, verify_res[idx] == AUTH_STALE);
490 }
491
492 if (failures == auth_size) {
494 } else {
496 }
497
498cleanup:
499 ast_sip_cleanup_auths(auths, auth_size);
500 return res;
501}
502
505 .check_authentication = digest_check_auth,
506};
507
508static int build_entity_id(void)
509{
510 char *eid;
511
513 if (!eid) {
514 return -1;
515 }
516
518 ao2_global_obj_replace_unref(entity_id, eid);
519 ao2_ref(eid, -1);
520 return 0;
521}
522
523static void global_loaded(const char *object_type)
524{
526}
527
528/*! \brief Observer which is used to update our default_realm when the global setting changes */
531};
532
533static int reload_module(void)
534{
535 if (build_entity_id()) {
536 return -1;
537 }
538 return 0;
539}
540
541static int load_module(void)
542{
543 if (build_entity_id()) {
545 }
546
549
551 ao2_global_obj_release(entity_id);
553 }
555}
556
557static int unload_module(void)
558{
561 ao2_global_obj_release(entity_id);
562 return 0;
563}
564
565AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP authentication resource",
566 .support_level = AST_MODULE_SUPPORT_CORE,
567 .load = load_module,
568 .unload = unload_module,
570 .load_pri = AST_MODPRI_CHANNEL_DEPEND - 5,
571 .requires = "res_pjsip",
const char * str
Definition: app_jack.c:147
static int copy(char *infile, char *outfile)
Utility function to copy a file.
while(1)
Definition: ast_expr2f.c:880
Asterisk main include file. File version handling, generic pbx functions.
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ao2_global_obj_replace_unref(holder, obj)
Replace an ao2 object in the global holder, throwing away any old object.
Definition: astobj2.h:901
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_global_obj_ref(holder)
Get a reference to the object stored in the global holder.
Definition: astobj2.h:918
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ao2_global_obj_release(holder)
Release the ao2 object held in the global holder.
Definition: astobj2.h:859
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
char * strsep(char **str, const char *delims)
Support for logging to various files, console and syslog Configuration in file logger....
#define ast_debug(level,...)
Log a DEBUG message.
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:317
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODPRI_CHANNEL_DEPEND
Definition: module.h:326
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
def info(msg)
static void * cleanup(void *unused)
Definition: pbx_realtime.c:124
static struct ast_sip_endpoint * artificial_endpoint
static int reload(void)
struct ast_sip_auth * ast_sip_get_artificial_auth(void)
Retrieves a reference to the artificial auth.
struct ast_sip_endpoint * ast_sip_get_artificial_endpoint(void)
Retrieves a reference to the artificial endpoint.
void ast_sip_cleanup_auths(struct ast_sip_auth *auths[], size_t num_auths)
Clean up retrieved auth structures from memory.
int ast_sip_retrieve_auths(const struct ast_sip_auth_vector *auths, struct ast_sip_auth **out)
Retrieve relevant SIP auth structures from sorcery.
void ast_copy_pj_str(char *dest, const pj_str_t *src, size_t size)
Copy a pj_str_t into a standard character buffer.
Definition: res_pjsip.c:2201
void ast_sip_unregister_authenticator(struct ast_sip_authenticator *auth)
Unregister a SIP authenticator.
Definition: res_pjsip.c:152
void ast_sip_get_default_realm(char *realm, size_t size)
Retrieve the global default realm.
#define AST_SIP_AUTH_MAX_REALM_LENGTH
Definition: res_pjsip.h:74
int ast_sip_register_authenticator(struct ast_sip_authenticator *auth)
Register a SIP authenticator.
Definition: res_pjsip.c:140
@ AST_SIP_AUTH_TYPE_ARTIFICIAL
Definition: res_pjsip.h:574
@ AST_SIP_AUTH_TYPE_MD5
Definition: res_pjsip.h:570
@ AST_SIP_AUTH_TYPE_USER_PASS
Definition: res_pjsip.h:568
struct ast_sorcery * ast_sip_get_sorcery(void)
Get a pointer to the SIP sorcery structure.
ast_sip_check_auth_result
Possible returns from ast_sip_check_authentication.
Definition: res_pjsip.h:1229
@ AST_SIP_AUTHENTICATION_CHALLENGE
Definition: res_pjsip.h:1231
@ AST_SIP_AUTHENTICATION_ERROR
Definition: res_pjsip.h:1237
@ AST_SIP_AUTHENTICATION_SUCCESS
Definition: res_pjsip.h:1233
@ AST_SIP_AUTHENTICATION_FAILED
Definition: res_pjsip.h:1235
static void setup_auth_srv(pj_pool_t *pool, pjsip_auth_srv *auth_server, const char *realm)
Common code for initializing a pjsip_auth_srv.
static struct ast_sorcery_observer global_observer
Observer which is used to update our default_realm when the global setting changes.
static int check_nonce(const char *candidate, const pjsip_rx_data *rdata, const struct ast_sip_auth *auth)
Ensure that a nonce on an incoming request is sane.
digest_verify_result
Result of digest verification.
static int find_challenge(const pjsip_rx_data *rdata, const struct ast_sip_auth *auth)
static void global_loaded(const char *object_type)
AO2_GLOBAL_OBJ_STATIC(entity_id)
static struct ast_threadstorage auth_store
static int build_entity_id(void)
static const struct ast_sip_auth * get_auth(void)
Retrieve shallow copy authentication information from thread-local storage.
static int reload_module(void)
static pj_status_t digest_lookup(pj_pool_t *pool, const pj_str_t *realm, const pj_str_t *acc_name, pjsip_cred_info *info)
Lookup callback for authentication verification.
static int store_auth(const struct ast_sip_auth *auth)
Store shallow copy authentication information in thread-local storage.
static char default_realm[AST_SIP_AUTH_MAX_REALM_LENGTH+1]
static int remove_auth(void)
Remove shallow copy authentication information from thread-local storage.
static int verify(const struct ast_sip_auth *auth, pjsip_rx_data *rdata, pj_pool_t *pool)
astobj2 callback for verifying incoming credentials
static enum ast_sip_check_auth_result digest_check_auth(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, pjsip_tx_data *tdata)
Check authentication using Digest scheme.
static int load_module(void)
static char * verify_result_str[]
static int unload_module(void)
static void auth_store_cleanup(void *data)
static void challenge(const char *realm, pjsip_tx_data *tdata, const pjsip_rx_data *rdata, int is_stale)
astobj2 callback for adding digest challenges to responses
static int digest_requires_authentication(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
Determine if authentication is required.
static struct ast_sip_authenticator digest_authenticator
static int build_nonce(struct ast_str **nonce, const char *timestamp, const pjsip_rx_data *rdata, const char *realm)
Calculate a nonce.
#define NULL
Definition: resample.c:96
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2317
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.
Definition: sorcery.c:2423
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.
Definition: sorcery.c:2391
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.
Definition: sorcery.c:1442
String manipulation functions.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
#define ast_str_alloca(init_len)
Definition: strings.h:848
const ast_string_field md5_creds
Definition: res_pjsip.h:597
const ast_string_field realm
Definition: res_pjsip.h:597
unsigned int nonce_lifetime
Definition: res_pjsip.h:599
const ast_string_field auth_user
Definition: res_pjsip.h:597
const ast_string_field auth_pass
Definition: res_pjsip.h:597
enum ast_sip_auth_type type
Definition: res_pjsip.h:601
An interchangeable way of handling digest authentication for SIP.
Definition: res_pjsip.h:1247
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.
Definition: res_pjsip.h:1252
An entity with which Asterisk communicates.
Definition: res_pjsip.h:963
struct ast_sip_auth_vector inbound_auths
Definition: res_pjsip.h:1008
Interface for a sorcery object type observer.
Definition: sorcery.h:332
void(* loaded)(const char *object_type)
Callback for when an object type is loaded/reloaded.
Definition: sorcery.h:343
Support for dynamic strings.
Definition: strings.h:623
Test Framework API.
#define ast_test_suite_event_notify(s, f,...)
Definition: test.h:189
#define AST_THREADSTORAGE_CUSTOM(a, b, c)
Define a thread storage variable, with custom initialization and cleanup.
void * ast_threadstorage_get(struct ast_threadstorage *ts, size_t init_size)
Retrieve thread storage.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define ast_assert(a)
Definition: utils.h:739
void ast_md5_hash(char *output, const char *input)
Produces MD5 hash based on input string.
Definition: utils.c:250
#define AST_UUID_STR_LEN
Definition: uuid.h:27
char * ast_uuid_generate_str(char *buf, size_t size)
Generate a UUID string.
Definition: uuid.c:141
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:609