src/modules/auth_identity/auth_identity.c
618649c1
 /*
  * Copyright (c) 2007 iptelorg GmbH
  *
598bac61
  * This file is part of Kamailio, a free SIP server.
618649c1
  *
598bac61
  * Kamailio is free software; you can redistribute it and/or modify
618649c1
  * 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
  *
598bac61
  * Kamailio is distributed in the hope that it will be useful,
618649c1
  * 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
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
618649c1
  */
 
105d0ab0
 /*!
  * \file
1f16abf4
  * \brief Kamailio auth-identity :: Module interface
105d0ab0
  * \ingroup auth-identity
  * Module: \ref auth-identity
  */
 
1f16abf4
 /*! \defgroup auth-identity Kamailio SIP identity support
105d0ab0
  *
  * Auth Identity module provides functionalities for securely identifying
  * originators of SIP messages. This module has two basic service:
  *   - authorizer - authorizes a message and adds Identity and Identity-Info headers
  *   - verifier - verifies an authorized message
  *
  */
 
 
cd519eda
 #include <stdio.h>
 #include <time.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include <openssl/pem.h>
 #include <openssl/err.h>
 #include <openssl/sha.h>
 
 #include <curl/curl.h>
 #include <curl/easy.h>
 
cf83221d
 #include "../../core/dprint.h"
 #include "../../core/ut.h"
 #include "../../core/sr_module.h"
 #include "../../core/mem/mem.h"
 #include "../../core/parser/parse_from.h"
 #include "../../core/parser/parse_cseq.h"
 #include "../../core/parser/parse_content.h"
 #include "../../core/parser/parse_uri.h"
 #include "../../core/parser/contact/parse_contact.h"
 #include "../../core/timer.h"
cd519eda
 
 #include "auth_identity.h"
 
 MODULE_VERSION;
 
 static int mod_init(void); /* Module initialization function */
 static void mod_deinit(void);
 static int add_identity(struct sip_msg* msg, char* srt1, char* str2);
 static int get_certificate(struct sip_msg* msg, char* srt1, char* str2);
 static int check_validity(struct sip_msg* msg, char* srt1, char* str2);
 static int check_date(struct sip_msg* msg, char* srt1, char* str2);
 static int check_callid(struct sip_msg* msg, char* srt1, char* str2);
 static int date_proc(struct sip_msg* msg, char* srt1, char* str2);
 static int check_certificate(struct sip_msg* msg, char* srt1, char* str2);
 void callid_gc(unsigned int tick, void *param);
 
 /*
  * Module parameter variables
  */
 char	*glb_sprivkeypath="";	/* private key of the authentication service */
 char 	*glb_sservercerturl="";	/* URL of the certificate of the authentication service */
 char 	*glb_sservercertpath=""; /* Path of the certificate of the authentication service */
 int		glb_icertlimit=CERTIFICATE_TABLE_ITEM_LIMIT;
 char	*glb_scainfo="";
 int		glb_iauthval=AUTH_MSG_VALIDITY_TIME;	/* Message validity time in seconds (verification service)*/
 int		glb_imsgtime=AUTH_MSG_TO_AUTH_VALIDITY_TIME;	/* Message validity time in seconds (authentication service)*/
 int 	glb_icallidlimit=CALLID_TABLE_ITEM_LIMIT;
 
 CURL 	*glb_hcurl;		/* global cURL handle */
 X509 	*glb_pcertx509=NULL;
 X509_STORE *glb_cacerts=NULL;
 
 RSA 	*glb_hmyprivkey=NULL;	/* private key of the authentication service */
 time_t	glb_imycertnotafter=0;
 
 int 	glb_authservice_disabled=0;
d534ffe2
 int 	glb_acceptpem=0;
