modules_k/auth/auth_mod.c
fc54405e
 /*
31ccf6a2
  * $Id$ 
  *
  * Copyright (C) 2001-2003 FhG Fokus
  *
27642a08
  * This file is part of Kamailio, a free SIP server.
31ccf6a2
  *
27642a08
  * Kamailio is free software; you can redistribute it and/or modify
31ccf6a2
  * 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
  *
27642a08
  * Kamailio is distributed in the hope that it will be useful,
31ccf6a2
  * 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
  *
  * History:
  * --------
  * 2003-02-26 checks and group moved to separate modules (janakj)
  * 2003-03-10 New module interface (janakj)
  * 2003-03-16 flags export parameter added (janakj)
  * 2003-03-19 all mallocs/frees replaced w/ pkg_malloc/pkg_free (andrei)
  * 2003-04-28 rpid contributed by Juha Heinanen added (janakj) 
  * 2005-05-31 general avp specification added for rpid (bogdan)
cfbd60f8
  * 2006-03-01 pseudo variables support for domain name (bogdan)
31ccf6a2
  */
 
fc54405e
 /*!
  * \file
  * \brief Digest Authentication Module
  * \ingroup auth
  * - Module: \ref auth
  */
 
 /*!
  * \defgroup auth AUTH :: The Kamailio auth Module
  * The module provides functions to authentificate users.
  * It also exports a API that can be used from other modules.
  */
31ccf6a2
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <time.h>
 #include "../../sr_module.h"
 #include "../../dprint.h"
 #include "../../mem/mem.h"
 #include "../../error.h"
b0a7f212
 #include "../../pvar.h"
31ccf6a2
 #include "../../ut.h"
329269f9
 #include "../../mod_fix.h"
3b25e1d0
 #include "../../lock_alloc.h"
8461dc65
 #include "../sl/sl_api.h"
31ccf6a2
 #include "auth_mod.h"
 #include "challenge.h"
 #include "rpid.h"
 #include "api.h"
 
 MODULE_VERSION
 
fc54405e
 /*! length of the random secret */
31ccf6a2
 #define RAND_SECRET_LEN 32
 
 #define DEF_RPID_PREFIX ""
 #define DEF_RPID_SUFFIX ";party=calling;id-type=subscriber;screen=yes"
 #define DEF_STRIP_REALM ""
cdece58c
 #define DEF_RPID_AVP "$avp(s:rpid)"
31ccf6a2
 
 
fc54405e
 /*!
31ccf6a2
  * Module destroy function prototype
  */
 static void destroy(void);
 
fc54405e
 /*!
31ccf6a2
  * Module initialization function prototype
  */
 static int mod_init(void);
 
dd2bef32
 int pv_proxy_authorize(struct sip_msg* msg, char* realm, char* str2);
 int pv_www_authorize(struct sip_msg* msg, char* realm, char* str2);
31ccf6a2
 
fc54405e
 /*! SL binds */
8461dc65
 struct sl_binds slb;
31ccf6a2
 
 
 /*
  * Module parameter variables
  */
fc54405e
 char* sec_param    = 0;   /*!< If the parameter is not used, the secret phrase will be auto-generated */
c1fd5724
 unsigned int   nonce_expire = 30; /*!< Nonce lifetime - default 30 seconds */
31ccf6a2
 
 str secret;
 char* sec_rand = 0;
 
dd2bef32
 int auth_calc_ha1 = 0;
31ccf6a2
 
fc54405e
 /*! Default Remote-Party-ID prefix */
31ccf6a2
 str rpid_prefix = {DEF_RPID_PREFIX, sizeof(DEF_RPID_PREFIX) - 1};
fc54405e
 /*! Default Remote-Party-IDD suffix */
31ccf6a2
 str rpid_suffix = {DEF_RPID_SUFFIX, sizeof(DEF_RPID_SUFFIX) - 1};
