Review Board 1.7.16


SIP authentication support

Review Request #2310 - Created Feb. 4, 2013 and submitted

Mark Michelson
/team/mmichelson/authenticate
ASTERISK-20953
Reviewers
asterisk-dev
jcolp, mjordan
Asterisk
This introduces inbound authentication support for the new SIP work.

I reworked the initial concept of authentication drastically. I realized that the original API I came up with was making it awkward to use any other authentication scheme than digest authentication. The newer authenticator callbacks are now simpler: there is one to find out whether an endpoint requires authentication and a second one to do whatever authentication is necessary, returning the results of such attempts.

This adds an authenticator, res_sip_authenticator_digest, that uses digest authentication in order to authenticate. Initially, I thought I was going to need some features from PJSIP trunk, but I realized after backporting them that they really didn't help the situation any. Thus I had to resort to using some thread-local storage in order to be able to access certain data in one of the PJSIP authentication callbacks. Due to the threading model in use, this is safe.

Authentication is configured via a type=auth section in res_sip.conf. Then an endpoint can specify auth=blah in order to tie authentication credentials to the endpoint. Endpoints can specify multiple auth sections if they have credentials for multiple realms. Similarly, multiple enpdoints may specify the same auth section if for whatever reason endpoints share credentials. This means that Asterisk may send multiple WWW-Authenticate headers out in an authentication challenge and can cope with multiple Authorization headers in requests.

I made a change to the sorcery API. The sorcery apply handler now returns an int instead of void. This way, if there is an error when applying an objectset, it can be detected. I did this because once an auth object has been constructed, I wasn't sure of another way to do some verification that the configured auth was sane. If a different change should be made, or if I was missing a simpler way to verify a constructed auth object, please let me know. IMO, this change makes good sense, though, especially as I was changing the transport apply handler to be able to return an error condition when it encounters problems.
Inbound calls have been made when authentication is configured and when it is not. I have ensured that endpoints with no configured authentication do not get challenged and that endpoints with configured authentication do get challenged.

I also tested both the password and md5_cred versions of storing authentication and verified that authentication worked properly.
/team/group/pimp_my_sip/include/asterisk/res_sip.h
Revision 381217 New Change
[20] 171 lines
[+20] [+] enum ast_sip_dtmf_mode {
172
	/*! Use SIP INFO DTMF (blech) */
172
	/*! Use SIP INFO DTMF (blech) */
173
	AST_SIP_DTMF_INFO,
173
	AST_SIP_DTMF_INFO,
174
};
174
};
175

    
   
175

   
176
/*!
176
/*!

    
   
177
 * \brief Methods of storing SIP digest authentication credentials.

    
   
178
 *

    
   
179
 * Note that both methods result in MD5 digest authentication being

    
   
180
 * used. The two methods simply alter how Asterisk determines the

    
   
181
 * credentials for a SIP authentication

    
   
182
 */

    
   
183
enum ast_sip_auth_type {

    
   
184
	/*! Credentials stored as a username and password combination */

    
   
185
	AST_SIP_AUTH_TYPE_USER_PASS,

    
   
186
	/*! Credentials stored as an MD5 sum */

    
   
187
	AST_SIP_AUTH_TYPE_MD5,

    
   
188
};

    
   
189

   

    
   
190
#define SIP_SORCERY_AUTH_TYPE "auth"

    
   
191

   

    
   
192
struct ast_sip_auth {

    
   
193
	/* Sorcery ID of the auth is its name */

    
   
194
	SORCERY_OBJECT(details);

    
   
195
	AST_DECLARE_STRING_FIELDS(

    
   
196
		/* Identification for these credentials */

    
   
197
		AST_STRING_FIELD(realm);

    
   
198
		/* Authentication username */

    
   
199
		AST_STRING_FIELD(auth_user);

    
   
200
		/* Authentication password */

    
   
201
		AST_STRING_FIELD(auth_pass);

    
   
202
		/* Authentication credentials in MD5 format (hash of user:realm:pass) */

    
   
203
		AST_STRING_FIELD(md5_creds);

    
   
204
	);

    
   
205
	/* The time period (in seconds) that a nonce may be reused */

    
   
206
	unsigned int nonce_lifetime;

    
   
207
	/* Used to determine what to use when authenticating */

    
   
208
	enum ast_sip_auth_type type;

    
   
209
};

    
   
