diff options
-rw-r--r-- | build.symbian/pjsip.mmp | 1 | ||||
-rw-r--r-- | pjsip-apps/src/pjsua/pjsua_app.c | 5 | ||||
-rw-r--r-- | pjsip/include/pjsip/print_util.h | 12 | ||||
-rw-r--r-- | pjsip/include/pjsip/sip_auth.h | 3 | ||||
-rw-r--r-- | pjsip/include/pjsip/sip_auth_aka.h | 155 | ||||
-rw-r--r-- | pjsip/include/pjsip/sip_config.h | 9 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_auth_aka.c | 127 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_auth_client.c | 56 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_auth_msg.c | 4 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_acc.c | 21 |
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")); |