blob: 514d9d5afa04444f138e0ada7775a87b42aad47a [file] [log] [blame]
Alexandre Lision8af73cb2013-12-10 14:11:20 -05001/* $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 */
35PJ_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_bzero(auth_srv, sizeof(*auth_srv));
44 pj_strdup( pool, &auth_srv->realm, realm);
45 auth_srv->lookup = lookup;
46 auth_srv->is_proxy = (options & PJSIP_AUTH_SRV_IS_PROXY);
47
48 return PJ_SUCCESS;
49}
50
51/*
52 * Initialize server authorization session data structure to serve the
53 * specified realm and to use lookup_func function to look for the credential
54 * info.
55 */
56PJ_DEF(pj_status_t) pjsip_auth_srv_init2(
57 pj_pool_t *pool,
58 pjsip_auth_srv *auth_srv,
59 const pjsip_auth_srv_init_param *param)
60{
61 PJ_ASSERT_RETURN(pool && auth_srv && param, PJ_EINVAL);
62
63 pj_bzero(auth_srv, sizeof(*auth_srv));
64 pj_strdup( pool, &auth_srv->realm, param->realm);
65 auth_srv->lookup2 = param->lookup2;
66 auth_srv->is_proxy = (param->options & PJSIP_AUTH_SRV_IS_PROXY);
67
68 return PJ_SUCCESS;
69}
70
71
72/* Verify incoming Authorization/Proxy-Authorization header against the
73 * specified credential.
74 */
75static pj_status_t pjsip_auth_verify( const pjsip_authorization_hdr *hdr,
76 const pj_str_t *method,
77 const pjsip_cred_info *cred_info )
78{
79 if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0) {
80 char digest_buf[PJSIP_MD5STRLEN];
81 pj_str_t digest;
82 const pjsip_digest_credential *dig = &hdr->credential.digest;
83
84 /* Check that username and realm match.
85 * These checks should have been performed before entering this
86 * function.
87 */
88 PJ_ASSERT_RETURN(pj_strcmp(&dig->username, &cred_info->username) == 0,
89 PJ_EINVALIDOP);
90 PJ_ASSERT_RETURN(pj_strcmp(&dig->realm, &cred_info->realm) == 0,
91 PJ_EINVALIDOP);
92
93 /* Prepare for our digest calculation. */
94 digest.ptr = digest_buf;
95 digest.slen = PJSIP_MD5STRLEN;
96
97 /* Create digest for comparison. */
98 pjsip_auth_create_digest(&digest,
99 &hdr->credential.digest.nonce,
100 &hdr->credential.digest.nc,
101 &hdr->credential.digest.cnonce,
102 &hdr->credential.digest.qop,
103 &hdr->credential.digest.uri,
104 &cred_info->realm,
105 cred_info,
106 method );
107
108 /* Compare digest. */
109 return (pj_stricmp(&digest, &hdr->credential.digest.response) == 0) ?
110 PJ_SUCCESS : PJSIP_EAUTHINVALIDDIGEST;
111
112 } else {
113 pj_assert(!"Unsupported authentication scheme");
114 return PJSIP_EINVALIDAUTHSCHEME;
115 }
116}
117
118
119/*
120 * Request the authorization server framework to verify the authorization
121 * information in the specified request in rdata.
122 */
123PJ_DEF(pj_status_t) pjsip_auth_srv_verify( pjsip_auth_srv *auth_srv,
124 pjsip_rx_data *rdata,
125 int *status_code)
126{
127 pjsip_authorization_hdr *h_auth;
128 pjsip_msg *msg = rdata->msg_info.msg;
129 pjsip_hdr_e htype;
130 pj_str_t acc_name;
131 pjsip_cred_info cred_info;
132 pj_status_t status;
133
134 PJ_ASSERT_RETURN(auth_srv && rdata, PJ_EINVAL);
135 PJ_ASSERT_RETURN(msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG);
136
137 htype = auth_srv->is_proxy ? PJSIP_H_PROXY_AUTHORIZATION :
138 PJSIP_H_AUTHORIZATION;
139
140 /* Initialize status with 200. */
141 *status_code = 200;
142
143 /* Find authorization header for our realm. */
144 h_auth = (pjsip_authorization_hdr*) pjsip_msg_find_hdr(msg, htype, NULL);
145 while (h_auth) {
146 if (!pj_stricmp(&h_auth->credential.common.realm, &auth_srv->realm))
147 break;
148
149 h_auth = h_auth->next;
150 if (h_auth == (void*) &msg->hdr) {
151 h_auth = NULL;
152 break;
153 }
154
155 h_auth=(pjsip_authorization_hdr*)pjsip_msg_find_hdr(msg,htype,h_auth);
156 }
157
158 if (!h_auth) {
159 *status_code = auth_srv->is_proxy ? 407 : 401;
160 return PJSIP_EAUTHNOAUTH;
161 }
162
163 /* Check authorization scheme. */
164 if (pj_stricmp(&h_auth->scheme, &pjsip_DIGEST_STR) == 0)
165 acc_name = h_auth->credential.digest.username;
166 else {
167 *status_code = auth_srv->is_proxy ? 407 : 401;
168 return PJSIP_EINVALIDAUTHSCHEME;
169 }
170
171 /* Find the credential information for the account. */
172 if (auth_srv->lookup2) {
173 pjsip_auth_lookup_cred_param param;
174
175 pj_bzero(&param, sizeof(param));
176 param.realm = auth_srv->realm;
177 param.acc_name = acc_name;
178 param.rdata = rdata;
179 status = (*auth_srv->lookup2)(rdata->tp_info.pool, &param, &cred_info);
180 if (status != PJ_SUCCESS) {
181 *status_code = PJSIP_SC_FORBIDDEN;
182 return status;
183 }
184 } else {
185 status = (*auth_srv->lookup)(rdata->tp_info.pool, &auth_srv->realm,
186 &acc_name, &cred_info);
187 if (status != PJ_SUCCESS) {
188 *status_code = PJSIP_SC_FORBIDDEN;
189 return status;
190 }
191 }
192
193 /* Authenticate with the specified credential. */
194 status = pjsip_auth_verify(h_auth, &msg->line.req.method.name,
195 &cred_info);
196 if (status != PJ_SUCCESS) {
197 *status_code = PJSIP_SC_FORBIDDEN;
198 }
199 return status;
200}
201
202
203/*
204 * Add authentication challenge headers to the outgoing response in tdata.
205 * Application may specify its customized nonce and opaque for the challenge,
206 * or can leave the value to NULL to make the function fills them in with
207 * random characters.
208 */
209PJ_DEF(pj_status_t) pjsip_auth_srv_challenge( pjsip_auth_srv *auth_srv,
210 const pj_str_t *qop,
211 const pj_str_t *nonce,
212 const pj_str_t *opaque,
213 pj_bool_t stale,
214 pjsip_tx_data *tdata)
215{
216 pjsip_www_authenticate_hdr *hdr;
217 char nonce_buf[16];
218 pj_str_t random;
219
220 PJ_ASSERT_RETURN( auth_srv && tdata, PJ_EINVAL );
221
222 random.ptr = nonce_buf;
223 random.slen = sizeof(nonce_buf);
224
225 /* Create the header. */
226 if (auth_srv->is_proxy)
227 hdr = pjsip_proxy_authenticate_hdr_create(tdata->pool);
228 else
229 hdr = pjsip_www_authenticate_hdr_create(tdata->pool);
230
231 /* Initialize header.
232 * Note: only support digest authentication now.
233 */
234 hdr->scheme = pjsip_DIGEST_STR;
235 hdr->challenge.digest.algorithm = pjsip_MD5_STR;
236 if (nonce) {
237 pj_strdup(tdata->pool, &hdr->challenge.digest.nonce, nonce);
238 } else {
239 pj_create_random_string(nonce_buf, sizeof(nonce_buf));
240 pj_strdup(tdata->pool, &hdr->challenge.digest.nonce, &random);
241 }
242 if (opaque) {
243 pj_strdup(tdata->pool, &hdr->challenge.digest.opaque, opaque);
244 } else {
245 pj_create_random_string(nonce_buf, sizeof(nonce_buf));
246 pj_strdup(tdata->pool, &hdr->challenge.digest.opaque, &random);
247 }
248 if (qop) {
249 pj_strdup(tdata->pool, &hdr->challenge.digest.qop, qop);
250 } else {
251 hdr->challenge.digest.qop.slen = 0;
252 }
253 pj_strdup(tdata->pool, &hdr->challenge.digest.realm, &auth_srv->realm);
254 hdr->challenge.digest.stale = stale;
255
256 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
257
258 return PJ_SUCCESS;
259}
260