cd519eda
 
 dynstr	glb_sdgst={{0,0},0}; /* Digest string */
 dynstr	glb_sidentity={{0,0},0}; /* Identity message header */
 dynstr	glb_sidentityinfo={{0,0},0}; /* Identity-info message header */
 dynstr	glb_sdate={{0,0},0}; /* Date  message header */
 
 dynstr	glb_encedmsg={{0,0},0}; /* buffer for rsa encrypted string */
 dynstr	glb_b64encedmsg={{0,0},0}; /* buffer for base64, rsa encrypted string */
 
 ttable *glb_tcert_table=0;				/* Certificate Table */
 char glb_certisdownloaded=0;
 tcert_item glb_tcert={{0,0},{0,0},0};	/* Actually Used Certificate */
 
 ttable *glb_tcallid_table=0;			/* Certificate Table */
 typedef struct timeparams { /* sturct of the callid garbage collector */
 	int ibnow;	/* the actual bucket we've not checked yet */
 	int ibnum;  /* number of the buckets we've to check */
 	int ibcir;  /* timer function's called this times during the whole table check */
 } ttimeparams;
 ttimeparams glb_ttimeparams={0,0,0};
 
 /*
  * Exported functions
  */
 static cmd_export_t glb_cmds[] = {
bab6d466
 	{"auth_date_proc", date_proc, 0, 0, 0, REQUEST_ROUTE},
 	{"auth_add_identity", add_identity, 0, 0, 0, REQUEST_ROUTE},
 	{"vrfy_get_certificate", get_certificate, 0, 0, 0, REQUEST_ROUTE},
 	{"vrfy_check_msgvalidity", check_validity, 0, 0, 0, REQUEST_ROUTE},
 	{"vrfy_check_certificate", check_certificate, 0, 0, 0, REQUEST_ROUTE},
 	{"vrfy_check_date", check_date, 0, 0, 0, REQUEST_ROUTE},
 	{"vrfy_check_callid", check_callid, 0, 0, 0, REQUEST_ROUTE},
 	{0, 0, 0, 0, 0, 0}
cd519eda
 };
 
 
 /*
  * Exported parameters
  */
 static param_export_t glb_params[] = {
 	{"privatekey_path", 		PARAM_STRING,	&glb_sprivkeypath},
 	{"certificate_url", 		PARAM_STRING,	&glb_sservercerturl},
 	{"certificate_cache_limit", PARAM_INT, 		&glb_icertlimit},
 	{"callid_cache_limit",		PARAM_INT, 		&glb_icallidlimit},
 	{"certificate_path", 		PARAM_STRING,	&glb_sservercertpath},
 	{"auth_validity_time",		PARAM_INT,    	&glb_iauthval},
 	{"msg_timeout", 			PARAM_INT, 		&glb_imsgtime},
 	{"cainfo_path", 			PARAM_STRING, 	&glb_scainfo},
d534ffe2
 	{"accept_pem_certs", 		PARAM_INT,		&glb_acceptpem},
cd519eda
 	{0, 0, 0}
 };
 
 
 /*
  * Module interface
  */
 struct module_exports exports = {
 	"auth_identity",
bab6d466
         DEFAULT_DLFLAGS, /* dlopen flags */
cd519eda
 	glb_cmds,   /* Exported functions */
 	glb_params, /* Exported parameters */
bab6d466
 	0,          /* RPC methods */
         0,          /* pseudo-variables exports */
cd519eda
 	0,          /* response function */
bab6d466
 	mod_init,   /* module initialization function */
 	0,	    /* child initialization function */
 	mod_deinit  /* destroy function */
cd519eda
 };
 
 
 static int mod_init(void)
 {
 	CURLcode iRet;
 	str sstr;
 	FILE *hpemfile;
 	char serr[160];
 	X509 *pmycert=NULL;		/* certificate of the authentication service */
 	time_t tnow, ttmp;
 
 	/*
 	 *
 	 * Parameter check
 	 *
 	 */
 	if (glb_sprivkeypath[0]==0) {
 		LOG(L_WARN, "AUTH_IDENTITY:mod_init: Private key path is missing! Authorization service is disabled\n");
 		glb_authservice_disabled=1;
 	}
 	if (!glb_authservice_disabled && glb_sservercerturl[0]==0) {
 		LOG(L_WARN, "AUTH_IDENTITY:mod_init: URL of certificate of the server is missing! Authorization service is disabled\n");
d91ccbbb
 		glb_authservice_disabled=1;
cd519eda
 	}
 	if (!glb_authservice_disabled && glb_sservercertpath[0]==0) {
 		LOG(L_WARN, "AUTH_IDENTITY:mod_init: Path of certificate of the server is missing! Authorization service is disabled\n");
 		glb_authservice_disabled=1;
 	}
 
 	/*
 	 *
 	 * Init the curl session and download buffer
 	 *
 	 */
 	curl_global_init(CURL_GLOBAL_ALL);
 	if ((glb_hcurl=curl_easy_init())==NULL) {
 		LOG(L_ERR, "AUTH_IDENTITY:mod_init: Unable to init cURL library!\n");
 		return -1;
 	}
 	/* send all data to this function  */
 	if ((iRet=curl_easy_setopt(glb_hcurl, CURLOPT_WRITEFUNCTION, curlmem_cb))!=0) {
 		LOG(L_ERR,
 			"AUTH_IDENTITY:mod_init: Unable to set cURL write function option: %s\n",
 			curl_easy_strerror(iRet));
 		return -2;
 	}
 	/* we pass our 'glb_tcert' struct to the callback function */
741d3925
 	if ((iRet=curl_easy_setopt(glb_hcurl, CURLOPT_WRITEDATA, (void *)(&glb_tcert.scertpem)))!=0) {
cd519eda
 		LOG(L_ERR,
 			"AUTH_IDENTITY:mod_init: Unable to set cURL writedata option: %s\n",
 			curl_easy_strerror(iRet));
 		return -4;
 	}
 	if (!(glb_tcert.scertpem.s=pkg_malloc(CERTIFICATE_LENGTH))) {
 		LOG(L_ERR, "AUTH_IDENTITY:mod_init: Not enough memory error\n");
 		return -3;
 	}
   	/* some servers don't like requests that are made without a user-agent
 	   field, so we provide one */
741d3925
 	if ((iRet=curl_easy_setopt(glb_hcurl, CURLOPT_USERAGENT, NAME "-Agent/1.0"))!=0) {
cd519eda
 		LOG(L_WARN,
 			"AUTH_IDENTITY:mod_init: Unable to set cURL useragent option: %s\n",
 			curl_easy_strerror(iRet));
 	}
 	if ((iRet=curl_easy_setopt(glb_hcurl, CURLOPT_SSL_VERIFYPEER, 1))!=0) {
 		LOG(L_WARN,
 			"AUTH_IDENTITY:mod_init: Unable to set cURL verifypeer option: %s\n",
 			curl_easy_strerror(iRet));
 	}
 	if ((iRet=curl_easy_setopt(glb_hcurl, CURLOPT_SSL_VERIFYHOST, 2))!=0) {
 		LOG(L_WARN,
 			"AUTH_IDENTITY:mod_init: Unable to set cURL verifyhost option: %s\n",
 			curl_easy_strerror(iRet));
 	}
 
 	/* cainfo_path module parameter's been set */
 	if (glb_scainfo[0]) {
 		if ((iRet=curl_easy_setopt(glb_hcurl, CURLOPT_CAINFO, glb_scainfo))!=0) {
 			LOG(L_WARN,
 				"AUTH_IDENTITY:mod_init: Unable to set cURL cainfo option: %s\n",
 				curl_easy_strerror(iRet));
 		}
 	}
 
 
 	/*
 	 *
 	 * OpenSSL certificate verification initialization
 	 *
 	 */
 	OpenSSL_add_all_algorithms();
 	if (!(glb_cacerts=X509_STORE_new())) {
 		LOG(L_ERR, "AUTH_IDENTITY:mod_init: unable to initialize X509 store\n");
 		return -16;
 	}
 	if (X509_STORE_set_default_paths(glb_cacerts)!=1) {
 		LOG(L_ERR, "AUTH_IDENTITY:mod_init: unable to set X509 store default path\n");
 		return -17;
 	}
 	if (glb_scainfo[0]
 		   && X509_STORE_load_locations(glb_cacerts, glb_scainfo, NULL) != 1)
 		LOG(L_WARN, "AUTH_IDENTITY:mod_init: unable to load X509 store location\n");
 
 
 	/*
 	 *
 	 * Init the Date, Digest-String, Identity and Identity-Info
 	 *
 	 */
 	if (initdynstr(&glb_sdgst, DGST_STR_INIT_SIZE))
 		return -5;
 
 	/*
 	 * Init certificate table
 	 */
 	if (init_table(&glb_tcert_table,
 				   CERTIFICATE_TABLE_ENTRIES,
 				   glb_icertlimit,
 				   cert_item_cmp,
 				   cert_item_init,
 				   cert_item_least,
 				   cert_item_free,
 				   NULL))
  		return -5;
 
 	/*
 	 * Init call-id table
 	 */
 	if (init_table(&glb_tcallid_table,
 				   CALLID_TABLE_ITEM_LIMIT,
 				   glb_icallidlimit,
 				   cid_item_cmp,
 				   cid_item_init,
 				   cid_item_least,
 				   cid_item_free,
 				   cid_item_gc))
 		return -5;
 
 	glb_ttimeparams.ibnow=0;
 	/* we've to check the whole table in glb_imsgtime, so the number of
 	   buckets we've to check in every timer call is
 	   CALLID_TABLE_ENTRIES/glb_imsgtime/CALLID_GARBAGE_COLLECTOR_INTERVAL */
 	glb_ttimeparams.ibcir=glb_iauthval/CALLID_GARBAGE_COLLECTOR_INTERVAL;
 	if (!glb_ttimeparams.ibcir)
 		glb_ttimeparams.ibcir=1;
 	glb_ttimeparams.ibnum=CALLID_TABLE_ENTRIES/glb_ttimeparams.ibcir;
 
 	if (register_timer(callid_gc, (void*)&glb_ttimeparams /* param*/, CALLID_GARBAGE_COLLECTOR_INTERVAL  /* period */) < 0 ) {
 		LOG(L_ERR, "AUTH_IDENTITY:mod_init: Can not register timer\n");
 		return -5;
 	}
 
 	/*
 	 * If there were not enough parameter set then we could not initialize
 	 * the authorizer part
 	 */
 	if (glb_authservice_disabled)
 		return 0;
 
 
 	if (initdynstr(&glb_sidentity, DGST_STR_INIT_SIZE))
 		return -6;
 
 	if (initdynstr(&glb_sdate, AUTH_TIME_LENGTH))
 		return -7;
 
 	if (initdynstr(&glb_sidentityinfo, AUTH_URL_LENGTH))
 		return -8;
 
 	/* we initialize indentity info header */
 	sstr.s=IDENTITY_INFO_FIRST_PART; sstr.len=strlen(IDENTITY_INFO_FIRST_PART);
 	if (cpy2dynstr(&glb_sidentityinfo, &sstr))
 		return -9;
 	sstr.s=glb_sservercerturl; sstr.len=strlen(glb_sservercerturl);
 	if (app2dynstr(&glb_sidentityinfo, &sstr))
 		return -10;
 	sstr.s=IDENTITY_INFO_LAST_PART;
 	/* we copy the trailing \0 because append_hf expects strings */
 	sstr.len=strlen(IDENTITY_INFO_LAST_PART) + 1;
 	if (app2dynstr(&glb_sidentityinfo, &sstr))
 		return -11;
 
 	/*
   	 * Get my certificate
 	 */
 	if (!(hpemfile=fopen(glb_sservercertpath, "r"))) {
d534ffe2
 		LOG(L_ERR, "AUTH_IDENTITY:mod_init: unable to open certificate '%s'\n", strerror(errno));
cd519eda
 		return -12;
 	}
 	if (!(pmycert=PEM_read_X509(hpemfile, NULL, NULL, NULL))) {
 		ERR_error_string_n(ERR_get_error(), serr, sizeof serr);
d534ffe2
 		LOG(L_ERR, "AUTH_IDENTITY:mod_init: '%s'\n", serr);
cd519eda
 		fclose(hpemfile);
 		return -13;
 	}
 	if (x509_get_notafter(&glb_imycertnotafter, pmycert)) {
d534ffe2
 		LOG(L_ERR, "AUTH_IDENTITY:mod_init: Error getting certificate expiration date\n");
cd519eda
 		return -13;
 	}
 	if (x509_get_notbefore(&ttmp, pmycert)) {
d534ffe2
 		LOG(L_ERR, "AUTH_IDENTITY:mod_init: Error getting certificate validity date\n");
cd519eda
 		return -13;
 	}
 	if ((tnow=time(0)) < 0) {
d534ffe2
 		LOG(L_ERR, "AUTH_IDENTITY:mod_init: time error %s\n", strerror(errno));
cd519eda
 		return -13;
 	}
 	if (tnow < ttmp || tnow > glb_imycertnotafter) {
d534ffe2
 		LOG(L_ERR, "AUTH_IDENTITY:mod_init: Date of certificate is invalid (%s)\n", glb_sservercertpath);
cd519eda
 		return -14;
 	}
 
 	if (fclose(hpemfile))
d534ffe2
 		LOG(L_ERR, "AUTH_IDENTITY:mod_init: unable to close file\n");
cd519eda
 	X509_free(pmycert);
 
 	/*
 	 *
  	 * Init RSA-SHA1 encoder
 	 *
 	 */
 	hpemfile=fopen(glb_sprivkeypath, "r");
 	if (!hpemfile)
 	{
d534ffe2
 		LOG(L_ERR, "AUTH_IDENTITY:mod_init: unable to open private key '%s'\n", strerror(errno));
cd519eda
 		return -12;
 	}
 	glb_hmyprivkey=PEM_read_RSAPrivateKey(hpemfile, NULL, NULL, NULL);
 	if (!glb_hmyprivkey)
 	{
 		ERR_error_string_n(ERR_get_error(), serr, sizeof serr);
d534ffe2
 		LOG(L_ERR, "AUTH_IDENTITY:mod_init: '%s'\n", serr);
cd519eda
 		fclose(hpemfile);
 		return -13;
 	}
 	if (fclose(hpemfile))
d534ffe2
 		LOG(L_ERR, "AUTH_IDENTITY:mod_init: unable to close file\n");
cd519eda
 
 	/* we encrypt the digest string hash to this buffer */
 	if (initdynstr(&glb_encedmsg, RSA_size(glb_hmyprivkey)))
 		return -14;
 
 	/* we base64 encode the encrypted digest string hash to this buffer */
 	if (initdynstr(&glb_b64encedmsg, (RSA_size(glb_hmyprivkey)/3+1)*4))
 		return -15;
 
 	return 0;
 }
 
 
 static void mod_deinit(void)
 {
 	curl_easy_cleanup(glb_hcurl);
 	if (glb_tcert.scertpem.s)
 		pkg_free(glb_tcert.scertpem.s);
 	free_dynstr(&glb_sdgst);
 	free_dynstr(&glb_sidentity);
 	free_dynstr(&glb_sdate);
  	free_table(glb_tcert_table);
 	free_table(glb_tcallid_table);
 
 	if (glb_cacerts)
 		X509_STORE_free(glb_cacerts);
 }
 
 
 /*
  *
  *	VERIFIER FUNCTIONS
  *
  */
 
 
 static int get_certificate(struct sip_msg* msg, char* srt1, char* str2)
 {
d534ffe2
 	if (identityinfohdr_proc(&glb_tcert.surl, NULL, msg))
cd519eda
 		return -3;
 
b9fc8d46
 	/* we support rsa-sha1 only (alg.len==0 then we use rsa-sha1) */
 	if (get_identityinfo(msg)->alg.len
 		&& (get_identityinfo(msg)->alg.len != strlen("rsa-sha1")
 		    || strncasecmp("rsa-sha1",
 							get_identityinfo(msg)->alg.s,
 							get_identityinfo(msg)->alg.len ))) {
d534ffe2
 		LOG(L_ERR, "AUTH_IDENTITY:get_certificate: Unsupported Identity-Info algorithm\n");
cd519eda
 		return -5;
 	}
 
 	/* this case ivalidbefore==0 singns that this certificate was downloaded */
 	glb_tcert.ivalidbefore=0;
 
 	/* chech whether this certificate is our certificate table */
 	if (get_cert_from_table(glb_tcert_table, &glb_tcert.surl, &glb_tcert)) {
 		/* we did not found it in the table, so we've to download it */
 		/* we reset the PEM buffer */
 		glb_tcert.scertpem.len=0;
d534ffe2
 		if (download_cer(&glb_tcert.surl, glb_hcurl))
cd519eda
 			return -6;
 		glb_certisdownloaded=1;
 	} else
 		glb_certisdownloaded=0;
 
d534ffe2
 	if (retrieve_x509(&glb_pcertx509, &glb_tcert.scertpem, glb_acceptpem))
cd519eda
 		return -7;
 
 
 	return 1;
 }
 
 /*
  * If the digest-string, assembled from the message, corresponds to the string
  * decoded from the Identity header by the acquired public key then the message
  * is valid. RFC 4474 [6] Step 3
  */
 static int check_validity(struct sip_msg* msg, char* srt1, char* str2)
 {
 	str sidentity;
 	char sencedsha[HASH_STR_SIZE];
 	int iencedshalen;
b9fc8d46
 #ifndef NEW_RSA_PROC
cd519eda
 	char ssha[HASH_STR_SIZE];
b9fc8d46
 #endif
cd519eda
 	int ishalen;
 	unsigned char sstrcrypted[SHA_DIGEST_LENGTH];
 	int iRet=1;
 
 
 	if (!glb_pcertx509) {
d534ffe2
 		LOG(L_ERR, "AUTH_IDENTITY:check_validity: Certificate uninitialized! (has vrfy_get_certificate been called?)\n");
cd519eda
 		return -1;
 	}
 
 	do {
 		/* get the value of identity header parsed */
 		if (identityhdr_proc(&sidentity, NULL, msg)) {
 			iRet=-1;
 			break;
 		}
 
 		/* the length of identity value should be 172 octets long */
 		if (sidentity.len > sizeof(sencedsha)) {
d534ffe2
 			LOG(L_ERR, "AUTH_IDENTITY:check_validity: Unexpected Identity length (%d)\n", sidentity.len);
cd519eda
 			iRet=-2;
 			break;
 		}
 
 		/* base64 decode the value of Identity header */
 		base64decode(sidentity.s, sidentity.len, sencedsha, &iencedshalen);
 
b9fc8d46
 		/* assemble the digest string to be able to compare it with decrypted one */
 		if (digeststr_asm(&glb_sdgst, msg, NULL, AUTH_INCOMING_BODY)) {
 			iRet=-5;
 			break;
 		}
 		/* calculate hash */
 		SHA1((unsigned char*)getstr_dynstr(&glb_sdgst).s,
 			  getstr_dynstr(&glb_sdgst).len,
 			  sstrcrypted);
 
 #ifdef NEW_RSA_PROC
 		/* decrypt with public key retrieved from the downloaded certificate
 		   and compare it with the calculated digest hash */
 		if (rsa_sha1_dec(sencedsha, iencedshalen,
 						 (char *)sstrcrypted, sizeof(sstrcrypted), &ishalen,
 						 glb_pcertx509)) {
 			iRet=-3;
 			break;
 		} else
d534ffe2
 			LOG(AUTH_DBG_LEVEL, "AUTH_IDENTITY VERIFIER: Identity OK\n");
b9fc8d46
 #else
cd519eda
 		/* decrypt with public key retrieved from the downloaded certificate */
 		if (rsa_sha1_dec(sencedsha, iencedshalen,
 						 ssha, sizeof(ssha), &ishalen,
 						 glb_pcertx509)) {
 			iRet=-3;
 			break;
 		}
 
 		/* check size */
 		if (ishalen != sizeof(sstrcrypted)) {
d534ffe2
 			LOG(L_ERR, "AUTH_IDENTITY:check_validity: Unexpected decrypted hash length (%d != %d)\n", ishalen, SHA_DIGEST_LENGTH);
cd519eda
 			iRet=-4;
 			break;
 		}
 		/* compare */
 		if (memcmp(sstrcrypted, ssha, ishalen)) {
d534ffe2
 			LOG(L_INFO, "AUTH_IDENTITY VERIFIER: comparing hashes failed -> Invalid Identity Header\n");
cd519eda
 			iRet=-6;
 			break;
 		} else
d534ffe2
 			LOG(AUTH_DBG_LEVEL, "AUTH_IDENTITY VERIFIER: Identity OK\n");
b9fc8d46
 #endif
cd519eda
 	} while (0);
 
 	glb_pcertx509=NULL;
 
 	return iRet;
 }
 
 /*
  * The Date header must indicate a time within 3600 seconds of the receipt of a
  * message. RFC 4474 [6] Step 4
  */
 static int check_date(struct sip_msg* msg, char* srt1, char* str2)
 {
 	time_t tnow, tmsg;
 	int ires;
 
 	ires=datehdr_proc(NULL, NULL, msg);
ccbce2c0
 	if (ires)
cd519eda
 		return -1;
ccbce2c0
 
cd519eda
 
 #ifdef HAVE_TIMEGM
 	tmsg=timegm(&get_date(msg)->date);
 #else
 	tmsg=_timegm(&get_date(msg)->date);
 #endif
 	if (tmsg < 0) {
d534ffe2
 		LOG(L_ERR, "AUTH_IDENTITY:check_date: timegm error\n");
cd519eda
 		return -2;
 	}
 
 	if ((tnow=time(0)) < 0) {
d534ffe2
 		LOG(L_ERR, "AUTH_IDENTITY:check_date: time error %s\n", strerror(errno));
cd519eda
 		return -3;
 	}
 
 	if (tnow > tmsg + glb_iauthval) {
d534ffe2
 		LOG(L_INFO, "AUTH_IDENTITY VERIFIER: Outdated date header value (%ld sec)\n", tnow - tmsg + glb_iauthval);
cd519eda
 		return -4;
 	} else
d534ffe2
 		LOG(AUTH_DBG_LEVEL, "AUTH_IDENTITY VERIFIER: Date header value OK\n");
cd519eda
 
 	return 1;
 }
 
 
 int check_certificate(struct sip_msg* msg, char* srt1, char* str2) {
d534ffe2
 	struct sip_uri tfrom_uri;
 	str suri;
cd519eda
 
 	if (!glb_pcertx509) {
d534ffe2
 		LOG(L_ERR, "AUTH_IDENTITY:check_certificate: Certificate uninitialized! (has vrfy_get_certificate been called?)\n");
cd519eda
 		return -1;
 	}
 	/* this certificate was downloaded so we've to verify and add it to table */
 	if (glb_certisdownloaded) {
d534ffe2
 		if (fromhdr_proc(&suri, NULL, msg))
 			return -1;
 
 		if (parse_uri(suri.s, suri.len, &tfrom_uri)) {
 			LOG(L_ERR, "AUTH_IDENTITY:get_certificate: Error while parsing FROM URI\n");
cd519eda
 			return -2;
d534ffe2
 		}
cd519eda
 
ccbce2c0
 		if (verify_x509(glb_pcertx509, glb_cacerts))
cd519eda
 			return -3;
 
d534ffe2
 		if (check_x509_subj(glb_pcertx509, &tfrom_uri.host))
 			return -4;
ccbce2c0
 
cd519eda
 		/* we retrieve expiration date from the certificate (it needs for
 		   certificate table garbage collector) */
 		if (x509_get_notafter(&glb_tcert.ivalidbefore, glb_pcertx509))
d534ffe2
 			return -5;
cd519eda
 
 		if (addcert2table(glb_tcert_table, &glb_tcert))
d534ffe2
 			return -6;
cd519eda
 	}
 	return 1;
 }
 
 static int check_callid(struct sip_msg* msg, char* srt1, char* str2)
 {
 	str scid, sftag, scseqnum;
 	unsigned int ucseq;
 	int ires;
 	time_t ivalidbefore;
 
 
 	if (callidhdr_proc(&scid, NULL, msg))
 		return -1;
 
 	if (cseqhdr_proc(&scseqnum, NULL, msg))
 		return -2;
 	if (str2int(&scseqnum, &ucseq))
 		return -3;
 
 	if (fromhdr_proc(NULL, &sftag, msg))
 		return -4;
 
 	if ((ivalidbefore=time(0)) < 0) {
d534ffe2
 		LOG(L_ERR, "AUTH_IDENTITY:check_callid: time error %s\n", strerror(errno));
cd519eda
 		return -5;
 	}
 
 	ires=proc_cid(glb_tcallid_table,
 				  &scid,
 				  &sftag,
 				  ucseq,
 				  ivalidbefore + glb_iauthval);
 	if (ires) {
 		if (ires==AUTH_FOUND)
d534ffe2
 			LOG(L_INFO, "AUTH_IDENTITY VERIFIER: Call is replayed!\n");
cd519eda
 		return -6;
 	}
 
 	return 1;
 }
 
 
 void callid_gc(unsigned int tick, void *param)
 {
 	/* check the last slice */
 	if (((ttimeparams*)param)->ibnow + 1 == ((ttimeparams*)param)->ibcir) {
 		garbage_collect(glb_tcallid_table,
 						 (((ttimeparams*)param)->ibnow)*((ttimeparams*)param)->ibnum,
 						 CALLID_TABLE_ENTRIES-1);
 		/* we step to the first slice */
 		((ttimeparams*)param)->ibnow=0;
 	} else {
 		garbage_collect(glb_tcallid_table,
 						 (((ttimeparams*)param)->ibnow)*((ttimeparams*)param)->ibnum,
 						 ((((ttimeparams*)param)->ibnow+1)*((ttimeparams*)param)->ibnum)-1);
 		/* we step to the next slice */
 		((ttimeparams*)param)->ibnow++;
 	}
 }
 
 /*
  *
  *	AUTHORIZER FUNCTIONS
  *
  */
 
 /* Checks the Date header of the message. RFC4474 [5] Step 3 */
 static int date_proc(struct sip_msg* msg, char* srt1, char* str2)
 {
 	str sdate;
 	int iRes;
 	time_t tmsg, tnow;
 
 	if (glb_authservice_disabled) {
b9fc8d46
 		LOG(L_WARN, "AUTH_IDENTITY:date_proc: Authentication Service is disabled\n");
cd519eda
 		return -1;
 	}
 
 	getstr_dynstr(&glb_sdate).len=0;
 
 	/* we'd like to get the DATE header of the massage */
 	iRes=datehdr_proc(&sdate, NULL, msg);
 	switch (iRes) {
 		case AUTH_ERROR:
 			return -1;
 		case AUTH_NOTFOUND:
 			if (append_date(&getstr_dynstr(&glb_sdate), glb_sdate.size, &tmsg, msg))
 				return -2;
 			break;
 		/* Message has Date header so we check that */
 		case AUTH_OK:
 #ifdef HAVE_TIMEGM
 			tmsg=timegm(&get_date(msg)->date);
 #else
 			tmsg=_timegm(&get_date(msg)->date);
 #endif
 			if (tmsg < 0) {
d534ffe2
 				LOG(L_ERR, "AUTH_IDENTITY:date_proc: timegm error\n");
cd519eda
 				return -3;
 			}
 			if ((tnow=time(NULL))<0) {
d534ffe2
 				LOG(L_ERR, "AUTH_IDENTITY:date_proc: time error\n");
cd519eda
 				return -4;
 			}
 			/*
 			 * If the value of this field contains a time different by more than
 			 * ten minutes from the current time noted by the authentication
 			 * service then it should reject the message.
 			 */
 			if (tmsg + glb_imsgtime < tnow || tnow + glb_imsgtime < tmsg) {
d534ffe2
 				LOG(L_INFO, "AUTH_IDENTITY AUTHORIZER: Date header overdue\n");
cd519eda
 				return -6;
 			}
 			break;
 		default:
 			/* unknown result */
 			return -7;
 	}
 
 	/*
 	 * The authentication service MUST verify that the Date header
 	 * falls within the validity period of its certificate
 	 * RFC 4474 [6] Step 3
 	 */
 	if (glb_imycertnotafter < tmsg) {
d534ffe2
 		LOG(L_INFO, "AUTH_IDENTITY AUTHORIZER: My certificate has been expired\n");
cd519eda
 		return -8;
 	}
 
 	return 1;
 }
 
 /*
  * Concates the message From, To, Call-ID, Cseq, Date,  Contact header fields
  * and the message body to digest-string, signs with the domain private-key,
  * BASE64 encodes that, and finally adds it to the message as the 'Identity'
  * header value. RFC4474 [5] Step 4
  *
  * Adds Identity-Info header to the message which contains an URI from which
  * its certificate can be acquired. RFC4474 [5] Step 4
  */
 static int add_identity(struct sip_msg* msg, char* srt1, char* str2)
 {
 	int iRes;
 	str sstr;
 
 
 	if (glb_authservice_disabled) {
b9fc8d46
 		LOG(L_WARN, "AUTH_IDENTITY:add_identity: Authentication Service is disabled\n");
cd519eda
 		return -1;
 	}
 
 	/* check Date */
 	iRes=datehdr_proc(NULL, NULL, msg);
 	switch (iRes) {
  		case AUTH_ERROR:
 			return -1;
 		case AUTH_NOTFOUND:
 			if (!getstr_dynstr(&glb_sdate).len) {
 				/*
 				 * date_proc() must be called before add_identity() because
 				 * that function initializes the Date if that not exists
 				 * in the SIP message
 				 */
d534ffe2
 				LOG(L_ERR, "AUTH_IDENTITY:add_identity: Date header is not found (has auth_date_proc been called?)\n");
cd519eda
 				return -2;
 			}
 			/*  assemble the digest string and the DATE header is missing in the orignal message */
 			if (digeststr_asm(&glb_sdgst,
 							  msg,
 							  &getstr_dynstr(&glb_sdate),
 							  AUTH_OUTGOING_BODY | AUTH_ADD_DATE))
 				return -3;
 			break;
 		default:
 			/*  assemble the digest string and the DATE header is available in the message */
 			if (digeststr_asm(&glb_sdgst, msg, NULL, AUTH_OUTGOING_BODY))
 				return -4;
 			break;
 	}
 
 	/* calculate the SHA1 hash and encrypt with our provate key */
 	if (rsa_sha1_enc(&glb_sdgst, &glb_encedmsg, &glb_b64encedmsg, glb_hmyprivkey))
 		return -5;
 
 	/* we assemble the value of the Identity haader */
 	sstr.s=IDENTITY_FIRST_PART; sstr.len=strlen(IDENTITY_FIRST_PART);
 	if (cpy2dynstr(&glb_sidentity, &sstr))
 		return -6;
 
 	if (app2dynstr(&glb_sidentity, &getstr_dynstr(&glb_b64encedmsg)))
 		return -7;
 
 	sstr.s=IDENTITY_LAST_PART;
 	/* +1 : we need the trailing \0 character too */
 	sstr.len=strlen(IDENTITY_LAST_PART) + 1;
 	if (app2dynstr(&glb_sidentity, &sstr))
 		return -8;
 
 	if (append_hf(msg, getstr_dynstr(&glb_sidentity).s, HDR_IDENTITY_T))
 		return -9;
 
 	if (append_hf(msg, getstr_dynstr(&glb_sidentityinfo).s, HDR_IDENTITY_INFO_T))
 		return -10;
 
 	return 1;
 }