fc54405e
 /*! Prefix to strip from realm */
31ccf6a2
 str realm_prefix = {DEF_STRIP_REALM, sizeof(DEF_STRIP_REALM) - 1};
cdece58c
 
fc54405e
 /*! definition of AVP containing rpid value */
31ccf6a2
 char* rpid_avp_param = DEF_RPID_AVP;
 
fc54405e
 /*! definition of AVP containing username value */
dd2bef32
 char* user_spec_param = 0;
b0a7f212
 static pv_spec_t user_spec;
dd2bef32
 
 
fc54405e
 /*! definition of AVP containing password value */
dd2bef32
 char* passwd_spec_param = 0;
b0a7f212
 static pv_spec_t passwd_spec;
dd2bef32
 
fc54405e
 /*! nonce index */
3b25e1d0
 gen_lock_t* nonce_lock= NULL;
 char* nonce_buf= NULL;
 int* sec_monit= NULL;
 int* second= NULL;
 int* next_index= NULL;
 
fc54405e
 /*! control nonce usage checking */
c8825041
 int nonce_reuse = 0;
 
31ccf6a2
 /*
fc54405e
  * Exported functions
31ccf6a2
  */
 static cmd_export_t cmds[] = {
b74114d8
 	{"www_challenge",       (cmd_function)www_challenge,           2,
 		fixup_spve_uint, 0, REQUEST_ROUTE},
 	{"proxy_challenge",     (cmd_function)proxy_challenge,         2,
 		fixup_spve_uint, 0, REQUEST_ROUTE},
 	{"pv_www_authorize",    (cmd_function)pv_www_authorize,        1,
 		fixup_spve_null, 0, REQUEST_ROUTE},
 	{"pv_proxy_authorize",  (cmd_function)pv_proxy_authorize,      1,
 		fixup_spve_null, 0, REQUEST_ROUTE},
c4d2e802
 	{"consume_credentials", (cmd_function)consume_credentials,     0, 0,
80998a7f
 			0, REQUEST_ROUTE},
c4d2e802
 	{"is_rpid_user_e164",   (cmd_function)is_rpid_user_e164,       0, 0,
80998a7f
 			0, REQUEST_ROUTE},
c4d2e802
 	{"append_rpid_hf",      (cmd_function)append_rpid_hf,          0, 0,
80998a7f
 			0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE},
75739b85
 	{"append_rpid_hf",      (cmd_function)append_rpid_hf_p,        2,
 			fixup_str_str,
80998a7f
 			0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE},
31ccf6a2
 	{"bind_auth",           (cmd_function)bind_auth, 0, 0,
80998a7f
 			0, 0},
 	{0, 0, 0, 0, 0, 0}
31ccf6a2
 };
 
 
 /*
  * Exported parameters
  */
 static param_export_t params[] = {
 	{"secret",          STR_PARAM, &sec_param      },
 	{"nonce_expire",    INT_PARAM, &nonce_expire   },
 	{"rpid_prefix",     STR_PARAM, &rpid_prefix.s  },
 	{"rpid_suffix",     STR_PARAM, &rpid_suffix.s  },
 	{"realm_prefix",    STR_PARAM, &realm_prefix.s },
 	{"rpid_avp",        STR_PARAM, &rpid_avp_param },
dd2bef32
 	{"username_spec",   STR_PARAM, &user_spec_param   },
 	{"password_spec",   STR_PARAM, &passwd_spec_param },
 	{"calculate_ha1",   INT_PARAM, &auth_calc_ha1     },
c8825041
 	{"nonce_reuse",     INT_PARAM, &nonce_reuse       },
31ccf6a2
 	{0, 0, 0}
 };
 
 
 /*
  * Module interface
  */
 struct module_exports exports = {
 	"auth", 
51716422
 	DEFAULT_DLFLAGS, /* dlopen flags */
31ccf6a2
 	cmds,
 	params,
739aec8b
 	0,          /* exported statistics */
de26de44
 	0,          /* exported MI functions */
71c26efd
 	0,          /* exported pseudo-variables */
3eee3a4e
 	0,          /* extra processes */
31ccf6a2
 	mod_init,   /* module initialization function */
 	0,          /* response function */
 	destroy,    /* destroy function */
 	0           /* child initialization function */
 };
 
 
fc54405e
 /*!
  * \brief Generate a random secret
  *
  * Generate a random secret. A secret parameter was not used so we
  * generate a random value here.
  * \return 0 on success, -1 on failure
31ccf6a2
  */
 static inline int generate_random_secret(void)
 {
 	int i;
 
 	sec_rand = (char*)pkg_malloc(RAND_SECRET_LEN);
 	if (!sec_rand) {
789504a6
 		LM_ERR("no pkg memory left\n");
31ccf6a2
 		return -1;
 	}
 
cc010ffe
 	/* the generator is seeded from the core */
31ccf6a2
 
 	for(i = 0; i < RAND_SECRET_LEN; i++) {
 		sec_rand[i] = 32 + (int)(95.0 * rand() / (RAND_MAX + 1.0));
 	}
 
 	secret.s = sec_rand;
 	secret.len = RAND_SECRET_LEN;
 
789504a6
 	/*LM_DBG("Generated secret: '%.*s'\n", secret.len, secret.s); */
31ccf6a2
 
 	return 0;
 }
 
 
 static int mod_init(void)
 {
b0a7f212
 	str stmp;
31ccf6a2
 	
8461dc65
 	/* load the SL API */
 	if (load_sl_api(&slb)!=0) {
789504a6
 		LM_ERR("can't load SL API\n");
8461dc65
 		return -1;
31ccf6a2
 	}
 
 	/* If the parameter was not used */
 	if (sec_param == 0) {
 		/* Generate secret using random generator */
 		if (generate_random_secret() < 0) {
789504a6
 			LM_ERR("failed to generate random secret\n");
31ccf6a2
 			return -3;
 		}
 	} else {
 		/* Otherwise use the parameter's value */
 		secret.s = sec_param;
 		secret.len = strlen(secret.s);
 	}
 
cdece58c
 	if ( init_rpid_avp(rpid_avp_param)<0 ) {
789504a6
 		LM_ERR("failed to process rpid AVPs\n");
31ccf6a2
 		return -4;
 	}
 
 	rpid_prefix.len = strlen(rpid_prefix.s);
 	rpid_suffix.len = strlen(rpid_suffix.s);
 	realm_prefix.len = strlen(realm_prefix.s);
 
dd2bef32
 	if(user_spec_param!=0)
 	{
b0a7f212
 		stmp.s = user_spec_param; stmp.len = strlen(stmp.s);
 		if(pv_parse_spec(&stmp, &user_spec)==NULL)
dd2bef32
 		{
789504a6
 			LM_ERR("failed to parse username spec\n");
dd2bef32
 			return -5;
 		}
 		switch(user_spec.type) {
b0a7f212
 			case PVT_NONE:
 			case PVT_EMPTY:
 			case PVT_NULL:
 			case PVT_MARKER:
 			case PVT_COLOR:
789504a6
 				LM_ERR("invalid username spec\n");
dd2bef32
 				return -6;
 			default: ;
 		}
 	}
 	if(passwd_spec_param!=0)
 	{
b0a7f212
 		stmp.s = passwd_spec_param; stmp.len = strlen(stmp.s);
 		if(pv_parse_spec(&stmp, &passwd_spec)==NULL)
dd2bef32
 		{
789504a6
 			LM_ERR("failed to parse password spec\n");
dd2bef32
 			return -7;
 		}
 		switch(passwd_spec.type) {
b0a7f212
 			case PVT_NONE:
 			case PVT_EMPTY:
 			case PVT_NULL:
 			case PVT_MARKER:
 			case PVT_COLOR:
789504a6
 				LM_ERR("invalid password spec\n");
dd2bef32
 				return -8;
 			default: ;
 		}
 	}
fc54405e
 
c8825041
 	if(nonce_reuse==0)
 	{
 	    nonce_lock = (gen_lock_t*)lock_alloc();
 		if(nonce_lock== NULL)
 	    {
 		    LM_ERR("no more shared memory\n");
 			return -1;
 	    }
 
 		/* initialize lock_nonce */
 	    if(lock_init(nonce_lock)== 0)
 		{
 	        LM_ERR("failed to init lock\n");
 		    return -9;
 	    }
 
9e877e74
 		nonce_buf= (char*)shm_malloc(NBUF_LEN);
c8825041
 		if(nonce_buf== NULL)
 	    {
 		    LM_ERR("no more share memory\n");
 			return -10;
 	    }
 		memset(nonce_buf, 255, NBUF_LEN);
fc54405e
 
9e877e74
 		sec_monit= (int*)shm_malloc((nonce_expire +1)* sizeof(int));
 		if(sec_monit== NULL)
c8825041
 		{
 			LM_ERR("no more share memory\n");
 	        return -10;
 		}
9e877e74
 		memset(sec_monit, -1, (nonce_expire +1)* sizeof(int));
c8825041
 		second= (int*)shm_malloc(sizeof(int));
9e877e74
 		next_index= (int*)shm_malloc(sizeof(int));
c8825041
 		if(second==  NULL || next_index== NULL)
 	    {
 		    LM_ERR("no more share memory\n");
 	        return -10;
 		}
9e877e74
 		*next_index= -1;
c8825041
 	}
dd2bef32
 
31ccf6a2
 	return 0;
 }
 
 
 static void destroy(void)
 {
 	if (sec_rand) pkg_free(sec_rand);
fc54405e
 
c8825041
 	if(nonce_reuse==0)
 	{
 	    if(nonce_lock)
 		{
 			lock_destroy(nonce_lock);
 	        lock_dealloc(nonce_lock);
 		}
 
 	    if(nonce_buf)
 		    shm_free(nonce_buf);
 	    if(second)
 		    shm_free(second);
 	    if(sec_monit)
 		    shm_free(sec_monit);
 	    if(next_index)
 		    shm_free(next_index);
 	}
31ccf6a2
 }
 
