⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sip_auth_client.c

📁 基于sip协议的网络电话源码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* $Id: sip_auth_client.c 974 2007-02-19 01:13:53Z bennylp $ *//*  * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  */#include <pjsip/sip_auth.h>#include <pjsip/sip_auth_parser.h>	/* just to get pjsip_DIGEST_STR */#include <pjsip/sip_transport.h>#include <pjsip/sip_endpoint.h>#include <pjsip/sip_errno.h>#include <pjlib-util/md5.h>#include <pj/log.h>#include <pj/string.h>#include <pj/pool.h>#include <pj/guid.h>#include <pj/assert.h>#include <pj/ctype.h>/* A macro just to get rid of type mismatch between char and unsigned char */#define MD5_APPEND(pms,buf,len)	pj_md5_update(pms, (const pj_uint8_t*)buf, len)/* Logging. */#define THIS_FILE   "sip_auth_client.c"#if 0#  define AUTH_TRACE_(expr)  PJ_LOG(3, expr)#else#  define AUTH_TRACE_(expr)#endif/* Transform digest to string. * output must be at least PJSIP_MD5STRLEN+1 bytes. * * NOTE: THE OUTPUT STRING IS NOT NULL TERMINATED! */static void digest2str(const unsigned char digest[], char *output){    int i;    for (i = 0; i<16; ++i) {	pj_val_to_hex_digit(digest[i], output);	output += 2;    }}/* * Create response digest based on the parameters and store the * digest ASCII in 'result'.  */void pjsip_auth_create_digest( pj_str_t *result,			       const pj_str_t *nonce,			       const pj_str_t *nc,			       const pj_str_t *cnonce,			       const pj_str_t *qop,			       const pj_str_t *uri,			       const pjsip_cred_info *cred_info,			       const pj_str_t *method){    char ha1[PJSIP_MD5STRLEN];    char ha2[PJSIP_MD5STRLEN];    unsigned char digest[16];    pj_md5_context pms;    pj_assert(result->slen >= PJSIP_MD5STRLEN);    AUTH_TRACE_((THIS_FILE, "Begin creating digest"));    if (cred_info->data_type == PJSIP_CRED_DATA_PLAIN_PASSWD) {	/*** 	 *** ha1 = MD5(username ":" realm ":" password) 	 ***/	pj_md5_init(&pms);	MD5_APPEND( &pms, cred_info->username.ptr, cred_info->username.slen);	MD5_APPEND( &pms, ":", 1);	MD5_APPEND( &pms, cred_info->realm.ptr, cred_info->realm.slen);	MD5_APPEND( &pms, ":", 1);	MD5_APPEND( &pms, cred_info->data.ptr, cred_info->data.slen);	pj_md5_final(&pms, digest);	digest2str(digest, ha1);    } else if (cred_info->data_type == PJSIP_CRED_DATA_DIGEST) {	pj_assert(cred_info->data.slen == 32);	pj_memcpy( ha1, cred_info->data.ptr, cred_info->data.slen );    }    AUTH_TRACE_((THIS_FILE, "  ha1=%.32s", ha1));    /***     *** ha2 = MD5(method ":" req_uri)      ***/    pj_md5_init(&pms);    MD5_APPEND( &pms, method->ptr, method->slen);    MD5_APPEND( &pms, ":", 1);    MD5_APPEND( &pms, uri->ptr, uri->slen);    pj_md5_final(&pms, digest);    digest2str(digest, ha2);    AUTH_TRACE_((THIS_FILE, "  ha2=%.32s", ha2));    /***     *** When qop is not used:     ***    response = MD5(ha1 ":" nonce ":" ha2)      ***     *** When qop=auth is used:     ***    response = MD5(ha1 ":" nonce ":" nc ":" cnonce ":" qop ":" ha2)     ***/    pj_md5_init(&pms);    MD5_APPEND( &pms, ha1, PJSIP_MD5STRLEN);    MD5_APPEND( &pms, ":", 1);    MD5_APPEND( &pms, nonce->ptr, nonce->slen);    if (qop && qop->slen != 0) {	MD5_APPEND( &pms, ":", 1);	MD5_APPEND( &pms, nc->ptr, nc->slen);	MD5_APPEND( &pms, ":", 1);	MD5_APPEND( &pms, cnonce->ptr, cnonce->slen);	MD5_APPEND( &pms, ":", 1);	MD5_APPEND( &pms, qop->ptr, qop->slen);    }    MD5_APPEND( &pms, ":", 1);    MD5_APPEND( &pms, ha2, PJSIP_MD5STRLEN);    /* This is the final response digest. */    pj_md5_final(&pms, digest);        /* Convert digest to string and store in chal->response. */    result->slen = PJSIP_MD5STRLEN;    digest2str(digest, result->ptr);    AUTH_TRACE_((THIS_FILE, "  digest=%.32s", result->ptr));    AUTH_TRACE_((THIS_FILE, "Digest created"));}/* * Finds out if qop offer contains "auth" token. */static pj_bool_t has_auth_qop( pj_pool_t *pool, const pj_str_t *qop_offer){    pj_str_t qop;    char *p;    pj_strdup_with_null( pool, &qop, qop_offer);    p = qop.ptr;    while (*p) {	*p = (char)pj_tolower(*p);	++p;    }    p = qop.ptr;    while (*p) {	if (*p=='a' && *(p+1)=='u' && *(p+2)=='t' && *(p+3)=='h') {	    int e = *(p+4);	    if (e=='"' || e==',' || e==0)		return PJ_TRUE;	    else		p += 4;	} else {	    ++p;	}    }    return PJ_FALSE;}/* * Generate response digest.  * Most of the parameters to generate the digest (i.e. username, realm, uri, * and nonce) are expected to be in the credential. Additional parameters (i.e. * password and method param) should be supplied in the argument. * * The resulting digest will be stored in cred->response. * The pool is used to allocate 32 bytes to store the digest in cred->response. */static pj_status_t respond_digest( pj_pool_t *pool,				   pjsip_digest_credential *cred,				   const pjsip_digest_challenge *chal,				   const pj_str_t *uri,				   const pjsip_cred_info *cred_info,				   const pj_str_t *cnonce,				   pj_uint32_t nc,				   const pj_str_t *method){    /* Check algorithm is supported. We only support MD5. */    if (chal->algorithm.slen && pj_stricmp(&chal->algorithm, &pjsip_MD5_STR))    {	PJ_LOG(4,(THIS_FILE, "Unsupported digest algorithm \"%.*s\"",		  chal->algorithm.slen, chal->algorithm.ptr));	return PJSIP_EINVALIDALGORITHM;    }    /* Build digest credential from arguments. */    pj_strdup(pool, &cred->username, &cred_info->username);    pj_strdup(pool, &cred->realm, &chal->realm);    pj_strdup(pool, &cred->nonce, &chal->nonce);    pj_strdup(pool, &cred->uri, uri);    cred->algorithm = pjsip_MD5_STR;    pj_strdup(pool, &cred->opaque, &chal->opaque);        /* Allocate memory. */    cred->response.ptr = pj_pool_alloc(pool, PJSIP_MD5STRLEN);    cred->response.slen = PJSIP_MD5STRLEN;    if (chal->qop.slen == 0) {	/* Server doesn't require quality of protection. */	/* Convert digest to string and store in chal->response. */	pjsip_auth_create_digest( &cred->response, &cred->nonce, NULL, NULL, 				  NULL, uri, cred_info, method);    } else if (has_auth_qop(pool, &chal->qop)) {	/* Server requires quality of protection. 	 * We respond with selecting "qop=auth" protection.	 */	cred->qop = pjsip_AUTH_STR;	cred->nc.ptr = pj_pool_alloc(pool, 16);	cred->nc.slen = pj_ansi_snprintf(cred->nc.ptr, 16, "%08u", nc);	if (cnonce && cnonce->slen) {	    pj_strdup(pool, &cred->cnonce, cnonce);	} else {	    pj_str_t dummy_cnonce = { "b39971", 6};	    pj_strdup(pool, &cred->cnonce, &dummy_cnonce);	}	pjsip_auth_create_digest( &cred->response, &cred->nonce, &cred->nc, 				  cnonce, &pjsip_AUTH_STR, uri, cred_info, 				  method );    } else {	/* Server requires quality protection that we don't support. */	PJ_LOG(4,(THIS_FILE, "Unsupported qop offer %.*s", 		  chal->qop.slen, chal->qop.ptr));	return PJSIP_EINVALIDQOP;    }    return PJ_SUCCESS;}#if defined(PJSIP_AUTH_QOP_SUPPORT) && PJSIP_AUTH_QOP_SUPPORT!=0/* * Update authentication session with a challenge. */static void update_digest_session( pj_pool_t *ses_pool, 				   pjsip_cached_auth *cached_auth,				   const pjsip_www_authenticate_hdr *hdr ){    if (hdr->challenge.digest.qop.slen == 0)	return;    /* Initialize cnonce and qop if not present. */    if (cached_auth->cnonce.slen == 0) {	/* Save the whole challenge */	cached_auth->last_chal = pjsip_hdr_clone(ses_pool, hdr);	/* Create cnonce */	pj_create_unique_string( ses_pool, &cached_auth->cnonce );	/* Initialize nonce-count */	cached_auth->nc = 1;	/* Save realm. */	pj_assert(cached_auth->realm.slen != 0);	if (cached_auth->realm.slen == 0) {	    pj_strdup(ses_pool, &cached_auth->realm, 		      &hdr->challenge.digest.realm);	}    } else {	/* Update last_nonce and nonce-count */	if (!pj_strcmp(&hdr->challenge.digest.nonce, 		       &cached_auth->last_chal->challenge.digest.nonce)) 	{	    /* Same nonce, increment nonce-count */	    ++cached_auth->nc;	} else {	    /* Server gives new nonce. */	    pj_strdup(ses_pool, &cached_auth->last_chal->challenge.digest.nonce,		      &hdr->challenge.digest.nonce);	    /* Has the opaque changed? */	    if (pj_strcmp(&cached_auth->last_chal->challenge.digest.opaque,			  &hdr->challenge.digest.opaque)) 	    {		pj_strdup(ses_pool, 			  &cached_auth->last_chal->challenge.digest.opaque,			  &hdr->challenge.digest.opaque);	    }	    cached_auth->nc = 1;	}    }}#endif	/* PJSIP_AUTH_QOP_SUPPORT *//* Find cached authentication in the list for the specified realm. */static pjsip_cached_auth *find_cached_auth( pjsip_auth_clt_sess *sess,					    const pj_str_t *realm ){    pjsip_cached_auth *auth = sess->cached_auth.next;    while (auth != &sess->cached_auth) {	if (pj_stricmp(&auth->realm, realm) == 0)	    return auth;	auth = auth->next;    }    return NULL;}/* Find credential to use for the specified realm and auth scheme. */static const pjsip_cred_info* auth_find_cred( const pjsip_auth_clt_sess *sess,					      const pj_str_t *realm,					      const pj_str_t *auth_scheme){    unsigned i;    PJ_UNUSED_ARG(auth_scheme);    for (i=0; i<sess->cred_cnt; ++i) {	if (pj_stricmp(&sess->cred_info[i].realm, realm) == 0)	    return &sess->cred_info[i];    }    return NULL;}/* Init client session. */PJ_DEF(pj_status_t) pjsip_auth_clt_init(  pjsip_auth_clt_sess *sess,					  pjsip_endpoint *endpt,					  pj_pool_t *pool, 					  unsigned options){    PJ_ASSERT_RETURN(sess && endpt && pool && (options==0), PJ_EINVAL);    sess->pool = pool;    sess->endpt = endpt;    sess->cred_cnt = 0;    sess->cred_info = NULL;    pj_list_init(&sess->cached_auth);    return PJ_SUCCESS;}/* Clone session. */PJ_DEF(pj_status_t) pjsip_auth_clt_clone( pj_pool_t *pool,					  pjsip_auth_clt_sess *sess,					  const pjsip_auth_clt_sess *rhs ){    unsigned i;    PJ_ASSERT_RETURN(pool && sess && rhs, PJ_EINVAL);    sess->pool = pool;    sess->endpt = (pjsip_endpoint*)rhs->endpt;    sess->cred_cnt = rhs->cred_cnt;    sess->cred_info = pj_pool_alloc(pool, 				    sess->cred_cnt*sizeof(pjsip_cred_info));    for (i=0; i<rhs->cred_cnt; ++i) {	pj_strdup(pool, &sess->cred_info[i].realm, &rhs->cred_info[i].realm);	pj_strdup(pool, &sess->cred_info[i].scheme, &rhs->cred_info[i].scheme);	pj_strdup(pool, &sess->cred_info[i].username, 		  &rhs->cred_info[i].username);	sess->cred_info[i].data_type = rhs->cred_info[i].data_type;	pj_strdup(pool, &sess->cred_info[i].data, &rhs->cred_info[i].data);    }    /* TODO note:     * Cloning the full authentication client is quite a big task.     * We do only the necessary bits here, i.e. cloning the credentials.     * The drawback of this basic approach is, a forked dialog will have to     * re-authenticate itself on the next request because it has lost the     * cached authentication headers.     */    PJ_TODO(FULL_CLONE_OF_AUTH_CLIENT_SESSION);    return PJ_SUCCESS;}/* Set client credentials. */PJ_DEF(pj_status_t) pjsip_auth_clt_set_credentials( pjsip_auth_clt_sess *sess,						    int cred_cnt,						    const pjsip_cred_info *c){    PJ_ASSERT_RETURN(sess && c, PJ_EINVAL);    if (cred_cnt == 0) {	sess->cred_cnt = 0;    } else {	int i;	sess->cred_info = pj_pool_alloc(sess->pool, cred_cnt * sizeof(*c));	for (i=0; i<cred_cnt; ++i) {	    sess->cred_info[i].data_type = c[i].data_type;	    pj_strdup(sess->pool, &sess->cred_info[i].scheme, &c[i].scheme);	    pj_strdup(sess->pool, &sess->cred_info[i].realm, &c[i].realm);	    pj_strdup(sess->pool, &sess->cred_info[i].username, &c[i].username);	    pj_strdup(sess->pool, &sess->cred_info[i].data, &c[i].data);	}	sess->cred_cnt = cred_cnt;    }    return PJ_SUCCESS;}/*  * Create Authorization/Proxy-Authorization response header based on the challege * in WWW-Authenticate/Proxy-Authenticate header. */static pj_status_t auth_respond( pj_pool_t *req_pool,				 const pjsip_www_authenticate_hdr *hdr,				 const pjsip_uri *uri,				 const pjsip_cred_info *cred_info,				 const pjsip_method *method,				 pj_pool_t *sess_pool,				 pjsip_cached_auth *cached_auth,				 pjsip_authorization_hdr **p_h_auth){

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -