1
/* $Id: sip_auth_server.c 3553 2011-05-05 06:14:19Z 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_strdup( pool, &auth_srv->realm, realm);
44
auth_srv->lookup = lookup;
45
auth_srv->is_proxy = (options & PJSIP_AUTH_SRV_IS_PROXY);
51
/* Verify incoming Authorization/Proxy-Authorization header against the
52
* specified credential.
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 )
58
if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0) {
59
char digest_buf[PJSIP_MD5STRLEN];
61
const pjsip_digest_credential *dig = &hdr->credential.digest;
63
/* Check that username and realm match.
64
* These checks should have been performed before entering this
67
PJ_ASSERT_RETURN(pj_strcmp(&dig->username, &cred_info->username) == 0,
69
PJ_ASSERT_RETURN(pj_strcmp(&dig->realm, &cred_info->realm) == 0,
72
/* Prepare for our digest calculation. */
73
digest.ptr = digest_buf;
74
digest.slen = PJSIP_MD5STRLEN;
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,
88
return (pj_stricmp(&digest, &hdr->credential.digest.response) == 0) ?
89
PJ_SUCCESS : PJSIP_EAUTHINVALIDDIGEST;
92
pj_assert(!"Unsupported authentication scheme");
93
return PJSIP_EINVALIDAUTHSCHEME;
99
* Request the authorization server framework to verify the authorization
100
* information in the specified request in rdata.
102
PJ_DEF(pj_status_t) pjsip_auth_srv_verify( pjsip_auth_srv *auth_srv,
103
pjsip_rx_data *rdata,
106
pjsip_authorization_hdr *h_auth;
107
pjsip_msg *msg = rdata->msg_info.msg;
110
pjsip_cred_info cred_info;
113
PJ_ASSERT_RETURN(auth_srv && rdata, PJ_EINVAL);
114
PJ_ASSERT_RETURN(msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG);
116
htype = auth_srv->is_proxy ? PJSIP_H_PROXY_AUTHORIZATION :
117
PJSIP_H_AUTHORIZATION;
119
/* Initialize status with 200. */
122
/* Find authorization header for our realm. */
123
h_auth = (pjsip_authorization_hdr*) pjsip_msg_find_hdr(msg, htype, NULL);
125
if (!pj_stricmp(&h_auth->credential.common.realm, &auth_srv->realm))
128
h_auth = h_auth->next;
129
if (h_auth == (void*) &msg->hdr) {
134
h_auth=(pjsip_authorization_hdr*)pjsip_msg_find_hdr(msg,htype,h_auth);
138
*status_code = auth_srv->is_proxy ? 407 : 401;
139
return PJSIP_EAUTHNOAUTH;
142
/* Check authorization scheme. */
143
if (pj_stricmp(&h_auth->scheme, &pjsip_DIGEST_STR) == 0)
144
acc_name = h_auth->credential.digest.username;
146
*status_code = auth_srv->is_proxy ? 407 : 401;
147
return PJSIP_EINVALIDAUTHSCHEME;
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;
158
/* Authenticate with the specified credential. */
159
status = pjsip_auth_verify(h_auth, &msg->line.req.method.name,
161
if (status != PJ_SUCCESS) {
162
*status_code = PJSIP_SC_FORBIDDEN;
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
174
PJ_DEF(pj_status_t) pjsip_auth_srv_challenge( pjsip_auth_srv *auth_srv,
176
const pj_str_t *nonce,
177
const pj_str_t *opaque,
179
pjsip_tx_data *tdata)
181
pjsip_www_authenticate_hdr *hdr;
185
PJ_ASSERT_RETURN( auth_srv && tdata, PJ_EINVAL );
187
random.ptr = nonce_buf;
188
random.slen = sizeof(nonce_buf);
190
/* Create the header. */
191
if (auth_srv->is_proxy)
192
hdr = pjsip_proxy_authenticate_hdr_create(tdata->pool);
194
hdr = pjsip_www_authenticate_hdr_create(tdata->pool);
196
/* Initialize header.
197
* Note: only support digest authentication now.
199
hdr->scheme = pjsip_DIGEST_STR;
200
hdr->challenge.digest.algorithm = pjsip_MD5_STR;
202
pj_strdup(tdata->pool, &hdr->challenge.digest.nonce, nonce);
204
pj_create_random_string(nonce_buf, sizeof(nonce_buf));
205
pj_strdup(tdata->pool, &hdr->challenge.digest.nonce, &random);
208
pj_strdup(tdata->pool, &hdr->challenge.digest.opaque, opaque);
210
pj_create_random_string(nonce_buf, sizeof(nonce_buf));
211
pj_strdup(tdata->pool, &hdr->challenge.digest.opaque, &random);
214
pj_strdup(tdata->pool, &hdr->challenge.digest.qop, qop);
216
hdr->challenge.digest.qop.slen = 0;
218
pj_strdup(tdata->pool, &hdr->challenge.digest.realm, &auth_srv->realm);
219
hdr->challenge.digest.stale = stale;
221
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);