src/modules/auth/challenge.c
9f0c8882
 /*
4ca850ad
  * Digest Authentication Module
c4b6428c
  *
95072403
  * Copyright (C) 2001-2003 FhG Fokus
c4b6428c
  *
4ca850ad
  * This file is part of Kamailio, a free SIP server.
c4b6428c
  *
4ca850ad
  * Kamailio is free software; you can redistribute it and/or modify
ed346bcc
  * 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
c4b6428c
  *
4ca850ad
  * Kamailio is distributed in the hope that it will be useful,
ed346bcc
  * 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.
c4b6428c
  *
  * You should have received a copy of the GNU General Public License
4ca850ad
  * along with this program; if not, write to the Free Software
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
669521a0
  *
9f0c8882
  */
 
cf83221d
 #include "../../core/data_lump.h"
 #include "../../core/mem/mem.h"
 #include "../../core/parser/digest/digest.h"
 #include "../../core/usr_avp.h"
 #include "../../core/ut.h"
c60d97dd
 #include "auth_mod.h"
eef11a9f
 #include "challenge.h"
c60d97dd
 #include "nonce.h"
5782beda
 #include "api.h"
12690ddb
 #include "nc.h"
6ac1181e
 #include "ot_nonce.h"
9f0c8882
 
8ee734e1
 #define QOP_PARAM_START   ", qop=\""
 #define QOP_PARAM_START_LEN (sizeof(QOP_PARAM_START)-1)
 #define QOP_PARAM_END     "\""
 #define QOP_PARAM_END_LEN (sizeof(QOP_PARAM_END)-1)
d70c59ac
 #define STALE_PARAM	  ", stale=true"
 #define STALE_PARAM_LEN	  (sizeof(STALE_PARAM)-1)
 #define DIGEST_REALM	  ": Digest realm=\""
 #define DIGEST_REALM_LEN  (sizeof(DIGEST_REALM)-1)
 #define DIGEST_NONCE	  "\", nonce=\""
 #define DIGEST_NONCE_LEN  (sizeof(DIGEST_NONCE)-1)
 #define DIGEST_MD5	  ", algorithm=MD5"
 #define DIGEST_MD5_LEN	  (sizeof(DIGEST_MD5)-1)
1babd807
 #define DIGEST_ALGORITHM     ", algorithm="
 #define DIGEST_ALGORITHM_LEN (sizeof(DIGEST_ALGORITHM)-1)
9f0c8882
 
 
00d758fb
 extern str auth_realm_prefix;
 /**
  * @brief Strip the beginning of a realm string
  *
  * Strip the beginning of a realm string, depending on the length of
  * the realm_prefix.
  * @param _realm realm string
  */
 void strip_realm(str* _realm)
 {
 	/* no param defined -- return */
 	if (!auth_realm_prefix.len) return;
 
 	/* prefix longer than realm -- return */
 	if (auth_realm_prefix.len > _realm->len) return;
 
 	/* match ? -- if so, shorten realm -*/
 	if (memcmp(auth_realm_prefix.s, _realm->s, auth_realm_prefix.len) == 0) {
 		_realm->s += auth_realm_prefix.len;
 		_realm->len -= auth_realm_prefix.len;
 	}
 	return;
 }
 
e2ee8b57
 /**
  * Calculate a new nonce.
  * @param nonce  Pointer to a buffer of *nonce_len. It must have enough
  *               space to hold the nonce. MAX_NONCE_LEN should be always
  *               safe.
  * @param nonce_len A value/result parameter. Initially it contains the
  *                  nonce buffer length. If the length is too small, it
  *                  will be set to the needed length and the function will
  *                  return error immediately. After a succesfull call it will
  *                  contain the size of nonce written into the buffer,
  *                  without the terminating 0.
  * @param cfg This is the value of one of the three module parameters that
  *            control which optional checks are enabled/disabled and which
  *            parts of the message will be included in the nonce string.
  * @param msg     The message for which the nonce is computed. If
  *                auth_extra_checks is set, the MD5 of some fields of the
  *                message will be included in the  generated nonce.
  * @return 0 on success and -1 on error
  */
 int calc_new_nonce(char* nonce, int *nonce_len, int cfg, struct sip_msg* msg)
 {
 	int t;
 #if defined USE_NC || defined USE_OT_NONCE
 	unsigned int n_id;
 	unsigned char pool;
 	unsigned char pool_flags;
 #endif
 
 	t=time(0);
 #if defined USE_NC || defined USE_OT_NONCE
 	if (nc_enabled || otn_enabled){
 		pool=nid_get_pool();
 		n_id=nid_inc(pool);
 		pool_flags=0;
 #ifdef USE_NC
 		if (nc_enabled){
 			nc_new(n_id, pool);
 			pool_flags|=  NF_VALID_NC_ID;
 		}
 #endif
 #ifdef USE_OT_NONCE
 		if (otn_enabled){
 			otn_new(n_id, pool);
 			pool_flags|= NF_VALID_OT_ID;
 		}
 #endif
 	}else{
 		pool=0;
 		pool_flags=0;
 		n_id=0;
 	}
 	return calc_nonce(nonce, nonce_len, cfg, t, t + nonce_expire, n_id,
 				pool | pool_flags,
 				&secret1, &secret2, msg);
 #else  /* USE_NC || USE_OT_NONCE*/
 	return calc_nonce(nonce, nonce_len, cfg, t, t + nonce_expire,
 				&secret1, &secret2, msg);
 #endif /* USE_NC || USE_OT_NONCE */
 }
 