210

   

    
   
211
/*!
177
 * \brief An entity with which Asterisk communicates
212
 * \brief An entity with which Asterisk communicates
178
 */
213
 */
179
struct ast_sip_endpoint {
214
struct ast_sip_endpoint {
180
	SORCERY_OBJECT(details);
215
	SORCERY_OBJECT(details);
181
	AST_DECLARE_STRING_FIELDS(
216
	AST_DECLARE_STRING_FIELDS(
[+20] [20] 10 lines
[+20] struct ast_sip_endpoint {
192
	struct ast_sip_aor *aor;
227
	struct ast_sip_aor *aor;
193
	/*! Codec preferences */
228
	/*! Codec preferences */
194
	struct ast_codec_pref prefs;
229
	struct ast_codec_pref prefs;
195
	/*! Configured codecs */
230
	/*! Configured codecs */
196
	struct ast_format_cap *codecs;
231
	struct ast_format_cap *codecs;

    
   
232
	/*! Names of authentication credentials */

    
   
233
	const char **sip_auths;

    
   
234
	/*! Number of configured auths */

    
   
235
	size_t num_auths;
197
	/*! DTMF mode to use with this endpoint */
236
	/*! DTMF mode to use with this endpoint */
198
	enum ast_sip_dtmf_mode dtmf;
237
	enum ast_sip_dtmf_mode dtmf;
199
	/*! Enabled SIP extensions */
238
	/*! Enabled SIP extensions */
200
	unsigned int extensions;
239
	unsigned int extensions;
201
	/*! Minimum session expiration period, in seconds */
240
	/*! Minimum session expiration period, in seconds */
[+20] [20] 30 lines
[+20] [+] void ast_sip_endpoint_get_location(const struct ast_sip_endpoint *endpoint, char *location_buf, size_t size);
232
 * \retval non-NULL The endpoint with this IP address
271
 * \retval non-NULL The endpoint with this IP address
233
 */
272
 */
234
struct ast_sip_endpoint *ast_sip_get_endpoint_from_location(const char *addr);
273
struct ast_sip_endpoint *ast_sip_get_endpoint_from_location(const char *addr);
235
 
274
 
236
/*!
275
/*!
237
 * \brief Data used for creating authentication challenges.
276
 * \brief Possible returns from ast_sip_check_authentication
238
 * 

   
239
 * This data gets populated by an authenticator's get_authentication_credentials() callback.

   
240
 */

   
241
struct ast_sip_digest_challenge_data {

   
242
    /*!

   
243
     * The realm to which the user is authenticating. An authenticator MUST fill this in.

   
244
     */

   
245
    const char *realm;

   
246
    /*!

   
247
     * Indicates whether the username and password are in plaintext or encoded as MD5.

   
248
     * If this is non-zero, then the data is an MD5 sum. Otherwise, the username and password are plaintext.

   
249
     * Authenticators MUST set this.

   
250
     */

   
251
    int is_md5;

   
252
    /*!

   
253
     * This is the actual username and secret. The is_md5 field is used to determine which member

   
254
     * of the union is to be used when creating the authentication challenge. In other words, if

   
255
     * is_md5 is non-zero, then we will use the md5 field of the auth union. Otherwise, we will

   
256
     * use the plain struct in the auth union.

   
257
     * Authenticators MUST fill in the appropriate field of the union.

   
258
     */

   
259
    union {

   
260
        /*!

   
261
         * Structure containing the username and password to encode in a digest authentication challenge.

   
262
         */

   
263
        struct {

   
264
            const char *username;

   
265
            const char *password;

   
266
        } plain;

   
267
        /*!

   
268
         * An MD5-encoded string that incorporates the username and password.

   
269
         */

   
270
        const char *md5;

   
271
    } auth;

   
272
    /*!

   
273
     * Domain for which the authentication challenge is being sent. This corresponds to the "domain=" portion of

   
274
     * a digest authentication.

   
275
     *

   
276
     * Authenticators do not have to fill in this field since it is an optional part of a digest.

   
277
     */

   
278
    const char *domain;

   
279
    /*!

   
280
     * Opaque string for digest challenge. This corresponds to the "opaque=" portion of a digest authentication.

   
281
     * Authenticators do not have to fill in this field. If an authenticator does not fill it in, Asterisk will provide one.

   
282
     */

   
283
    const char *opaque;

   
284
    /*!

   
285
     * Nonce string for digest challenge. This corresponds to the "nonce=" portion of a digest authentication.

   
286
     * Authenticators do not have to fill in this field. If an authenticator does not fill it in, Asterisk will provide one.

   
287
     */
277
 */
288
    const char *nonce;
278
enum ast_sip_check_auth_result {

    
   
279
    /*! Authentication needs to be challenged */

    
   
280
    AST_SIP_AUTHENTICATION_CHALLENGE,
Moved from 574

    
   
281
    /*! Authentication succeeded */
Moved from 575

    
   
282
    AST_SIP_AUTHENTICATION_SUCCESS,
Moved from 576

    
   
283
    /*! Authentication failed */
Moved from 577

    
   
284
    AST_SIP_AUTHENTICATION_FAILED,

    
   
285
    /*! Authentication encountered some internal error */

    
   
286
    AST_SIP_AUTHENTICATION_ERROR,
289
};
287
};
290
 
288

   
291
/*!
289
/*!
292
 * \brief An interchangeable way of handling digest authentication for SIP.
290
 * \brief An interchangeable way of handling digest authentication for SIP.
293
 * 
291
 * 
294
 * An authenticator is responsible for filling in the callbacks provided below. Each is called from a publicly available
292
 * An authenticator is responsible for filling in the callbacks provided below. Each is called from a publicly available
295
 * function in res_sip. The authenticator can use configuration or other local policy to determine whether authentication
293
 * function in res_sip. The authenticator can use configuration or other local policy to determine whether authentication
[+20] [20] 4 lines
[+20] [+] struct ast_sip_authenticator {
300
     * \brief Check if a request requires authentication
298
     * \brief Check if a request requires authentication
301
     * See ast_sip_requires_authentication for more details
299
     * See ast_sip_requires_authentication for more details
302
     */
300
     */
303
    int (*requires_authentication)(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata);
301
    int (*requires_authentication)(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata);
304
    /*!
302
	/*!
305
     * \brief Attempt to authenticate the incoming request
303
	 * \brief Check that an incoming request passes authentication.
306
     * See ast_sip_authenticate_request for more details
304
	 *
307
     */
305
	 * The tdata parameter is useful for adding information such as digest challenges.
308
    int (*authenticate_request)(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata);
306
	 *
309
    /*!
307
	 * \param endpoint The endpoint sending the incoming request
310
     * \brief Get digest authentication details
308
	 * \param rdata The incoming request
311
     * See ast_sip_get_authentication_credentials for more details
309
	 * \param tdata Tentative outgoing request.
312
    */
310
	 */
313
    int (*get_authentication_credentials)(struct ast_sip_endpoint *endpoint, struct ast_sip_digest_challenge_data *challenge);
311
	enum ast_sip_check_auth_result (*check_authentication)(struct ast_sip_endpoint *endpoint,

    
   
312
			pjsip_rx_data *rdata, pjsip_tx_data *tdata);
314
};
313
};
315
 
314
 
316
/*!
315
/*!
317
 * \brief An entity responsible for identifying the source of a SIP message
316
 * \brief An entity responsible for identifying the source of a SIP message
318
 */
317
 */
[+20] [20] 127 lines
[+20] [+] struct ast_sorcery *ast_sip_get_sorcery(void);
446
 * \retval 0 success
445
 * \retval 0 success
447
 */
446
 */
448
int ast_sip_initialize_sorcery_transport(struct ast_sorcery *sorcery);
447
int ast_sip_initialize_sorcery_transport(struct ast_sorcery *sorcery);
449

    
   
448

   
450
/*!
449
/*!

    
   
450
 * \brief Initialize authentication support on a sorcery instance

    
   
451
 *

    
   
452
 * \param sorcery The sorcery instance

    
   
453
 *

    
   
454
 * \retval -1 failure

    
   
455
 * \retval 0 success

    
   
456
 */

    
   
457
int ast_sip_initialize_sorcery_auth(struct ast_sorcery *sorcery);

    
   
458

   

    
   
459
/*!
451
 * \brief Create a new SIP work structure
460
 * \brief Create a new SIP work structure
452
 *
461
 *
453
 * A SIP work is a means of grouping together SIP tasks. For instance, one
462
 * A SIP work is a means of grouping together SIP tasks. For instance, one
454
 * might create a SIP work so that all tasks for a given SIP dialog will
463
 * might create a SIP work so that all tasks for a given SIP dialog will
455
 * be grouped together. Grouping the work together ensures that the
464
 * be grouped together. Grouping the work together ensures that the
[+20] [20] 78 lines
[+20] [+] int ast_sip_send_request(const char *method, const struct ast_sip_body *body,
534
 * \retval 0 The request does not require authentication
543
 * \retval 0 The request does not require authentication
535
 */
544
 */
536
int ast_sip_requires_authentication(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata);
545
int ast_sip_requires_authentication(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata);
537
 
546
 
538
/*!
547
/*!
539
 * \brief Authenticate an inbound SIP request
548
 * \brief Method to determine authentication status of an incoming request
540
 *
549
 *
541
 * This calls into the registered authenticator's authenticate_request callback
550
 * This will call into a registered authenticator. The registered authenticator will
542
 * in order to determine if the request contains proper credentials as to be
551
 * do what is necessary to determine whether the incoming request passes authentication.
543
 * authenticated.
552
 * A tentative response is passed into this function so that if, say, a digest authentication
544
 *
553
 * challenge should be sent in the ensuing response, it can be added to the response.
545
 * If there is no registered authenticator, then the request will assumed to be

   
546
 * authenticated properly.

   
547
 *

   
548
 * \param endpoint The endpoint from which the request originates

   
549
 * \param rdata The incoming SIP request

   
550
 * \retval 0 Successfully authenticated

   
551
 * \retval nonzero Failure to authenticate

   
552
 */

   
553
int ast_sip_authenticate_request(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata);

   
554
 

   
555
/*!

   
556
 * \brief Get authentication credentials in order to challenge a request

   
557
 *

   
558
 * This calls into the registered authenticator's get_authentication_credentials

   
559
 * callback in order to get credentials required for challenging a request.

   
560
 *

   
561
 * \param endpoint The endpoint whose credentials are being gathered

   
562
 * \param[out] challenge The necessary data in order to be able to challenge a request

   
563
 * \retval 0 Success

   
564
 * \retval -1 Failure

   
565
 */

   
566
int ast_sip_get_authentication_credentials(struct ast_sip_endpoint *endpoint, struct ast_sip_digest_challenge_data *challenge);

   
567
 

   
568
/*!

   
569
 * \brief Possible returns from ast_sip_check_authentication

   
570
 */

   
571
enum ast_sip_check_auth_result {

   
572
    /*! Authentication challenge sent */

   
573
    AST_SIP_AUTHENTICATION_CHALLENGE_SENT,

   
574
    /*! Authentication succeeded */
Moved to 281

   
575
    AST_SIP_AUTHENTICATION_SUCCESS,
Moved to 282

   
576
    /*! Authentication failed */
Moved to 283

   
577
    AST_SIP_AUTHENTICATION_FAILED,
Moved to 284

   
578
    /*! Authentication not required */

   
579
    AST_SIP_AUTHENTICATION_NOT_REQUIRED,

   
580
};

   
581
 

   
582
/*!

   
583
 * \brief Shortcut routine to check for authentication of an incoming request

   
584
 *

   
585
 * This is a wrapper that will call into a registered authenticator to see if a request

   
586
 * should be authenticated. Then if it should be, will attempt to authenticate. If the

   
587
 * request cannot be authenticated, then a challenge will be sent. Calling this can be

   
588
 * a suitable substitute for calling ast_sip_requires_authentication(),

   
589
 * ast_sip_authenticate_request(), and ast_sip_get_authentication_credentials()

   
590
 *
554
 *
591
 * \param endpoint The endpoint from the request was sent
555
 * \param endpoint The endpoint from the request was sent
592
 * \param rdata The request to potentially authenticate
556
 * \param rdata The request to potentially authenticate

    
   
557
 * \param tdata Tentative response to the request
593
 * \return The result of checking authentication.
558
 * \return The result of checking authentication.
594
 */
559
 */
595
enum ast_sip_check_auth_result ast_sip_check_authentication(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata);
560
enum ast_sip_check_auth_result ast_sip_check_authentication(struct ast_sip_endpoint *endpoint,
596
 
561
		pjsip_rx_data *rdata, pjsip_tx_data *tdata);
597
/*!

   
598
 * \brief Add digest information to an authentication challenge

   
599
 *

   
600
 * \param challenge Details to help in constructing a WWW-Authenticate header

   
601
 * \param tdata The challenge to add the digest to

   
602
 */

   
603
void ast_sip_add_digest_to_challenge(struct ast_sip_digest_challenge_data *challenge, pjsip_tx_data *tdata);

   
604
 
562
 
605
/*!
563
/*!
606
 * \brief Determine the endpoint that has sent a SIP message
564
 * \brief Determine the endpoint that has sent a SIP message
607
 *
565
 *
608
 * This will call into each of the registered endpoint identifiers'
566
 * This will call into each of the registered endpoint identifiers'
[+20] [20] 77 lines
/team/group/pimp_my_sip/include/asterisk/sorcery.h
Revision 381217 New Change
 
/team/group/pimp_my_sip/main/astobj2.c
Revision 381217 New Change
 
/team/group/pimp_my_sip/main/sorcery.c
Revision 381217 New Change
 
/team/group/pimp_my_sip/res/res_sip.c
Revision 381217 New Change
 
/team/group/pimp_my_sip/res/res_sip_authenticator_digest.c
New File
 
/team/group/pimp_my_sip/res/res_sip_session.c
Revision 381217 New Change
 
/team/group/pimp_my_sip/res/res_sip/config_auth.c
New File
 
/team/group/pimp_my_sip/res/res_sip/config_transport.c
Revision 381217 New Change
 
/team/group/pimp_my_sip/res/res_sip/sip_configuration.c
Revision 381217 New Change
 
/team/group/pimp_my_sip/tests/test_sorcery.c
Revision 381217 New Change
 
  1. /team/group/pimp_my_sip/include/asterisk/res_sip.h: Loading...
  2. /team/group/pimp_my_sip/include/asterisk/sorcery.h: Loading...
  3. /team/group/pimp_my_sip/main/astobj2.c: Loading...
  4. /team/group/pimp_my_sip/main/sorcery.c: Loading...
  5. /team/group/pimp_my_sip/res/res_sip.c: Loading...
  6. /team/group/pimp_my_sip/res/res_sip_authenticator_digest.c: Loading...
  7. /team/group/pimp_my_sip/res/res_sip_session.c: Loading...
  8. /team/group/pimp_my_sip/res/res_sip/config_auth.c: Loading...
  9. /team/group/pimp_my_sip/res/res_sip/config_transport.c: Loading...
  10. /team/group/pimp_my_sip/res/res_sip/sip_configuration.c: Loading...
  11. /team/group/pimp_my_sip/tests/test_sorcery.c: Loading...

https://reviewboard.asterisk.org/ runs on a server provided by Digium, Inc. and uses bandwidth donated to the open source Asterisk community by API Digital Communications in Huntsville, AL USA.
Please report problems with this site to asteriskteam@digium.com.