Add support for ICE/STUN/TURN in res_rtp_asterisk and chan_sip.
[asterisk/asterisk.git] / res / pjproject / pjsip / src / pjsip / sip_auth_server.c
1 /* $Id$ */
2 /* 
3  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
19  */
20
21 #include <pjsip/sip_auth.h>
22 #include <pjsip/sip_auth_parser.h>      /* just to get pjsip_DIGEST_STR */
23 #include <pjsip/sip_auth_msg.h>
24 #include <pjsip/sip_errno.h>
25 #include <pjsip/sip_transport.h>
26 #include <pj/string.h>
27 #include <pj/assert.h>
28
29
30 /*
31  * Initialize server authorization session data structure to serve the 
32  * specified realm and to use lookup_func function to look for the credential 
33  * info. 
34  */
35 PJ_DEF(pj_status_t) pjsip_auth_srv_init(  pj_pool_t *pool,
36                                           pjsip_auth_srv *auth_srv,
37                                           const pj_str_t *realm,
38                                           pjsip_auth_lookup_cred *lookup,
39                                           unsigned options )
40 {
41     PJ_ASSERT_RETURN(pool && auth_srv && realm && lookup, PJ_EINVAL);
42
43     pj_strdup( pool, &auth_srv->realm, realm);
44     auth_srv->lookup = lookup;
45     auth_srv->is_proxy = (options & PJSIP_AUTH_SRV_IS_PROXY);
46
47     return PJ_SUCCESS;
48 }
49
50
51 /* Verify incoming Authorization/Proxy-Authorization header against the 
52  * specified credential.
53  */
54 static pj_status_t pjsip_auth_verify( const pjsip_authorization_hdr *hdr,
55                                       const pj_str_t *method,
56                                       const pjsip_cred_info *cred_info )
57 {
58     if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0) {
59         char digest_buf[PJSIP_MD5STRLEN];
60         pj_str_t digest;
61         const pjsip_digest_credential *dig = &hdr->credential.digest;
62
63         /* Check that username and realm match. 
64          * These checks should have been performed before entering this
65          * function.
66          */
67         PJ_ASSERT_RETURN(pj_strcmp(&dig->username, &cred_info->username) == 0,
68                          PJ_EINVALIDOP);
69         PJ_ASSERT_RETURN(pj_strcmp(&dig->realm, &cred_info->realm) == 0,
70                          PJ_EINVALIDOP);
71
72         /* Prepare for our digest calculation. */
73         digest.ptr = digest_buf;
74         digest.slen = PJSIP_MD5STRLEN;
75
76         /* Create digest for comparison. */
77         pjsip_auth_create_digest(&digest, 
78                                  &hdr->credential.digest.nonce,
79                                  &hdr->credential.digest.nc, 
80                                  &hdr->credential.digest.cnonce,
81                                  &hdr->credential.digest.qop,
82                                  &hdr->credential.digest.uri,
83                                  &cred_info->realm,
84                                  cred_info, 
85                                  method );
86
87         /* Compare digest. */
88         return (pj_stricmp(&digest, &hdr->credential.digest.response) == 0) ?
89                PJ_SUCCESS : PJSIP_EAUTHINVALIDDIGEST;
90
91     } else {
92         pj_assert(!"Unsupported authentication scheme");
93         return PJSIP_EINVALIDAUTHSCHEME;
94     }
95 }
96
97
98 /*
99  * Request the authorization server framework to verify the authorization 
100  * information in the specified request in rdata.
101  */
102 PJ_DEF(pj_status_t) pjsip_auth_srv_verify( pjsip_auth_srv *auth_srv,
103                                            pjsip_rx_data *rdata,
104                                            int *status_code)
105 {
106     pjsip_authorization_hdr *h_auth;
107     pjsip_msg *msg = rdata->msg_info.msg;
108     pjsip_hdr_e htype;
109     pj_str_t acc_name;
110     pjsip_cred_info cred_info;
111     pj_status_t status;
112
113     PJ_ASSERT_RETURN(auth_srv && rdata, PJ_EINVAL);
114     PJ_ASSERT_RETURN(msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG);
115
116     htype = auth_srv->is_proxy ? PJSIP_H_PROXY_AUTHORIZATION : 
117                                  PJSIP_H_AUTHORIZATION;
118
119     /* Initialize status with 200. */
120     *status_code = 200;
121
122     /* Find authorization header for our realm. */
123     h_auth = (pjsip_authorization_hdr*) pjsip_msg_find_hdr(msg, htype, NULL);
124     while (h_auth) {
125         if (!pj_stricmp(&h_auth->credential.common.realm, &auth_srv->realm))
126             break;
127
128         h_auth = h_auth->next;
129         if (h_auth == (void*) &msg->hdr) {
130             h_auth = NULL;
131             break;
132         }
133
134         h_auth=(pjsip_authorization_hdr*)pjsip_msg_find_hdr(msg,htype,h_auth);
135     }
136
137     if (!h_auth) {
138         *status_code = auth_srv->is_proxy ? 407 : 401;
139         return PJSIP_EAUTHNOAUTH;
140     }
141
142     /* Check authorization scheme. */
143     if (pj_stricmp(&h_auth->scheme, &pjsip_DIGEST_STR) == 0)
144         acc_name = h_auth->credential.digest.username;
145     else {
146         *status_code = auth_srv->is_proxy ? 407 : 401;
147         return PJSIP_EINVALIDAUTHSCHEME;
148     }
149
150     /* Find the credential information for the account. */
151     status = (*auth_srv->lookup)(rdata->tp_info.pool, &auth_srv->realm,
152                                  &acc_name, &cred_info);
153     if (status != PJ_SUCCESS) {
154         *status_code = PJSIP_SC_FORBIDDEN;
155         return status;
156     }
157
158     /* Authenticate with the specified credential. */
159     status = pjsip_auth_verify(h_auth, &msg->line.req.method.name, 
160                                &cred_info);
161     if (status != PJ_SUCCESS) {
162         *status_code = PJSIP_SC_FORBIDDEN;
163     }
164     return status;
165 }
166
167
168 /*
169  * Add authentication challenge headers to the outgoing response in tdata. 
170  * Application may specify its customized nonce and opaque for the challenge, 
171  * or can leave the value to NULL to make the function fills them in with 
172  * random characters.
173  */
174 PJ_DEF(pj_status_t) pjsip_auth_srv_challenge(  pjsip_auth_srv *auth_srv,
175                                                const pj_str_t *qop,
176                                                const pj_str_t *nonce,
177                                                const pj_str_t *opaque,
178                                                pj_bool_t stale,
179                                                pjsip_tx_data *tdata)
180 {
181     pjsip_www_authenticate_hdr *hdr;
182     char nonce_buf[16];
183     pj_str_t random;
184
185     PJ_ASSERT_RETURN( auth_srv && tdata, PJ_EINVAL );
186
187     random.ptr = nonce_buf;
188     random.slen = sizeof(nonce_buf);
189
190     /* Create the header. */
191     if (auth_srv->is_proxy)
192         hdr = pjsip_proxy_authenticate_hdr_create(tdata->pool);
193     else
194         hdr = pjsip_www_authenticate_hdr_create(tdata->pool);
195
196     /* Initialize header. 
197      * Note: only support digest authentication now.
198      */
199     hdr->scheme = pjsip_DIGEST_STR;
200     hdr->challenge.digest.algorithm = pjsip_MD5_STR;
201     if (nonce) {
202         pj_strdup(tdata->pool, &hdr->challenge.digest.nonce, nonce);
203     } else {
204         pj_create_random_string(nonce_buf, sizeof(nonce_buf));
205         pj_strdup(tdata->pool, &hdr->challenge.digest.nonce, &random);
206     }
207     if (opaque) {
208         pj_strdup(tdata->pool, &hdr->challenge.digest.opaque, opaque);
209     } else {
210         pj_create_random_string(nonce_buf, sizeof(nonce_buf));
211         pj_strdup(tdata->pool, &hdr->challenge.digest.opaque, &random);
212     }
213     if (qop) {
214         pj_strdup(tdata->pool, &hdr->challenge.digest.qop, qop);
215     } else {
216         hdr->challenge.digest.qop.slen = 0;
217     }
218     pj_strdup(tdata->pool, &hdr->challenge.digest.realm, &auth_srv->realm);
219     hdr->challenge.digest.stale = stale;
220
221     pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
222
223     return PJ_SUCCESS;
224 }
225