00d758fb
 
1babd807
 /**
b3f0a3f0
  * Create and return {WWW,Proxy}-Authenticate header field
1babd807
  * @param nonce nonce value
  * @param algorithm algorithm value
b3f0a3f0
  * @param qop qop value
1babd807
  * @return -1 on error, 0 on success
c4b6428c
  *
b3f0a3f0
  * The result is stored in param ahf.
1babd807
  * If nonce is not null that it is used, instead of call calc_nonce.
  * If algorithm is not null that it is used irrespective of _PRINT_MD5
c4b6428c
  *
  * Major usage of nonce and algorithm params is AKA authentication.
9f0c8882
  */
b3f0a3f0
 int get_challenge_hf(struct sip_msg* msg, int stale, str* realm,
 		str* nonce, str* algorithm, struct qp* qop, int hftype, str *ahf)
9f0c8882
 {
c4b6428c
 	char *p;
 	str* hfn, hf;
 	int nonce_len, l, cfg;
b3f0a3f0
 
 	if(!ahf)
 	{
 		LM_ERR("invalid output parameter\n");
 		return -1;
 	}
 
00d758fb
 	strip_realm(realm);
c4b6428c
 	if (realm) {
1a5cb034
 		LM_DBG("realm='%.*s'\n", realm->len, realm->s);
c4b6428c
 	}
 	if (nonce) {
1a5cb034
 		LM_DBG("nonce='%.*s'\n", nonce->len, nonce->s);
c4b6428c
 	}
 	if (algorithm) {
1a5cb034
 		LM_DBG("algorithm='%.*s'\n", algorithm->len, algorithm->s);
c4b6428c
 	}
 	if (qop && qop->qop_parsed != QOP_UNSPEC) {
1a5cb034
 		LM_DBG("qop='%.*s'\n", qop->qop_str.len, qop->qop_str.s);
c4b6428c
 	}
b3f0a3f0
 
c4b6428c
 	if (hftype == HDR_PROXYAUTH_T) {
ae29ff90
 		hfn = &proxy_challenge_header;
c4b6428c
 	} else {
ae29ff90
 		hfn = &www_challenge_header;
c4b6428c
 	}
 
ae29ff90
 	cfg = get_auth_checks(msg);
 
c4b6428c
 	nonce_len = get_nonce_len(cfg, nc_enabled || otn_enabled);
ae29ff90
 
c4b6428c
 	hf.len = hfn->len;
 	if (realm) {
1c9ccc12
 		hf.len += DIGEST_REALM_LEN
c4b6428c
 			+ realm->len;
 	}
1c9ccc12
 
c4b6428c
 	hf.len += DIGEST_NONCE_LEN;
1c9ccc12
 
c4b6428c
 	if (nonce) {
 		hf.len += nonce->len
 			+ 1; /* '"' */
 	}
 	else {
 		hf.len += nonce_len
 			+ 1; /* '"' */
 	}
1babd807
 	hf.len += ((stale) ? STALE_PARAM_LEN : 0);
c4b6428c
 	if (algorithm) {
 		hf.len += DIGEST_ALGORITHM_LEN + algorithm->len;
 	}
 	else {
 		hf.len += 0
669521a0
 #ifdef _PRINT_MD5
c4b6428c
 			+DIGEST_MD5_LEN
669521a0
 #endif
c4b6428c
 			;
 	}
 
 	if (qop && qop->qop_parsed != QOP_UNSPEC) {
b3f0a3f0
 		hf.len += QOP_PARAM_START_LEN + qop->qop_str.len + QOP_PARAM_END_LEN;
c4b6428c
 	}
1babd807
 	hf.len += CRLF_LEN;
c4b6428c
 	p = hf.s = pkg_malloc(hf.len);
 	if (!hf.s) {
1a5cb034
 		LM_ERR("No memory left (%d bytes)\n", hf.len);
ae29ff90
 		return -1;
c4b6428c
 	}
1c9ccc12
 
c4b6428c
 	memcpy(p, hfn->s, hfn->len); p += hfn->len;
1c9ccc12
 
c4b6428c
 	if(realm){
 		memcpy(p, DIGEST_REALM, DIGEST_REALM_LEN); p += DIGEST_REALM_LEN;
 		memcpy(p, realm->s, realm->len); p += realm->len;
 	}
1c9ccc12
 
c4b6428c
 	memcpy(p, DIGEST_NONCE, DIGEST_NONCE_LEN); p += DIGEST_NONCE_LEN;
 	if (nonce) {
 		memcpy(p, nonce->s, nonce->len); p += nonce->len;
 	}
 	else {
 		l=nonce_len;
e2ee8b57
 		if (calc_new_nonce(p, &l, cfg, msg) != 0)
 		{
1a5cb034
 			LM_ERR("calc_nonce failed (len %d, needed %d)\n", nonce_len, l);
e2ee8b57
 			pkg_free(hf.s);
 			return -1;
12690ddb
 		}
c4b6428c
 		p += l;
 	}
 	*p = '"'; p++;
1babd807
 
c4b6428c
 	if (qop && qop->qop_parsed != QOP_UNSPEC) {
ae29ff90
 		memcpy(p, QOP_PARAM_START, QOP_PARAM_START_LEN);
 		p += QOP_PARAM_START_LEN;
b3f0a3f0
 		memcpy(p, qop->qop_str.s, qop->qop_str.len);
 		p += qop->qop_str.len;
ae29ff90
 		memcpy(p, QOP_PARAM_END, QOP_PARAM_END_LEN);
 		p += QOP_PARAM_END_LEN;
c4b6428c
 	}
 	if (stale) {
ae29ff90
 		memcpy(p, STALE_PARAM, STALE_PARAM_LEN);
 		p += STALE_PARAM_LEN;
c4b6428c
 	}
1babd807
 	if (algorithm) {
b3f0a3f0
 		memcpy(p, DIGEST_ALGORITHM, DIGEST_ALGORITHM_LEN);
 		p += DIGEST_ALGORITHM_LEN;
 		memcpy(p, algorithm->s, algorithm->len);
 		p += algorithm->len;
1babd807
 	}
 	else {
8ee734e1
 #ifdef _PRINT_MD5
c4b6428c
 		memcpy(p, DIGEST_MD5, DIGEST_MD5_LEN ); p += DIGEST_MD5_LEN;
8ee734e1
 #endif
1babd807
 	}
c4b6428c
 	memcpy(p, CRLF, CRLF_LEN); p += CRLF_LEN;
 	hf.len=(int)(p-hf.s);	/* fix len, it might be smaller due to a smaller
 							 * nonce */
 
 	DBG("auth: '%.*s'\n", hf.len, ZSW(hf.s));
b3f0a3f0
 	*ahf = hf;
c4b6428c
 	return 0;
b3f0a3f0
 }
 
 /**
  * Create {WWW,Proxy}-Authenticate header field
  * @param nonce nonce value
  * @param algorithm algorithm value
  * @return -1 on error, 0 on success
  *
  * The result is stored in an attribute.
  * If nonce is not null that it is used, instead of call calc_nonce.
  * If algorithm is not null that it is used irrespective of _PRINT_MD5
  * The value of 'qop' module parameter is used.
  *
  * Major usage of nonce and algorithm params is AKA authentication.
  */
 int build_challenge_hf(struct sip_msg* msg, int stale, str* realm,
 		str* nonce, str* algorithm, int hftype)
 {
c4b6428c
 	str hf = {0, 0};
 	avp_value_t val;
b3f0a3f0
 	int ret;
 
 	ret = get_challenge_hf(msg, stale, realm, nonce, algorithm, &auth_qop,
c4b6428c
 			hftype, &hf);
b3f0a3f0
 	if(ret < 0)
 		return ret;
 
 	val.s = hf;
c4b6428c
 	if(add_avp(challenge_avpid.flags | AVP_VAL_STR, challenge_avpid.name, val)
b3f0a3f0
 			< 0) {
1a5cb034
 		LM_ERR("Error while creating attribute with challenge\n");
ae29ff90
 		pkg_free(hf.s);
 		return -1;
c4b6428c
 	}
40859b45
 	pkg_free(hf.s);
b3f0a3f0
 	return 0;
c60d97dd
 }