fc54405e
 
aa8b2c57
 /*!
fc54405e
  * \brief Generate a HA1 response from username and domain
  * \param msg SIP message
  * \param _username user name
aa8b2c57
  * \param _domain domain
fc54405e
  * \param _ha1 generated HA1
  * \return 0 on success, 1 on error and when the user could not found
  */
dd2bef32
 static inline int auth_get_ha1(struct sip_msg *msg, struct username* _username,
 		str* _domain, char* _ha1)
 {
b0a7f212
 	pv_value_t sval;
dd2bef32
 	
 	/* get username from PV */
b0a7f212
 	memset(&sval, 0, sizeof(pv_value_t));
 	if(pv_get_spec_value(msg, &user_spec, &sval)==0)
dd2bef32
 	{
b0a7f212
 		if(sval.flags==PV_VAL_NONE || (sval.flags&PV_VAL_NULL)
 				|| (sval.flags&PV_VAL_EMPTY) || (!(sval.flags&PV_VAL_STR)))
dd2bef32
 		{
b0a7f212
 			pv_value_destroy(&sval);
dd2bef32
 			return 1;
 		}
 		if(sval.rs.len!= _username->user.len
 				|| strncasecmp(sval.rs.s, _username->user.s, sval.rs.len))
 		{
789504a6
 			LM_DBG("username mismatch [%.*s] [%.*s]\n",
dd2bef32
 				_username->user.len, _username->user.s, sval.rs.len, sval.rs.s);
b0a7f212
 			pv_value_destroy(&sval);
dd2bef32
 			return 1;
 		}
 	} else {
 		return 1;
 	}
 	/* get password from PV */
b0a7f212
 	memset(&sval, 0, sizeof(pv_value_t));
 	if(pv_get_spec_value(msg, &passwd_spec, &sval)==0)
dd2bef32
 	{
b0a7f212
 		if(sval.flags==PV_VAL_NONE || (sval.flags&PV_VAL_NULL)
 				|| (sval.flags&PV_VAL_EMPTY) || (!(sval.flags&PV_VAL_STR)))
dd2bef32
 		{
b0a7f212
 			pv_value_destroy(&sval);
dd2bef32
 			return 1;
 		}
 	} else {
 		return 1;
 	}
 	if (auth_calc_ha1) {
 		/* Only plaintext passwords are stored in database,
 		 * we have to calculate HA1 */
 		calc_HA1(HA_MD5, &_username->whole, _domain, &sval.rs, 0, 0, _ha1);
789504a6
 		LM_DBG("HA1 string calculated: %s\n", _ha1);
dd2bef32
 	} else {
 		memcpy(_ha1, sval.rs.s, sval.rs.len);
 		_ha1[sval.rs.len] = '\0';
 	}
fc54405e
 
dd2bef32
 	return 0;
 }
 
