1
/* $Id: sip_auth_server.c 4214 2012-07-25 14:29:28Z nanang $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
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.
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.
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
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>
31
* Initialize server authorization session data structure to serve the
32
* specified realm and to use lookup_func function to look for the credential
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,
41
PJ_ASSERT_RETURN(pool && auth_srv && realm && lookup, PJ_EINVAL);
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);
52
* Initialize server authorization session data structure to serve the
53
* specified realm and to use lookup_func function to look for the credential
56
PJ_DEF(pj_status_t) pjsip_auth_srv_init2(
58
pjsip_auth_srv *auth_srv,
59
const pjsip_auth_srv_init_param *param)
61
PJ_ASSERT_RETURN(pool && auth_srv && param, PJ_EINVAL);
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);
72
/* Verify incoming Authorization/Proxy-Authorization header against the
73
* specified credential.
75
static pj_status_t pjsip_auth_verify( const pjsip_authorization_hdr *hdr,
76
const pj_str_t *method,
77
const pjsip_cred_info *cred_info )
79
if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0) {
80
char digest_buf[PJSIP_MD5STRLEN];
82
const pjsip_digest_credential *dig = &hdr->credential.digest;
84
/* Check that username and realm match.
85
* These checks should have been performed before entering this
88
PJ_ASSERT_RETURN(pj_strcmp(&dig->username, &cred_info->username) == 0,
90
PJ_ASSERT_RETURN(pj_strcmp(&dig->realm, &cred_info->realm) == 0,
93
/* Prepare for our digest calculation. */
94
digest.ptr = digest_buf;
95
digest.slen = PJSIP_MD5STRLEN;
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,
108
/* Compare digest. */
109
return (pj_stricmp(&digest, &hdr->credential.digest.response) == 0) ?
110
PJ_SUCCESS : PJSIP_EAUTHINVALIDDIGEST;
113
pj_assert(!"Unsupported authentication scheme");
114
return PJSIP_EINVALIDAUTHSCHEME;
120
* Request the authorization server framework to verify the authorization
121
* information in the specified request in rdata.
123
PJ_DEF(pj_status_t) pjsip_auth_srv_verify( pjsip_auth_srv *auth_srv,
124
pjsip_rx_data *rdata,
127
pjsip_authorization_hdr *h_auth;
128
pjsip_msg *msg = rdata->msg_info.msg;
131
pjsip_cred_info cred_info;
134
PJ_ASSERT_RETURN(auth_srv && rdata, PJ_EINVAL);
135
PJ_ASSERT_RETURN(msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG);
137
htype = auth_srv->is_proxy ? PJSIP_H_PROXY_AUTHORIZATION :
138
PJSIP_H_AUTHORIZATION;
140
/* Initialize status with 200. */
143
/* Find authorization header for our realm. */
144
h_auth = (pjsip_authorization_hdr*) pjsip_msg_find_hdr(msg, htype, NULL);
146
if (!pj_stricmp(&h_auth->credential.common.realm, &auth_srv->realm))
149
h_auth = h_auth->next;
150
if (h_auth == (void*) &msg->hdr) {
155
h_auth=(pjsip_authorization_hdr*)pjsip_msg_find_hdr(msg,htype,h_auth);
159
*status_code = auth_srv->is_proxy ? 407 : 401;
160
return PJSIP_EAUTHNOAUTH;
163
/* Check authorization scheme. */
164
if (pj_stricmp(&h_auth->scheme, &pjsip_DIGEST_STR) == 0)
165
acc_name = h_auth->credential.digest.username;
167
*status_code = auth_srv->is_proxy ? 407 : 401;
168
return PJSIP_EINVALIDAUTHSCHEME;
171
/* Find the credential information for the account. */
172
if (auth_srv->lookup2) {
173
pjsip_auth_lookup_cred_param param;
175
pj_bzero(¶m, sizeof(param));
176
param.realm = auth_srv->realm;
177
param.acc_name = acc_name;
179
status = (*auth_srv->lookup2)(rdata->tp_info.pool, ¶m, &cred_info);
180
if (status != PJ_SUCCESS) {
181
*status_code = PJSIP_SC_FORBIDDEN;
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;
193
/* Authenticate with the specified credential. */
194
status = pjsip_auth_verify(h_auth, &msg->line.req.method.name,
196
if (status != PJ_SUCCESS) {
197
*status_code = PJSIP_SC_FORBIDDEN;
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
209
PJ_DEF(pj_status_t) pjsip_auth_srv_challenge( pjsip_auth_srv *auth_srv,
211
const pj_str_t *nonce,
212
const pj_str_t *opaque,
214
pjsip_tx_data *tdata)
216
pjsip_www_authenticate_hdr *hdr;
220
PJ_ASSERT_RETURN( auth_srv && tdata, PJ_EINVAL );
222
random.ptr = nonce_buf;
223
random.slen = sizeof(nonce_buf);
225
/* Create the header. */
226
if (auth_srv->is_proxy)
227
hdr = pjsip_proxy_authenticate_hdr_create(tdata->pool);
229
hdr = pjsip_www_authenticate_hdr_create(tdata->pool);
231
/* Initialize header.
232
* Note: only support digest authentication now.
234
hdr->scheme = pjsip_DIGEST_STR;
235
hdr->challenge.digest.algorithm = pjsip_MD5_STR;
237
pj_strdup(tdata->pool, &hdr->challenge.digest.nonce, nonce);
239
pj_create_random_string(nonce_buf, sizeof(nonce_buf));
240
pj_strdup(tdata->pool, &hdr->challenge.digest.nonce, &random);
243
pj_strdup(tdata->pool, &hdr->challenge.digest.opaque, opaque);
245
pj_create_random_string(nonce_buf, sizeof(nonce_buf));
246
pj_strdup(tdata->pool, &hdr->challenge.digest.opaque, &random);
249
pj_strdup(tdata->pool, &hdr->challenge.digest.qop, qop);
251
hdr->challenge.digest.qop.slen = 0;
253
pj_strdup(tdata->pool, &hdr->challenge.digest.realm, &auth_srv->realm);
254
hdr->challenge.digest.stale = stale;
256
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);