summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2007-10-15 07:04:59 +0000
committerBenny Prijono <bennylp@teluu.com>2007-10-15 07:04:59 +0000
commit4b289329774686a3e261fac70fbd902942cb9b1f (patch)
tree2afebdecbce79f65ab386003be09649736665f7a
parentcee3cd46bbeec0bb7e76a5480e7cad9ee2f8cda5 (diff)
Continuing ticket #396: tested digest AKAv1, implemented AKAv2, and some works in the authentication framework to support it
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1500 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--build.symbian/pjsip.mmp1
-rw-r--r--pjsip-apps/src/pjsua/pjsua_app.c5
-rw-r--r--pjsip/include/pjsip/print_util.h12
-rw-r--r--pjsip/include/pjsip/sip_auth.h3
-rw-r--r--pjsip/include/pjsip/sip_auth_aka.h155
-rw-r--r--pjsip/include/pjsip/sip_config.h9
-rw-r--r--pjsip/src/pjsip/sip_auth_aka.c127
-rw-r--r--pjsip/src/pjsip/sip_auth_client.c56
-rw-r--r--pjsip/src/pjsip/sip_auth_msg.c4
-rw-r--r--pjsip/src/pjsua-lib/pjsua_acc.c21
10 files changed, 325 insertions, 68 deletions
diff --git a/build.symbian/pjsip.mmp b/build.symbian/pjsip.mmp
index efaf9bd5..513a7dee 100644
--- a/build.symbian/pjsip.mmp
+++ b/build.symbian/pjsip.mmp
@@ -32,6 +32,7 @@ OPTION GCC -x c++
// PJSIP-CORE files
+//SOURCE sip_auth_aka.c
SOURCE sip_auth_client.c
SOURCE sip_auth_msg.c
SOURCE sip_auth_parser.c
diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c
index cc3cacac..80dac5da 100644
--- a/pjsip-apps/src/pjsua/pjsua_app.c
+++ b/pjsip-apps/src/pjsua/pjsua_app.c
@@ -671,6 +671,11 @@ static pj_status_t parse_args(int argc, char *argv[],
case OPT_PASSWORD: /* authentication password */
cur_acc->cred_info[cur_acc->cred_count].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
cur_acc->cred_info[cur_acc->cred_count].data = pj_str(pj_optarg);
+#if PJSIP_HAS_DIGEST_AKA_AUTH
+ cur_acc->cred_info[cur_acc->cred_count].data_type |= PJSIP_CRED_DATA_EXT_AKA;
+ cur_acc->cred_info[cur_acc->cred_count].ext.aka.k = pj_str(pj_optarg);
+ cur_acc->cred_info[cur_acc->cred_count].ext.aka.cb = &pjsip_auth_create_aka_response;
+#endif
break;
case OPT_NEXT_CRED: /* next credential */
diff --git a/pjsip/include/pjsip/print_util.h b/pjsip/include/pjsip/print_util.h
index d7f30a08..6c9afa34 100644
--- a/pjsip/include/pjsip/print_util.h
+++ b/pjsip/include/pjsip/print_util.h
@@ -50,6 +50,17 @@
} \
} while (0)
+#define copy_advance_pair_quote(buf,str1,len1,str2,quotebegin,quoteend) \
+ do { \
+ printed = len1+str2.slen+2; \
+ if (printed >= (endbuf-buf)) return -1; \
+ pj_memcpy(buf,str1,len1); \
+ *(buf+len1)=quotebegin; \
+ pj_memcpy(buf+len1+1, str2.ptr, str2.slen); \
+ *(buf+printed-1) = quoteend; \
+ buf += printed; \
+ } while (0)
+
#define copy_advance_pair_escape(buf,str1,len1,str2,unres) \
do { \
if (str2.slen) { \
@@ -85,7 +96,6 @@
#define copy_advance copy_advance_check
#define copy_advance_pair copy_advance_pair_check
-#define copy_advance_pair_quote copy_advance_pair_quote_check
#define copy_advance_pair_quote_cond(buf,str1,len1,str2,quotebegin,quoteend) \
do { \
diff --git a/pjsip/include/pjsip/sip_auth.h b/pjsip/include/pjsip/sip_auth.h
index 521adfaa..466e85c2 100644
--- a/pjsip/include/pjsip/sip_auth.h
+++ b/pjsip/include/pjsip/sip_auth.h
@@ -121,9 +121,10 @@ struct pjsip_cred_info
/** Digest AKA credential information. Note that when AKA credential
* is being used, the \a data field of this #pjsip_cred_info is
* not used, but it still must be initialized to an empty string.
+ * Please see \ref PJSIP_AUTH_AKA_API for more information.
*/
struct {
- pj_str_t k; /**< Permanent key. */
+ pj_str_t k; /**< Permanent subscriber key. */
pj_str_t op; /**< Operator variant key. */
pj_str_t amf; /**< Authentication Management Field */
pjsip_cred_cb cb; /**< Callback to create AKA digest. */
diff --git a/pjsip/include/pjsip/sip_auth_aka.h b/pjsip/include/pjsip/sip_auth_aka.h
index c5c2a60d..2c8030ab 100644
--- a/pjsip/include/pjsip/sip_auth_aka.h
+++ b/pjsip/include/pjsip/sip_auth_aka.h
@@ -29,49 +29,170 @@
PJ_BEGIN_DECL
/**
- * @defgroup PJSIP_AUTH_AKA_API Digest AKA Authentication API's
+ * @defgroup PJSIP_AUTH_AKA_API Digest AKAv1 and AKAv2 Authentication API
* @ingroup PJSIP_AUTH_API
- * @brief Digest AKA helper API.
+ * @brief Digest AKAv1 and AKAv2 Authentication API
* @{
*
- * This module currently exports one function, #pjsip_auth_create_akav1_response(),
- * which can be registered as the callback function in \a ext.aka.cb field
- * of #pjsip_cred_info structure, to calculate the MD5-AKAv1 digest
- * response.
- */
+ * This module implements HTTP digest authentication using Authentication
+ * and Key Agreement (AKA) version 1 and version 2 (AKAv1-MD5 and AKAv2-MD5),
+ * as specified in RFC 3310 and RFC 4169. SIP AKA authentication is used
+ * by 3GPP and IMS systems.
+ *
+ * @section pjsip_aka_using Using Digest AKA Authentication
+ *
+ * Support for digest AKA authentication is currently made optional, so
+ * application needs to declare \a PJSIP_HAS_DIGEST_AKA_AUTH to non-zero
+ * in <tt>config_site.h</tt> to enable AKA support:
+ *
+ @code
+ #define PJSIP_HAS_DIGEST_AKA_AUTH 1
+ @endcode
+
+ *
+ * In addition, application would need to link with <b>libmilenage</b>
+ * library from \a third_party directory.
+ *
+ * Application then specifies digest AKA credential by initializing the
+ * authentication credential as follows:
+ *
+ @code
+
+ pjsip_cred_info cred;
+
+ pj_bzero(&cred, sizeof(cred));
+
+ cred.scheme = pj_str("Digest");
+ cred.realm = pj_str("ims-domain.test");
+ cred.username = pj_str("user@ims-domain.test");
+ cred.data_type = PJSIP_CRED_DATA_PLAIN_PASSWD | PJSIP_CRED_DATA_EXT_AKA;
+ cred.data = pj_str("password");
+ // AKA extended info
+ cred.ext.aka.k = pj_str("password");
+ cred.ext.aka.cb = &pjsip_auth_create_aka_response
+
+ @endcode
+ *
+ * Description:
+ * - To support AKA, application adds \a PJSIP_CRED_DATA_EXT_AKA flag in the
+ * \a data_type field. This indicates that extended information specific to
+ * AKA authentication is available in the credential, and that response
+ * digest computation will use the callback function instead of the usual MD5
+ * digest computation.
+ *
+ * - The \a scheme for the credential is "Digest".
+ *
+ * - The \a realm is the expected realm in the challenge. Application may
+ * also specify wildcard realm ("*") if it wishes to respond to any realms
+ * in the challenge.
+ *
+ * - The \a data field is optional. Application may fill this with the password
+ * if it wants to support both MD5 and AKA MD5 in a single credential. The
+ * pjsip_auth_create_aka_response() function will use this field if the
+ * challenge indicates "MD5" as the algorithm instead of "AKAv1-MD5" or
+ * "AKAv2-MD5".
+ *
+ * - The \a ext.aka.k field specifies the permanent subscriber key to be used
+ * for AKA authentication. Application may specify binary password containing
+ * NULL character in this key, since the length of the key is indicated in
+ * the \a slen field of the string.
+ *
+ * - The \a ext.aka.cb field specifies the callback function to calculate the
+ * response digest. Application can specify pjsip_auth_create_aka_response()
+ * in this field to use PJSIP's implementation, but it's free to provide
+ * it's own function.
+ *
+ * - Optionally application may set \a ext.aka.op and \a ext.aka.amf in the
+ * credential to specify AKA Operator variant key and AKA Authentication
+ * Management Field information.
+ */
+/**
+ * Length of Authentication Key (AK) in bytes.
+ */
#define PJSIP_AKA_AKLEN 6
+
+/**
+ * Length of Authentication Management Field (AMF) in bytes.
+ */
#define PJSIP_AKA_AMFLEN 2
+
+/**
+ * Length of AUTN in bytes.
+ */
#define PJSIP_AKA_AUTNLEN 16
+
+/**
+ * Length of Confidentiality Key (CK) in bytes.
+ */
#define PJSIP_AKA_CKLEN 16
+
+/**
+ * Length of Integrity Key (AK) in bytes.
+ */
#define PJSIP_AKA_IKLEN 16
+
+/**
+ * Length of permanent/subscriber Key (K) in bytes.
+ */
#define PJSIP_AKA_KLEN 16
+
+/**
+ * Length of AKA authentication code in bytes.
+ */
+#define PJSIP_AKA_MACLEN 8
+
+/**
+ * Length of operator key in bytes.
+ */
#define PJSIP_AKA_OPLEN 16
+
+/**
+ * Length of random challenge (RAND) in bytes.
+ */
#define PJSIP_AKA_RANDLEN 16
+
+/**
+ * Length of response digest in bytes.
+ */
#define PJSIP_AKA_RESLEN 8
-#define PJSIP_AKA_MACLEN 8
/**
- * This function creates MD5 AKAv1 response for the specified challenge
- * in \a chal, based on the information in the credential \a cred.
+ * Length of sequence number (SQN) in bytes.
+ */
+#define PJSIP_AKA_SQNLEN 6
+
+/**
+ * This function creates MD5, AKAv1-MD5, or AKAv2-MD5 response for
+ * the specified challenge in \a chal, according to the algorithm
+ * specified in the challenge, and based on the information in the
+ * credential \a cred.
+ *
* Application may register this function as \a ext.aka.cb field of
* #pjsip_cred_info structure to make PJSIP automatically call this
- * function to calculate the response digest.
+ * function to calculate the response digest. To do so, it needs to
+ * add \a PJSIP_CRED_DATA_EXT_AKA flag in the \a data_type field of
+ * the credential, and fills up other AKA specific information in
+ * the credential.
*
* @param pool Pool to allocate memory.
* @param chal The authentication challenge sent by server in 401
- * or 401 response, in either Proxy-Authenticate or
+ * or 401 response, as either Proxy-Authenticate or
* WWW-Authenticate header.
- * @param cred The credential that has been selected by the framework
- * to authenticate against the challenge.
+ * @param cred The credential to be used.
* @param method The request method.
- * @param auth The authentication credential where the digest response
- * will be placed to.
+ * @param auth The digest credential where the digest response
+ * will be placed to. Upon calling this function, the
+ * nonce, nc, cnonce, qop, uri, and realm fields of
+ * this structure must have been set by caller. Upon
+ * return, the \a response field will be initialized
+ * by this function.
*
* @return PJ_SUCCESS if response has been created successfully.
*/
-PJ_DECL(pj_status_t) pjsip_auth_create_akav1(pj_pool_t *pool,
+PJ_DECL(pj_status_t) pjsip_auth_create_aka_response(
+ pj_pool_t *pool,
const pjsip_digest_challenge*chal,
const pjsip_cred_info *cred,
const pj_str_t *method,
diff --git a/pjsip/include/pjsip/sip_config.h b/pjsip/include/pjsip/sip_config.h
index 82af631b..5dcfa09c 100644
--- a/pjsip/include/pjsip/sip_config.h
+++ b/pjsip/include/pjsip/sip_config.h
@@ -616,12 +616,13 @@
/**
- * Specify support for IMS/3GPP digest AKA authentication.
+ * Specify support for IMS/3GPP digest AKA authentication version 1 and 2
+ * (AKAv1-MD5 and AKAv2-MD5 respectively).
*
- * Default: 0 (disabled for now)
+ * Default: 0 (disabled, for now)
*/
-#ifndef PJSIP_HAS_DIGEST_AKAV1_AUTH
-# define PJSIP_HAS_DIGEST_AKAV1_AUTH 0
+#ifndef PJSIP_HAS_DIGEST_AKA_AUTH
+# define PJSIP_HAS_DIGEST_AKA_AUTH 0
#endif
diff --git a/pjsip/src/pjsip/sip_auth_aka.c b/pjsip/src/pjsip/sip_auth_aka.c
index 284b8c0f..75d649ba 100644
--- a/pjsip/src/pjsip/sip_auth_aka.c
+++ b/pjsip/src/pjsip/sip_auth_aka.c
@@ -20,44 +20,68 @@
#include <pjsip/sip_errno.h>
#include <pjlib-util/base64.h>
#include <pjlib-util/md5.h>
+#include <pjlib-util/hmac_md5.h>
#include <pj/assert.h>
#include <pj/log.h>
#include <pj/pool.h>
#include <pj/string.h>
-#if PJSIP_HAS_DIGEST_AKAV1_AUTH
+#if PJSIP_HAS_DIGEST_AKA_AUTH
#include "../../third_party/milenage/milenage.h"
/*
* Create MD5-AKA1 digest response.
*/
-PJ_DEF(pj_status_t) pjsip_auth_create_akav1( pj_pool_t *pool,
+PJ_DEF(pj_status_t) pjsip_auth_create_aka_response(
+ pj_pool_t *pool,
const pjsip_digest_challenge*chal,
const pjsip_cred_info *cred,
const pj_str_t *method,
pjsip_digest_credential *auth)
{
pj_str_t nonce_bin;
- pj_uint8_t *chal_rand, *chal_autn, *chal_mac;
+ int aka_version;
+ const pj_str_t pjsip_AKAv1_MD5 = { "AKAv1-MD5", 9 };
+ const pj_str_t pjsip_AKAv2_MD5 = { "AKAv2-MD5", 9 };
+ pj_uint8_t *chal_rand, *chal_sqnxoraka, *chal_mac;
+ pj_uint8_t k[PJSIP_AKA_KLEN];
+ pj_uint8_t op[PJSIP_AKA_OPLEN];
+ pj_uint8_t amf[PJSIP_AKA_AMFLEN];
pj_uint8_t res[PJSIP_AKA_RESLEN];
pj_uint8_t ck[PJSIP_AKA_CKLEN];
pj_uint8_t ik[PJSIP_AKA_IKLEN];
pj_uint8_t ak[PJSIP_AKA_AKLEN];
- pj_uint8_t sqn[PJSIP_AKA_AUTNLEN];
+ pj_uint8_t sqn[PJSIP_AKA_SQNLEN];
pj_uint8_t xmac[PJSIP_AKA_MACLEN];
pjsip_cred_info aka_cred;
int i, len;
pj_status_t status;
/* Check the algorithm is supported. */
- if (pj_stricmp2(&chal->algorithm, "md5") == 0) {
+ if (chal->algorithm.slen==0 || pj_stricmp2(&chal->algorithm, "md5") == 0) {
+ /*
+ * A normal MD5 authentication is requested. Fallbackt to the usual
+ * MD5 digest creation.
+ */
pjsip_auth_create_digest(&auth->response, &auth->nonce, &auth->nc,
&auth->cnonce, &auth->qop, &auth->uri,
&auth->realm, cred, method);
return PJ_SUCCESS;
- } else if (pj_stricmp2(&chal->algorithm, "AKAv1-MD5") != 0) {
+ } else if (pj_stricmp(&chal->algorithm, &pjsip_AKAv1_MD5) == 0) {
+ /*
+ * AKA version 1 is requested.
+ */
+ aka_version = 1;
+
+ } else if (pj_stricmp(&chal->algorithm, &pjsip_AKAv2_MD5) == 0) {
+ /*
+ * AKA version 2 is requested.
+ */
+ aka_version = 2;
+
+ } else {
/* Unsupported algorithm */
return PJSIP_EINVALIDALGORITHM;
}
@@ -70,36 +94,40 @@ PJ_DEF(pj_status_t) pjsip_auth_create_akav1( pj_pool_t *pool,
if (status != PJ_SUCCESS)
return PJSIP_EAUTHINNONCE;
- if (nonce_bin.slen < PJSIP_AKA_RANDLEN + PJSIP_AKA_AUTNLEN + PJSIP_AKA_MACLEN)
+ if (nonce_bin.slen < PJSIP_AKA_RANDLEN + PJSIP_AKA_AUTNLEN)
return PJSIP_EAUTHINNONCE;
/* Get RAND, AUTN, and MAC */
- chal_rand = (pj_uint8_t*) (nonce_bin.ptr + 0);
- chal_autn = (pj_uint8_t*) (nonce_bin.ptr + PJSIP_AKA_RANDLEN);
- chal_mac = (pj_uint8_t*) (nonce_bin.ptr + PJSIP_AKA_RANDLEN + PJSIP_AKA_AUTNLEN);
-
- /* Verify credential */
- PJ_ASSERT_RETURN(cred->ext.aka.k.slen == PJSIP_AKA_KLEN, PJSIP_EAUTHINAKACRED);
- PJ_ASSERT_RETURN(cred->ext.aka.op.slen == PJSIP_AKA_OPLEN, PJSIP_EAUTHINAKACRED);
+ chal_rand = (pj_uint8_t*)(nonce_bin.ptr + 0);
+ chal_sqnxoraka = (pj_uint8_t*) (nonce_bin.ptr + PJSIP_AKA_RANDLEN);
+ chal_mac = (pj_uint8_t*) (nonce_bin.ptr + PJSIP_AKA_RANDLEN +
+ PJSIP_AKA_SQNLEN + PJSIP_AKA_AMFLEN);
+
+ /* Copy k. op, and amf */
+ pj_bzero(k, sizeof(k));
+ pj_bzero(op, sizeof(op));
+ pj_bzero(amf, sizeof(amf));
+
+ if (cred->ext.aka.k.slen)
+ pj_memcpy(k, cred->ext.aka.k.ptr, cred->ext.aka.k.slen);
+ if (cred->ext.aka.op.slen)
+ pj_memcpy(op, cred->ext.aka.op.ptr, cred->ext.aka.op.slen);
+ if (cred->ext.aka.amf.slen)
+ pj_memcpy(amf, cred->ext.aka.amf.ptr, cred->ext.aka.amf.slen);
/* Given key K and random challenge RAND, compute response RES,
* confidentiality key CK, integrity key IK and anonymity key AK.
*/
- f2345((pj_uint8_t*)cred->ext.aka.k.ptr,
- chal_rand,
- res, ck, ik, ak,
- (pj_uint8_t*)cred->ext.aka.op.ptr);
+ f2345(k, chal_rand, res, ck, ik, ak, op);
/* Compute sequence number SQN */
- for (i=0; i<PJSIP_AKA_AUTNLEN; ++i)
- sqn[i] = (pj_uint8_t) (chal_autn[i] ^ ak[i]);
+ for (i=0; i<PJSIP_AKA_SQNLEN; ++i)
+ sqn[i] = (pj_uint8_t) (chal_sqnxoraka[i] ^ ak[i]);
+ /* Verify MAC in the challenge */
/* Compute XMAC */
- f1((pj_uint8_t*)cred->ext.aka.k.ptr, chal_rand, sqn,
- (pj_uint8_t*)cred->ext.aka.amf.ptr, xmac,
- (pj_uint8_t*)cred->ext.aka.op.ptr);
+ f1(k, chal_rand, sqn, amf, xmac, op);
- /* Verify MAC in the challenge */
if (pj_memcmp(chal_mac, xmac, PJSIP_AKA_MACLEN) != 0) {
return PJSIP_EAUTHINNONCE;
}
@@ -109,18 +137,57 @@ PJ_DEF(pj_status_t) pjsip_auth_create_akav1( pj_pool_t *pool,
*/
pj_memcpy(&aka_cred, cred, sizeof(aka_cred));
aka_cred.data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
- aka_cred.data.ptr = (char*)res;
- aka_cred.data.slen = PJSIP_AKA_RESLEN;
/* Create a response */
- pjsip_auth_create_digest(&auth->response, &chal->nonce,
- &auth->nc, &auth->cnonce, &auth->qop, &auth->uri,
- &chal->realm, &aka_cred, method);
+ if (aka_version == 1) {
+ /*
+ * For AKAv1, the password is RES
+ */
+ aka_cred.data.ptr = (char*)res;
+ aka_cred.data.slen = PJSIP_AKA_RESLEN;
+
+ pjsip_auth_create_digest(&auth->response, &chal->nonce,
+ &auth->nc, &auth->cnonce, &auth->qop,
+ &auth->uri, &chal->realm, &aka_cred, method);
+
+ } else if (aka_version == 2) {
+ /*
+ * For AKAv2, password is base64 encoded [1] parameters:
+ * PRF(RES||IK||CK,"http-digest-akav2-password")
+ *
+ * The pseudo-random function (PRF) is HMAC-MD5 in this case.
+ */
+ pj_hmac_md5_context ctx;
+ pj_uint8_t hmac_digest[16];
+ char hmac_digest64[24];
+ int out_len;
+
+ pj_hmac_md5_init(&ctx, (pj_uint8_t*)"http-digest-akav2-password", 26);
+ pj_hmac_md5_update(&ctx, res, PJSIP_AKA_RESLEN);
+ pj_hmac_md5_update(&ctx, ik, PJSIP_AKA_IKLEN);
+ pj_hmac_md5_update(&ctx, ck, PJSIP_AKA_CKLEN);
+ pj_hmac_md5_final(&ctx, hmac_digest);
+
+ out_len = sizeof(hmac_digest64);
+ status = pj_base64_encode(hmac_digest, 16, hmac_digest64, &out_len);
+ PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+
+ aka_cred.data.ptr = hmac_digest64;
+ aka_cred.data.slen = out_len;
+
+ pjsip_auth_create_digest(&auth->response, &chal->nonce,
+ &auth->nc, &auth->cnonce, &auth->qop,
+ &auth->uri, &chal->realm, &aka_cred, method);
+
+ } else {
+ pj_assert(!"Bug!");
+ return PJ_EBUG;
+ }
/* Done */
return PJ_SUCCESS;
}
-#endif /* PJSIP_HAS_DIGEST_AKAV1_AUTH */
+#endif /* PJSIP_HAS_DIGEST_AKA_AUTH */
diff --git a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c
index 0a3049e2..2001270e 100644
--- a/pjsip/src/pjsip/sip_auth_client.c
+++ b/pjsip/src/pjsip/sip_auth_client.c
@@ -48,6 +48,18 @@
#define EXT_MASK 0x00F0
+static void dup_bin(pj_pool_t *pool, pj_str_t *dst, const pj_str_t *src)
+{
+ dst->slen = src->slen;
+
+ if (dst->slen) {
+ dst->ptr = (char*) pj_pool_alloc(pool, src->slen);
+ pj_memcpy(dst->ptr, src->ptr, src->slen);
+ } else {
+ dst->ptr = NULL;
+ }
+}
+
PJ_DEF(void) pjsip_cred_info_dup(pj_pool_t *pool,
pjsip_cred_info *dst,
const pjsip_cred_info *src)
@@ -60,9 +72,9 @@ PJ_DEF(void) pjsip_cred_info_dup(pj_pool_t *pool,
pj_strdup_with_null(pool, &dst->data, &src->data);
if ((dst->data_type & EXT_MASK) == PJSIP_CRED_DATA_EXT_AKA) {
- pj_strdup(pool, &dst->ext.aka.k, &src->ext.aka.k);
- pj_strdup(pool, &dst->ext.aka.op, &src->ext.aka.op);
- pj_strdup(pool, &dst->ext.aka.amf, &src->ext.aka.amf);
+ dup_bin(pool, &dst->ext.aka.k, &src->ext.aka.k);
+ dup_bin(pool, &dst->ext.aka.op, &src->ext.aka.op);
+ dup_bin(pool, &dst->ext.aka.amf, &src->ext.aka.amf);
}
}
@@ -222,9 +234,16 @@ static pj_status_t respond_digest( pj_pool_t *pool,
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))
+ const pj_str_t pjsip_AKAv1_MD5_STR = { "AKAv1-MD5", 9 };
+
+ /* Check algorithm is supported. We support MD5 and AKAv1-MD5. */
+ if (chal->algorithm.slen==0 ||
+ (pj_stricmp(&chal->algorithm, &pjsip_MD5_STR) ||
+ pj_stricmp(&chal->algorithm, &pjsip_AKAv1_MD5_STR)))
{
+ ;
+ }
+ else {
PJ_LOG(4,(THIS_FILE, "Unsupported digest algorithm \"%.*s\"",
chal->algorithm.slen, chal->algorithm.ptr));
return PJSIP_EINVALIDALGORITHM;
@@ -235,9 +254,9 @@ static pj_status_t respond_digest( pj_pool_t *pool,
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->algorithm, &chal->algorithm);
pj_strdup(pool, &cred->opaque, &chal->opaque);
-
+
/* Allocate memory. */
cred->response.ptr = (char*) pj_pool_alloc(pool, PJSIP_MD5STRLEN);
cred->response.slen = PJSIP_MD5STRLEN;
@@ -470,8 +489,8 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_set_credentials( pjsip_auth_clt_sess *sess,
*/
if ((c[i].data_type & EXT_MASK) == PJSIP_CRED_DATA_EXT_AKA) {
-#if !PJSIP_HAS_DIGEST_AKAV1_AUTH
- pj_assert(!"PJSIP_HAS_DIGEST_AKAV1_AUTH is not enabled");
+#if !PJSIP_HAS_DIGEST_AKA_AUTH
+ pj_assert(!"PJSIP_HAS_DIGEST_AKA_AUTH is not enabled");
return PJSIP_EAUTHINAKACRED;
#endif
@@ -479,15 +498,15 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_set_credentials( pjsip_auth_clt_sess *sess,
PJ_ASSERT_RETURN(c[i].ext.aka.cb != NULL, PJ_EINVAL);
/* Verify K len */
- PJ_ASSERT_RETURN(c[i].ext.aka.k.slen == PJSIP_AKA_KLEN,
+ PJ_ASSERT_RETURN(c[i].ext.aka.k.slen <= PJSIP_AKA_KLEN,
PJSIP_EAUTHINAKACRED);
/* Verify OP len */
- PJ_ASSERT_RETURN(c[i].ext.aka.op.slen == PJSIP_AKA_OPLEN,
+ PJ_ASSERT_RETURN(c[i].ext.aka.op.slen <= PJSIP_AKA_OPLEN,
PJSIP_EAUTHINAKACRED);
/* Verify AMF len */
- PJ_ASSERT_RETURN(c[i].ext.aka.amf.slen == PJSIP_AKA_AMFLEN,
+ PJ_ASSERT_RETURN(c[i].ext.aka.amf.slen <= PJSIP_AKA_AMFLEN,
PJSIP_EAUTHINAKACRED);
sess->cred_info[i].ext.aka.cb = c[i].ext.aka.cb;
@@ -792,7 +811,18 @@ static pj_status_t process_auth( pj_pool_t *req_pool,
if (pj_stricmp(&hchal->challenge.common.realm,
&sent_auth->credential.common.realm )==0)
{
- break;
+ /* If this authorization has empty response, remove it. */
+ if (pj_stricmp(&sent_auth->scheme, &pjsip_DIGEST_STR)==0 &&
+ sent_auth->credential.digest.response.slen == 0)
+ {
+ /* This is empty authorization, remove it. */
+ hdr = hdr->next;
+ pj_list_erase(sent_auth);
+ continue;
+ } else {
+ /* Found previous authorization attempt */
+ break;
+ }
}
}
hdr = hdr->next;
diff --git a/pjsip/src/pjsip/sip_auth_msg.c b/pjsip/src/pjsip/sip_auth_msg.c
index 5cb78527..39ddddde 100644
--- a/pjsip/src/pjsip/sip_auth_msg.c
+++ b/pjsip/src/pjsip/sip_auth_msg.c
@@ -71,9 +71,9 @@ static int print_digest_credential(pjsip_digest_credential *cred, char *buf, pj_
copy_advance_pair_quote_cond(buf, "username=", 9, cred->username, '"', '"');
copy_advance_pair_quote_cond(buf, ", realm=", 8, cred->realm, '"', '"');
- copy_advance_pair_quote_cond(buf, ", nonce=", 8, cred->nonce, '"', '"');
+ copy_advance_pair_quote(buf, ", nonce=", 8, cred->nonce, '"', '"');
copy_advance_pair_quote_cond(buf, ", uri=", 6, cred->uri, '"', '"');
- copy_advance_pair_quote_cond(buf, ", response=", 11, cred->response, '"', '"');
+ copy_advance_pair_quote(buf, ", response=", 11, cred->response, '"', '"');
copy_advance_pair(buf, ", algorithm=", 12, cred->algorithm);
copy_advance_pair_quote_cond(buf, ", cnonce=", 9, cred->cnonce, '"', '"');
copy_advance_pair_quote_cond(buf, ", opaque=", 9, cred->opaque, '"', '"');
diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c
index 07cba74f..482d6bbe 100644
--- a/pjsip/src/pjsua-lib/pjsua_acc.c
+++ b/pjsip/src/pjsua-lib/pjsua_acc.c
@@ -737,6 +737,27 @@ PJ_DEF(pj_status_t) pjsua_acc_set_registration( pjsua_acc_id acc_id,
status = pjsip_regc_register(pjsua_var.acc[acc_id].regc, 1,
&tdata);
+ if (0 && status == PJ_SUCCESS && pjsua_var.acc[acc_id].cred_cnt) {
+ pjsua_acc *acc = &pjsua_var.acc[acc_id];
+ pjsip_authorization_hdr *h;
+ char *uri;
+ int d;
+
+ uri = (char*) pj_pool_alloc(tdata->pool, acc->cfg.reg_uri.slen+10);
+ d = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, tdata->msg->line.req.uri,
+ uri, acc->cfg.reg_uri.slen+10);
+ pj_assert(d > 0);
+
+ h = pjsip_authorization_hdr_create(tdata->pool);
+ h->scheme = pj_str("Digest");
+ h->credential.digest.username = acc->cred[0].username;
+ h->credential.digest.realm = acc->srv_domain;
+ h->credential.digest.uri = pj_str(uri);
+ h->credential.digest.algorithm = pj_str("md5");
+
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)h);
+ }
+
} else {
if (pjsua_var.acc[acc_id].regc == NULL) {
PJ_LOG(3,(THIS_FILE, "Currently not registered"));