fc54405e
 
 /*!
  * \brief Check authorization from a pseudo-variable
  * \param msg SIP message
  * \param realm authentification realm
  * \param hftype type of the header field
  * \return 1 when authorized, null on errors, negative on authentification failure
  */
b74114d8
 static inline int pv_authorize(struct sip_msg* msg, gparam_p realm,
dd2bef32
 										hdr_types_t hftype)
 {
 	static char ha1[256];
 	struct hdr_field* h;
 	auth_body_t* cred;
 	auth_result_t ret;
 	str domain;
 
b74114d8
 	if(fixup_get_svalue(msg, realm, &domain)!=0)
 	{
 		LM_ERR("invalid realm parameter\n");
 		return -1;
dd2bef32
 	}
 
b74114d8
 	if (domain.len==0)
 		domain.s = 0;
 
dd2bef32
 	ret = pre_auth(msg, &domain, hftype, &h);
 
 	if (ret != DO_AUTHORIZATION)
 		return ret;
 
 	cred = (auth_body_t*)h->parsed;
 
46041ef0
 	if ((auth_get_ha1(msg, &cred->digest.username, &domain, ha1)) > 0) {
dd2bef32
 		/* Username not found */
 		return USER_UNKNOWN;
 	}
 
 	/* Recalculate response, it must be same to authorize successfully */
 	if (!check_response(&(cred->digest),&msg->first_line.u.request.method,ha1))
 	{
 		return post_auth(msg, h);
 	}
 	return AUTH_ERROR;
 }
 
 
fc54405e
 /*!
  * \brief Small wrapper around pv_authorize, use proxy challenge
  * \param msg SIP message
6b22d1b8
  * \param realm authenfication realm
fc54405e
  * \param str2 unused
  * \return 1 on sucess, 0 on errors, negative on authentification failures
  */
dd2bef32
 int pv_proxy_authorize(struct sip_msg* msg, char* realm, char* str2)
 {
b74114d8
 	return pv_authorize(msg, (gparam_p)realm, HDR_PROXYAUTH_T);
dd2bef32
 }
 
 
fc54405e
 /*!
  * \brief Small wrapper around pv_authorize, use www challenge
  * \param msg SIP message
f7235964
  * \param realm authenfication realm
fc54405e
  * \param str2 unused
  * \return 1 on sucess, 0 on errors, negative on authentification failures
  */
dd2bef32
 int pv_www_authorize(struct sip_msg* msg, char* realm, char* str2)
 {
b74114d8
 	return pv_authorize(msg, (gparam_p)realm, HDR_AUTHORIZATION_T);
31ccf6a2
 }