[asterisk-commits] oej: branch oej/sip-identity-trunk r234530 - in /team/oej/sip-identity-trunk:...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Mon Dec 14 04:59:45 CST 2009
Author: oej
Date: Mon Dec 14 04:59:41 2009
New Revision: 234530
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=234530
Log:
Merging patch from issue into svn.
(related to issue #16187)
Reported by: plooby
Patches:
ics_identity_228661.patch uploaded by plooby (license 901)
Modified:
team/oej/sip-identity-trunk/channels/chan_sip.c
team/oej/sip-identity-trunk/configs/extensions.conf.sample
team/oej/sip-identity-trunk/configs/sip.conf.sample
Modified: team/oej/sip-identity-trunk/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/team/oej/sip-identity-trunk/channels/chan_sip.c?view=diff&rev=234530&r1=234529&r2=234530
==============================================================================
--- team/oej/sip-identity-trunk/channels/chan_sip.c (original)
+++ team/oej/sip-identity-trunk/channels/chan_sip.c Mon Dec 14 04:59:41 2009
@@ -622,6 +622,63 @@
#define SDP_MAX_RTPMAP_CODECS 32 /*!< Maximum number of codecs allowed in received SDP */
+/*! \brief RFC 4474 identity related defines */
+#define DEFAULT_IDENTITY_PRIV_URL_PREFIX "http:://localhost/keys/" /*!< default url path to private keys */
+#define DEFAULT_IDENTITY_PRIV_URL_SUFFIX "" /*!< default url suffix to private keys */
+#define DEFAULT_IDENTITY_PUB_URL_PREFIX "http:://localhost/certs/" /*!< default url path to public keys */
+#define DEFAULT_IDENTITY_PUB_URL_SUFFIX "" /*!< default url suffix to public keys */
+#define DEFAULT_IDENTITY_PUB_PATH "/var/lib/asterisk/identity/certs/" /*!< default path to public keys */
+#define DEFAULT_IDENTITY_PRIV_PATH "/var/lib/asterisk/identity/keys/" /*!< default path to private keys */
+#define DEFAULT_IDENTITY_CACHE_PATH "/var/lib/asterisk/identity/cache/" /*!< default url path to cache */
+
+#define IDENTITY_PRIV_URL_PREFIX 255 /*!< maximum length of private url prefix to fetch private key */
+#define IDENTITY_PRIV_URL_SUFFIX 16 /*!< maximum length of private url suffix to fetch private key */
+#define IDENTITY_PUB_URL_PREFIX 255 /*!< maximum length of public url prefix to fetch public key */
+#define IDENTITY_PUB_URL_SUFFIX 16 /*!< maximum length of public url suffix to fetch public key */
+#define IDENTITY_KEYBUF_SIZE 4096 /*!< maximum length of Identity Buffer */
+#define IDENTITY_MAXPORT 65535 /*!< maximum port number */
+#define IDENTITY_SERVER_PORT 80 /*!< Identity Service port, usually 80 for http */
+#define IDENTITY_SIGNBUF_SIZE 1024 /*!< Signature buffer size. */
+#define IDENTITY_HEADER_SIZE 1024 /*!< Max Header Size for identity related headers. */
+
+#define AST_CONFIG_MAX_PATH 255 /*!< Maximum filesystem path length */
+
+/*! \brief RFC 4474 identity key type */
+enum identity_key_type{
+ IDENTITY_PUBLIC, /*!< public key */
+ IDENTITY_PRIVATE, /*!< private key */
+};
+
+
+/*! \brief RFC 4474 identity key type */
+enum identity_action{
+ IDENTITY_ACTION_SIGN, /*!< sign sip message */
+ IDENTITY_ACTION_VERIFY, /*!< verify signature */
+};
+
+
+
+/*! \brief RFC 4474 identity mode */
+enum identity_mode{
+ IDENTITY_DISABLED, /*!< Disable RFC4474 functions */
+ IDENTITY_SIGN, /*!< Enable RFC4474 Signing only */
+ IDENTITY_VALIDATE, /*!< Enable RFC4474 Validate only */
+ IDENTITY_SIGN_AND_VALIDATE, /*!< Enable RFC4474 Sign and Validate */
+};
+
+/*! \brief RFC 4474 identity results */
+enum identity_result{
+ IDENTITY_RES_NONE=1, /*!< sip_identity_mode no result*/
+ IDENTITY_RES_SIGN_OK=2, /*!< sip_identity_mode sign ok */
+ IDENTITY_RES_SIGN_DISABLED=3, /*!< sip_identity_mode sign disabled */
+ IDENTITY_RES_SIGN_BROKEN_KEY=4, /*!< sip_identity_mode sign broken key*/
+ IDENTITY_RES_SIGN_NO_PRIVATE_KEY=5,/*!< sip_identity_mode sign no private key found */
+ IDENTITY_RES_VAL_OK=6, /*!< sip_identity_mode validation ok */
+ IDENTITY_RES_VAL_DISABLED=7, /*!< sip_identity_mode validation disabled */
+ IDENTITY_RES_VAL_BROKEN_CERT=8, /*!< sip_identity_mode validation broken cert*/
+ IDENTITY_RES_VAL_NO_CERT=9, /*!< sip_identity_mode validation no public cert found */
+};
+
/*! \brief Global jitterbuffer configuration - by default, jb is disabled */
static struct ast_jb_conf default_jbconf =
{
@@ -1258,6 +1315,21 @@
static int global_qualify_peers; /*!< Number of peers to poke at a given time */
+/*! \brief RFC 4474 Identity related static variables */
+static enum identity_mode sip_identity_mode;/*!< flag whether to sign the invite, validate or sign and validate*/
+static char identity_private_url_prefix[IDENTITY_PRIV_URL_PREFIX]; /*!< private url prefix is stored here used to fetch
+ the user private key from http or https server*/
+static char identity_private_url_suffix[IDENTITY_PRIV_URL_SUFFIX]; /*!< private url suffix is stored here used to fetch
+ the user private key from http or https server
+ which can be .pem or .cer*/
+static char identity_public_url_prefix[IDENTITY_PUB_URL_PREFIX]; /*!< public url prefix is stored here used to fetch the
+ user public key from http or https server used */
+static char identity_public_url_suffix[IDENTITY_PUB_URL_SUFFIX]; /*!< public url suffix is stored here used to fetch the
+ user public key from http or https server used */
+static char identity_private_path[AST_CONFIG_MAX_PATH]; /*!< location where private keys are stored */
+static char identity_public_path[AST_CONFIG_MAX_PATH]; /*!< location where public keys are stored */
+static char identity_cache_path[AST_CONFIG_MAX_PATH]; /*!< location where keys and certs are cached */
+
static enum st_mode global_st_mode; /*!< Mode of operation for Session-Timers */
static enum st_refresher global_st_refresher; /*!< Session-Timer refresher */
static int global_min_se; /*!< Lowest threshold for session refresh interval */
@@ -1350,6 +1422,7 @@
char debug; /*!< print extra debugging if non zero */
char has_to_tag; /*!< non-zero if packet has To: tag */
char ignore; /*!< if non-zero This is a re-transmit, ignore it */
+ enum identity_result sip_identity_result; /*!< rfc 4474 result signed or validated etc*/
ptrdiff_t header[SIP_MAX_HEADERS]; /*!< Array of offsets into the request string of each SIP header*/
ptrdiff_t line[SIP_MAX_LINES]; /*!< Array of offsets into the request string of each SDP line*/
struct ast_str *data;
@@ -1357,6 +1430,8 @@
struct sip_socket socket; /*!< The socket used for this request */
AST_LIST_ENTRY(sip_request) next;
};
+
+
/* \brief given a sip_request and an offset, return the char * that resides there
*
@@ -2932,6 +3007,985 @@
}
return res;
}
+
+
+/*
+ * Begin RFC4474 Identity definitions configuration values & functions
+ *
+ */
+
+/*! \brief helper function for rfc 4474 Return pointer to string with current SSL error
+ * \return Returns pointer to string with current SSL error
+ */
+static char* ssl_err(void)
+{
+ static char buf[1024];
+ ERR_error_string(ERR_get_error(), buf);
+ return buf;
+}
+
+/*! \brief helper function for rfc 4474 Return pointer to string with result explanation
+ * \param code the numeric (enum) code to be interpreted.
+ * \return Returns pointer to string with current SSL error
+ */
+static const char* ident_status(enum identity_result code)
+{
+ const char* rv;
+ switch(code) {
+ case IDENTITY_RES_NONE:
+ rv="SIP identity no result";
+ break;
+ case IDENTITY_RES_SIGN_OK:
+ rv="SIP identity sign ok";
+ break;
+ case IDENTITY_RES_SIGN_DISABLED:
+ rv="SIP identity sign disabled";
+ break;
+ case IDENTITY_RES_SIGN_BROKEN_KEY:
+ rv="SIP identity sign broken key";
+ break;
+ case IDENTITY_RES_SIGN_NO_PRIVATE_KEY:
+ rv="SIP identity sign no private key found";
+ break;
+ case IDENTITY_RES_VAL_OK:
+ rv="SIP identity validation ok";
+ break;
+ case IDENTITY_RES_VAL_DISABLED:
+ rv="SIP identity validation disabled";
+ break;
+ case IDENTITY_RES_VAL_BROKEN_CERT:
+ rv="SIP identity validation broken certifcate";
+ break;
+ case IDENTITY_RES_VAL_NO_CERT:
+ rv="SIP identity validation no public certifcate found";
+ break;
+ default:
+ rv="SIP identity unknown status";
+
+ }
+ return (rv);
+};
+
+
+/*! \brief helper function for rfc 4474 generate cache file name
+ * \return Returns pointer to filename. must be freed
+ */
+static char* id_cache_filename(const char*url)
+{
+ char *p, *p2;
+ size_t len;
+ char buf[AST_CONFIG_MAX_PATH];
+ strncpy(buf,identity_cache_path,sizeof(buf));
+ len = strlen(buf);
+ p2 = buf + len;
+
+ ast_uri_encode(url,p2,sizeof(buf)-len,1);
+
+ /* substitute specials */
+ while ((p=strchr(buf,'<'))) {
+ *p='_';
+ }
+ while ((p=strchr(buf,'>'))) {
+ *p='_';
+ }
+ return ast_strdup(buf);
+}
+
+
+/*! \brief helper function for rfc 4474 Fill buffer with cached content.
+ * \param url url of source file
+ * \param data output buffer
+ * \param datasize size of output buffer
+ * \return Returns true if found. out will contain file contents
+ */
+static int id_check_cache(const char*url,char *data,size_t datasize)
+{
+ FILE* stream;
+ int rv = FALSE;
+ char *cached_file=id_cache_filename(url);
+ if (sipdebug) {
+ ast_log(LOG_NOTICE,"** Identity key searching in id_check_cache %s => %s\n",url,cached_file);
+ }
+
+ /* does file exist? */
+ if ((stream = fopen (cached_file, "r")) != (FILE *)0) {
+ ast_log(LOG_NOTICE,"id_check_cache: opened %s / %d \n",cached_file,datasize);
+ fread(data, datasize, 1, stream);
+ if ( ferror(stream) ) {
+ ast_log(LOG_ERROR,"id_check_cache: private key not loaded from %s\n",cached_file);
+ }
+ else {
+ ast_log(LOG_NOTICE,"id_check_cache: private key loaded from %s\n",cached_file);
+ }
+ fclose(stream);
+ rv = TRUE;
+ }
+ ast_log(LOG_NOTICE,"id_get_private_key: done %d\n",rv);
+ ast_free(cached_file);
+ return rv;
+}
+
+/*! \brief helper function for rfc 4474 store buffer to cache.
+ * \param url url of source file
+ * \param data output buffer
+ * \param datasize size of output buffer
+ * \return Returns void.
+ */
+static void id_store_cache(const char*url,char *data,size_t datasize)
+{
+ FILE* stream;
+ char *cached_file=id_cache_filename(url);
+ if (sipdebug) {
+ ast_log(LOG_NOTICE,"** Identity key (datasize %d) to be stored in cache %s => %s\n",datasize,url,cached_file);
+ }
+
+ /* does file exist */
+ if ((stream = fopen (cached_file, "w")) != (FILE *)0) {
+ fwrite(data, datasize, 1, stream);
+ if (ferror(stream)) {
+ ast_log(LOG_ERROR,"id_store_cache: private key NOT written to %s\n",cached_file);
+ }
+ else {
+ ast_log(LOG_NOTICE,"id_store_cache: private key written to %s\n",cached_file);
+ }
+ fclose(stream);
+ }
+ else {
+ ast_log(LOG_ERROR,"** Identity id_store_cache cannot write to cache %s => %s\n",url,cached_file);
+ }
+ ast_free(cached_file);
+}
+
+
+/*! \brief helper function for rfc 4474 fetches public cert or private key for the user from server
+ * uses plain old socket to initiate http session to fetch keys from server
+ *
+ * \param type IDENTITY_PRIVATE for private key, IDENTITY_PUBLIC for public key
+ * \param user user name for whom the public or private key is fetched
+ * \param out where the fetched key is placed
+ * \param port port no to connect on the server
+ * \param url server name and path is fetched from url
+ * \return Returns integer result 1 is success, any other value means failure
+ */
+static int id_fetch_key_from_server(enum identity_key_type type,const char *user, char *out, size_t outsize,int port,const char *url)
+{
+ int sockfd, portno, n,torecv=0,rcvd=0;
+ struct sockaddr_in serv_addr;
+ struct hostent *server;
+ struct ast_hostent ae;
+ char *p,*p1,tempbuf[20],buffer[IDENTITY_KEYBUF_SIZE],serveradd[PATH_MAX];
+
+ /* ensure buffers are \0 terminated */
+ serveradd[0]='\0';
+ buffer[0]='\0';
+ out[0]='\0';
+
+ /* check local cache. */
+ if (id_check_cache(url,out,outsize)) {
+ if (sipdebug) {
+ ast_log(LOG_NOTICE,"** Identity key found in cache %s\n%s\n",url,out);
+ }
+ /* out is filled with contents */
+ return TRUE;
+ }
+
+
+ /* check port no */
+ if (port<1 || port>IDENTITY_MAXPORT) {
+ ast_log(LOG_ERROR,"Identity Error invalid port %d\n",port);
+ return -1;
+ }
+
+ /* format the http header buffer to fetch the keys from server */
+ if (type==IDENTITY_PRIVATE) { /* for private key */
+ p=strstr(url,"://");
+ if (p!=NULL) {
+ p1=strstr(p+4,"/");
+ if (p1 != NULL) {
+ memcpy(serveradd,p+3,p1-(p+3));
+ serveradd[p1-(p+3)]='\0';
+ strcpy(buffer,"GET ");
+ strcat(buffer,p1);
+ if (sipdebug) {
+ ast_log(LOG_NOTICE,"** Identity prv srv [%s], path [%s]\n",serveradd,buffer);
+ }
+ }
+ }
+ if (p==NULL || p1==NULL) {
+ ast_log(LOG_ERROR,"** Identity error parsing private URL [%s], path [%s]\n",serveradd,buffer);
+ return -1;
+ }
+ }
+ else { /* for public key */
+ p=strstr(url,"://"); /* Note: scheme is ignored. Assumed http. */
+ if (p!=NULL) {
+ p1=strstr(p+4,"/");
+ if (p1!=NULL) {
+ memcpy(serveradd,p+3,p1-(p+3) );
+ serveradd[p1-(p+3)]='\0';
+ p=strstr(p1,">"); /* mark end of url > */
+ if (p!=NULL) {
+ strcpy(buffer, "GET ");
+ memcpy(buffer + 4, p1, p - p1);
+ buffer[p-p1+4]='\0';
+ }
+ if (sipdebug) {
+ ast_log(LOG_NOTICE,"** Identity pub srv [%s], path [%s]\n", serveradd,buffer);
+ }
+ }
+ }
+ if (p==NULL || p1==NULL) {
+ ast_log(LOG_ERROR,"** Identity error parsing public URL [%s], path [%s]\n",serveradd,buffer);
+ return -1;
+ }
+ }
+
+ /* format the buffer to send to server */
+ strcat(buffer," HTTP/1.1\r\n");
+ strcat(buffer,"Host: ");
+ strcat(buffer,serveradd);
+ strcat(buffer,"\r\n");
+ strcat(buffer,"Accept: */*\r\n");
+ strcat(buffer,"\r\n");
+
+ server = ast_gethostbyname(serveradd,&ae);
+ if (server == NULL) {
+ ast_log(LOG_ERROR, "** Identity ERROR cannot resolve hostname=%s\n",serveradd);
+ return -1;
+ }
+
+ /* populate connect to server addr */
+ portno = port;
+ if (sipdebug) {
+ ast_log(LOG_NOTICE,"** Identity id_fetch_key_from_server(); srv: %s, buff: \n%s\nport: %d\n",serveradd, buffer, portno);
+ }
+
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sockfd < 0) {
+ ast_log(LOG_ERROR,"** Identity Error opening socket\n");
+ return -1;
+ }
+ memset((char *) &serv_addr,'\0', sizeof(serv_addr));
+ serv_addr.sin_family = AF_INET;
+ memcpy((char *)&serv_addr.sin_addr.s_addr, (char *)server->h_addr, server->h_length);
+ serv_addr.sin_port = htons(portno);
+ if (connect(sockfd,&serv_addr,sizeof(serv_addr)) < 0) {
+ ast_log(LOG_ERROR,"** Identity ERROR connecting \n");
+ return -1;
+ }
+ /* send to server the buffer */
+ n = write(sockfd,buffer,strlen(buffer));
+ if (n < 0) {
+ close(sockfd);
+ ast_log(LOG_ERROR,"** Identity ERROR writing to socket\n");
+ return -1;
+ }
+ /* read from server */
+ n = read(sockfd,buffer,sizeof(buffer));
+ if (n <= 0) {
+ close(sockfd);
+ ast_log(LOG_ERROR,"** Identity ERROR reading from socket\n");
+ return -1;
+ }
+ /* make sure content is null terminated */
+ buffer[(n<sizeof(buffer))?n:sizeof(buffer)-1]='\0';
+ /* write the read buffer from \r\n\r\n to out */
+ p=buffer;
+ p1=strstr(buffer,"\r\n\r\n");
+ if (p1!=NULL) {
+ memcpy(out,p1+4,strlen(buffer)-(p1+4-p));
+ out[strlen(buffer)-(p1+4-p)]='\0';
+ }
+ /* validate the response from host*/
+ p=strstr(buffer,"200 OK");
+ if (p==NULL) {
+ close(sockfd);
+ ast_log(LOG_ERROR,"** Identity ERROR key not found on server [%s] %s\n",serveradd,url);
+ ast_log(LOG_ERROR,"********************\n");
+ ast_log(LOG_ERROR,"buffer %s\n",buffer);
+ ast_log(LOG_ERROR,"********************\n");
+ return -1;
+ }
+ /* get content length*/
+ p=strstr(buffer,"Content-Length:");
+ if (p!=NULL) {
+ char* p2 = p+sizeof("Content-Length:");
+ p1=strstr(p2,"\r\n");
+ if (p1!=NULL) {
+ memcpy(tempbuf,p2,p1-p2);
+ tempbuf[p1-p2]='\0';
+ torecv=strtol(tempbuf,NULL, 10);
+ }
+ }
+ /* check the length for validity */
+ if (torecv<=0) {
+ ast_log(LOG_ERROR,"** Identity ERROR nothing to receive [%d]\n",torecv);
+ close(sockfd);
+ return -1;
+ }
+ /* find out whats received so far */
+ rcvd=strlen(out);
+ while (rcvd<torecv) {
+ buffer[0]='\0';
+ n = read(sockfd,buffer,sizeof(buffer));
+ if (n<0) {
+ ast_log(LOG_ERROR,"** Identity ERROR socket read failed [%d]\n",torecv);
+ close(sockfd);
+ return -1;
+ }
+ memcpy(out+rcvd,buffer,n);
+ rcvd+=n;
+ }
+ out[rcvd]='\0';
+ close(sockfd);
+
+ id_store_cache(url,out,rcvd);
+ return 1; /* success */
+}
+
+
+/*! \brief helper function for rfc 4474 gets private key from server and returns in EVP_PKEY format
+ *
+ * \param user user name for whom the private key is fetched
+ * \return Returns private key in EVP_PKEY or NULL if failed
+ */
+
+static EVP_PKEY* id_get_private_key(const char *user)
+{
+ BIO *pem_bio;
+ EVP_PKEY *pkey;
+ char data[IDENTITY_KEYBUF_SIZE];
+ char url[AST_CONFIG_MAX_PATH];
+ char filename[AST_CONFIG_MAX_PATH];
+ FILE *stream;
+
+ data[0]='\0';
+
+ /* get private key from file system */
+ strcpy(filename,identity_private_path);
+ strcat(filename,user);
+
+ if (sipdebug) {
+ ast_log(LOG_NOTICE,"id_get_private_key: private key potentially to be loaded from %s\n",filename);
+ }
+ if ((stream = fopen (filename, "r")) != (FILE *)0) {
+ fread(data, sizeof(data), 1, stream);
+ if (ferror(stream)){
+ ast_log(LOG_ERROR,"id_get_private_key: private key loading error from %s\n",filename);
+ }
+ else {
+ if (sipdebug) {
+ ast_log(LOG_NOTICE,"id_get_private_key: private key was loaded from %s\n",filename);
+ }
+ }
+ fclose(stream);
+ }
+ else {
+ strcpy(url,identity_private_url_prefix); //etg
+ strcat(url,user);
+ strcat(url,identity_private_url_suffix);
+ /* fetch the private key from server */
+ if (id_fetch_key_from_server(IDENTITY_PRIVATE,user,data,sizeof(data),IDENTITY_SERVER_PORT,url)!=1) {
+ ast_log(LOG_ERROR,"** id_get_private_key: get private key failed %s\n",user);
+ return NULL;
+ }
+ }
+ /* return private key in EVP_PKEY format */
+ if (NULL == (pem_bio = BIO_new_mem_buf(data, strlen(data)))) {
+ ast_log(LOG_ERROR,"** id_get_private_key: unable to initialize BIO\n");
+ return NULL;
+ }
+ if (NULL == (pkey = PEM_read_bio_PrivateKey(pem_bio, NULL, 0, NULL))) {
+ ast_log(LOG_ERROR,"** id_get_private_key: could not decode private key from %s\n", ssl_err());
+ BIO_free(pem_bio);
+ return NULL;
+ }
+ BIO_free(pem_bio);
+ return pkey;
+}
+
+/*! \brief helper function for rfc 4474 gets public key from server and returns in X509 format
+ *
+ * \param user user name for whom the public key is fetched
+ * \param idinfohdr identity info header from sip request which contains the URL
+ * \return Returns private key in X509 or null
+ */
+static X509* id_get_public_cert(const char *user,const char *idinfohdr)
+{
+ BIO *pem_bio;
+ X509 *cert;
+ char data[IDENTITY_KEYBUF_SIZE];
+ char filename[AST_CONFIG_MAX_PATH];
+ FILE *stream;
+
+ data[0]='\0';
+
+ /* get key from file system */
+ strcpy(filename,identity_public_path);
+ strcat(filename,user);
+
+ if ((stream = fopen (filename, "r")) != (FILE *)0) {
+ fread(data, sizeof(data), 1, stream);
+ if (ferror(stream)) {
+ ast_log(LOG_ERROR,"id_get_public_cert: private key loading error from %s\n",filename);
+ }
+ else {
+ if (sipdebug) {
+ ast_log(LOG_NOTICE,"id_get_public_cert: private key loaded from %s\n",filename);
+ }
+ }
+ fclose(stream);
+ }
+ else {
+ /* fetch the public key from server */
+ if (id_fetch_key_from_server(IDENTITY_PUBLIC,user,data,sizeof(data),IDENTITY_SERVER_PORT,idinfohdr)!=1) {
+ ast_log(LOG_ERROR,"** id_get_public_cert get public key failed %s\n",user);
+ return NULL;
+ }
+ }
+ if (sipdebug) {
+ ast_log(LOG_NOTICE,"** id_get_public_cert: user: %s data: %s \n", user, data);
+ }
+ /* return public key in EVP_PKEY format */
+ if (NULL == (pem_bio = BIO_new_mem_buf(data, strlen(data)))) {
+ ast_log(LOG_ERROR,"** id_get_public_cert: unable to initialize BIO\n");
+ return NULL;
+ }
+ if (NULL == (cert = PEM_read_bio_X509(pem_bio, NULL, 0, 0))) {
+ ast_log(LOG_ERROR,"** id_get_public_cert: could not decode public key from %s\n", ssl_err());
+ BIO_free(pem_bio);
+ return NULL;
+ }
+ BIO_free(pem_bio);
+ return cert;
+}
+
+
+/*! \brief helper function for rfc 4474 gets public key from server and returns in EVP_PKEY format
+ *
+ * \param user user name for whom the public key is fetched
+ * \param idinfohdr identity info header from sip request which contains the URL
+ * \return Returns private key in EVP_PKEY or null
+ */
+static EVP_PKEY* id_get_public_key(X509* cert)
+{
+ EVP_PKEY *pkey;
+ if (NULL == (pkey = X509_get_pubkey(cert))) {
+ ast_log(LOG_ERROR,"** id_get_public_key: could not get public key from %s\n", ssl_err());
+ }
+ return pkey;
+}
+
+/*! \brief helper function for rfc 4474 does based 64 encoding
+ *
+ * \param buf pointer to data to be encoded
+ * \param buflen length of the buffer to be encoded
+ * \return Returns pointer to encoded data or NULL if failed
+ */
+static char* id_b64_encode(const char* buf, int buflen)
+{
+ BIO *bmem, *b64;
+ BUF_MEM *bptr;
+ char *ret;
+
+ b64 = BIO_new(BIO_f_base64());
+ if (b64 == NULL) {
+ ast_log(LOG_ERROR,"** id_b64_encode: could not initialize b64 BIO: %s\n",ssl_err());
+ return NULL;
+ }
+ bmem = BIO_new(BIO_s_mem());
+ if (bmem == NULL) {
+ ast_log(LOG_ERROR,"** id_b64_encode: could not initialize bmem BIO: %s\n",ssl_err());
+ BIO_free_all(b64);
+ return NULL;
+ }
+ BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
+ b64 = BIO_push(b64, bmem);
+ if (BIO_write(b64, buf, buflen) != buflen) {
+ ast_log(LOG_ERROR,"** id_b64_encode: could not write: %s\n",ssl_err() );
+ BIO_free_all(b64);
+ return NULL;
+ }
+ BIO_flush(b64);
+ BIO_get_mem_ptr(b64, &bptr);
+ ret = ast_calloc(1, bptr->length+1);
+ if (ret == NULL) {
+ ast_log(LOG_ERROR,"** id_b64_encode: out of memory\n");
+ BIO_free_all(b64);
+ return NULL;
+ }
+ memcpy(ret, bptr->data, bptr->length);
+ ret[bptr->length] = 0;
+
+ BIO_free_all(b64);
+ return ret;
+}
+
+/*! \brief helper function for rfc 4474 does based 64 decoding
+ *
+ * \param buf pointer to data to be decoded
+ * \param buflen length of the buffer to be decoded
+ * \param decoded_data_len contains length of the decoded data this is an output param
+ * \return Returns pointer to decoded data or NULL if failed
+ */
+static char* id_b64_decode(const char* buf, int buflen,int *decoded_data_len)
+{
+ BIO *bmem, *b64;
+ char *ret;
+ int len=0;
+
+ b64 = BIO_new(BIO_f_base64());
+ if (b64 == NULL) {
+ ast_log(LOG_ERROR,"** id_b64_decode: could not initialize b64 BIO: %s\n", ssl_err());
+ return NULL;
+ }
+ BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
+ bmem = BIO_new_mem_buf((void*)buf, buflen);
+ if (bmem == NULL) {
+ ast_log(LOG_ERROR,"** id_b64_decode: could not initialize bmem BIO: %s\n", ssl_err());
+ BIO_free_all(b64);
+ return NULL;
+ }
+ bmem = BIO_push(b64, bmem);
+ ret = ast_calloc(1, buflen+1);
+ if (ret == NULL) {
+ ast_log(LOG_ERROR,"** id_b64_decode: out of memory\n");
+ BIO_free_all(bmem);
+ return NULL;
+ }
+ memcpy(ret, buf, buflen);
+ ret[buflen] = '\0';
+ len = BIO_read(bmem, ret, buflen+1);
+ if (len <= 0) {
+ ast_log(LOG_ERROR,"** id_b64_decode: could not read: %s\n", ssl_err());
+ BIO_free_all(bmem);
+ return NULL;
+ }
+ ret[len] = '\0';
+ *decoded_data_len=len;
+ if (sipdebug) {
+ ast_log(LOG_NOTICE,"** b64decode: i-buf: %s i-buflen: %d read-len: %d \n", buf, buflen, len);
+ }
+ BIO_free_all(b64);
+ return ret;
+}
+/*! \brief helper function for rfc 4474 does SHA1 hashing
+ *
+ * \param buf pointer to data to be SHA1 hashed
+ * \param retbuf pointer to hashed data
+ * \return Returns pointer to hashed data
+ */
+static char* id_hashstr(const char *buf,char *retbuf)
+{
+ SHA_CTX sha;
+ SHA1_Init(&sha);
+ SHA1_Update(&sha, buf, strlen(buf));
+ SHA1_Final((unsigned char*)retbuf, &sha);
+ return retbuf;
+}
+/*! \brief main function for rfc 4474 does signing
+ *
+ * \param tosign pointer to data to be signed (RSA SHA1)
+ * \param res pointer to signed data
+ * \param user pointer to user for whom the key is fetched and data is signed using the private key of user
+ * \return Returns 1 for success any other value failure
+ */
+static int id_sign(const char *tosign,char *res,const char *user)
+{
+ EVP_PKEY *pkey;
+ RSA *rsa;
+ unsigned char result[256];
+ unsigned int resultlen = sizeof(result);
+ unsigned char sha1res[20];
+ char *id;
+ /* hash the digest string */
+ if (id_hashstr(tosign,(char *) sha1res) == NULL) {
+ ast_log(LOG_ERROR,"** id_sign(): hash failed\n");
+ return FALSE;
+ }
+ /* get private key for user */
+ pkey = id_get_private_key(user);
+ if (pkey == NULL) {
+ ast_log(LOG_ERROR,"** id_sign(): get private key failed\n");
+ return FALSE;
+ }
+
+ if (NULL == (rsa = EVP_PKEY_get1_RSA(pkey))) {
+ ast_log(LOG_ERROR,"** id_sign(): Unable to get RSA Key: %s\n", ssl_err());
+ EVP_PKEY_free(pkey);
+ return FALSE;
+ }
+ EVP_PKEY_free(pkey);
+ if (resultlen < RSA_size(rsa)) {
+ ast_log(LOG_ERROR,"** id_sign(): RSA Result too big: %d !>= %d\n", resultlen, RSA_size(rsa));
+ return FALSE;
+ }
+ /* do RSA sign */
+ if (RSA_sign(NID_sha1, sha1res, sizeof(sha1res), result, &resultlen, rsa) != 1) {
+ ast_log(LOG_ERROR,"** id_sign(): Error computing RSA results: %s\n", ssl_err());
+ RSA_free(rsa);
+ return FALSE;
+ }
+ RSA_free(rsa);
+ /* do base64 encoding */
+ id = id_b64_encode((char *)result, resultlen);
+ memcpy(res,id,strlen(id)+1);
+ ast_free(id);
+ return TRUE;
+}
+
+/*! \brief callback function for rfc 4474. Interprets errors
+ *
+ * \param ok status
+ * \param ctx pointer to x509 store cotext
+ * \return Returns 1 for success, 0 value failure
+ */
+static int verify_id_cb(int ok, X509_STORE_CTX *ctx)
+{
+ char buf[512]; /* cert subject buffer. size ok */
+
+ if (sipdebug && ctx->current_cert) {
+ X509_NAME_oneline( X509_get_subject_name(ctx->current_cert),buf, sizeof(buf));
+ ast_log(LOG_NOTICE,"** verify_id_cb(%d): %s\n",ok,buf);
+ }
+ if (!ok) {
+ ast_log(LOG_ERROR,"** verify_id_cb: error %d at %d depth lookup:%s\n",ctx->error,
+ ctx->error_depth, X509_verify_cert_error_string(ctx->error));
+ /* allow self signing as OK */
+ if (ctx->error == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) {
+ ok=1;
+ }
+ }
+ return(ok);
+}
+
+
+
+
+/*! \brief main function for rfc 4474 does verifying the signature
+ *
+ * \param digeststr pointer to digest data
+ * \param identity pointer to identity header data from sip request
+ * \param user pointer to user for whom the key is fetched and data is verified using the public key of user
+ * \param identity_info pointer to identity-info header data from sip request
+ * \return Returns 1 for success any other value failure
+ */
+static int id_verify(const char *digeststr,const char *identity,const char *user,const char *identity_info)
+{
+ char *sig;
+ EVP_PKEY *pkey;
+ RSA *rsa;
+ X509 *cert;
+ int retlen;
+ unsigned char sha1res[20];
+ X509_NAME *nm;
+ X509_STORE *ctx;
+ X509_STORE_CTX *csc;
+ int cn_match;
+ int lastpos;
+
+ if (sipdebug) {
+ ast_log(LOG_NOTICE,"** id_verify: digeststr: %s iden: %s user: %s idinfo: %s\n", digeststr, identity, user, identity_info);
+ }
+
+ /* hash the digest string */
+ if (id_hashstr(digeststr, (char *)sha1res) == NULL) {
+ return FALSE;
+ }
+ /* base 64 decode of the identity header */
+ if ((sig=id_b64_decode(identity,strlen(identity),&retlen)) == NULL) {
+ ast_log(LOG_ERROR,"** id_verify(): Cannot decode Identity header\n");
+ return FALSE;
+ }
+ /* get public key for the user */
+ cert = id_get_public_cert(user,identity_info);
+ if (cert == NULL) {
+ return FALSE;
+ }
+ pkey = id_get_public_key(cert);
+ if (pkey == NULL) {
+ X509_free(cert);
+ return FALSE;
+ }
+ if (NULL == (rsa = EVP_PKEY_get1_RSA(pkey))) {
+ ast_log(LOG_ERROR,"** id_verify: Unable to get RSA Key: %s\n", ssl_err());
+ X509_free(cert);
+ EVP_PKEY_free(pkey);
+ return FALSE;
+ }
+ EVP_PKEY_free(pkey);
+
+ nm = X509_get_subject_name(cert);
+ if (sipdebug) {
+ char subject[256];
+ X509_NAME_oneline(nm, subject, sizeof(subject));
+ ast_log(LOG_NOTICE, "** id_verify Certificate Subject = %s\n", subject);
+ ast_log(LOG_NOTICE, "** id_verify: size-sha1res: %d retlen: %d \n", sizeof(sha1res), retlen);
+ }
+
+ /* do rsa verify */
+ if (RSA_verify(NID_sha1, sha1res, sizeof(sha1res),(unsigned char *) sig, retlen, rsa) != 1) {
+ ast_log(LOG_ERROR,"** id_verify: Verify failed: %s\n", ssl_err());
+ X509_free(cert);
+ RSA_free(rsa);
+ return FALSE;
+ }
+ RSA_free(rsa);
+
+
+ /* loop through the CNs */
+ cn_match = FALSE;
+ lastpos = -1 ;
+ do {
+ int name_len;
+ ASN1_STRING *tmp;
+ char *cname2;
+
+ lastpos = X509_NAME_get_index_by_NID(nm, NID_commonName, lastpos);
+ if (lastpos < 0) {
+ break;
+ }
+ tmp = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(nm,lastpos));
+ if (!tmp) {
+ ast_log(LOG_ERROR, "** id_verify: Error Processing certificate CN\n");
+ break;
+ }
+ name_len = ASN1_STRING_length(tmp);
+ cname2 = (char*)ast_malloc(name_len+1); /* remember ASN1 does not count \0 */
+ memcpy(cname2, ASN1_STRING_data(tmp), ASN1_STRING_length(tmp));
+ cname2[name_len]='\0';
+
+ cn_match = (0==strncmp(user,cname2,name_len+1));
+ if (sipdebug) {
+ ast_log(LOG_DEBUG, "** id_verify: CNAME = %s match:%s\n",cname2,cn_match?"yes":"no");
+ }
+ ast_free(cname2);
+ }
+ while ( !cn_match );
+
+ if (!cn_match) {
+ ast_log(LOG_ERROR, "** id_verify: FAIL: user %s not found in cert CN\n",user);
+ X509_free(cert);
+ return FALSE;
+ }
+
+ if (sipdebug) {
+ ast_log(LOG_NOTICE, "** id_verify: Callerid(num) matches Common Name. ok\n");
+ }
+
+ /* now verify chain */
+ ctx=X509_STORE_new();
+ X509_STORE_set_verify_cb_func(ctx,verify_id_cb);
+
+ if (X509_STORE_set_default_paths(ctx) != 1) {
+ ast_log(LOG_ERROR, " id_verify: X509_STORE_set_default_paths failed\n");
+ X509_STORE_free(ctx);
+ X509_free(cert);
+ return FALSE;
+ }
+ csc = X509_STORE_CTX_new();
+ if (!csc) {
+ ast_log(LOG_ERROR, " id_verify: X509_STORE_CTX_new failed\n");
+ X509_STORE_free(ctx);
+ X509_free(cert);
+ return FALSE;
+ }
+ if (!X509_STORE_CTX_init(csc,ctx,cert,NULL)) {
+ ast_log(LOG_ERROR, " id_verify: X509_STORE_CTX_init failed\n");
+ X509_STORE_free(ctx);
+ X509_STORE_CTX_free(csc);
+ X509_free(cert);
+ return FALSE;
+ }
+ if (!X509_verify_cert(csc)) {
+ ast_log(LOG_ERROR, " id_verify: X509_verify_cert failed.\n");
+ X509_STORE_free(ctx);
+ X509_STORE_CTX_free(csc);
+ X509_free(cert);
+ return FALSE;
+ }
+
+ if (sipdebug) {
+ ast_log(LOG_NOTICE, "** id_verify: validates ok\n");
+ }
+
+ /* verified ok. Now free everything */
+ X509_STORE_free(ctx);
+ X509_STORE_CTX_free(csc);
+ X509_free(cert);
+ ast_free(sig);
+
+ return TRUE;
+}
+/*! \brief helper function for rfc 4474 gets the specific sip header requested
+ *
+ * \param req pointer to sip_request strucutre
+ * \param name pointer to header value being requested
+ * \param out pointer to buffer where result is stored
+ */
+static void id_get_sip_header(struct sip_request *req,const char *name,char *out,size_t size)
+{
+ const char *p1,*p;
+ p=get_header(req, name);
+
+ if (p!=NULL) {
+ p1=strstr(p,"\r\n");
+ if (p1!=NULL) {
+ size_t len = p1-p;
+ /* reduce size if too long - will likely fail signing */
+ if ( len >= size ) {
+ len=size-1;
+ ast_log(LOG_ERROR,"** id_verify(): sip header too long: %s\n", p);
+ }
+ memcpy(out,p,size);
+ out[len]='\0';
+ }
+ else {
+ strncpy(out,p,size);
+ out[size-1]='\0';
+ }
+ }
+ else { /* no \r\n found so it should be good to copy */
+ strncpy(out,p,size);
+ out[size-1]='\0'; /* ensure null terminated */
+ }
+}
+
+/*! \brief helper function for rfc 4474 gets the address of record
+ *
+ * \param req pointer to sip_request strucutre
+ * \param name pointer to Address of record value being requested
+ * \param out pointer to buffer where result is stored
+ */
+
+static void id_get_addr_of_record(struct sip_request *req,const char *name,char *out,size_t size)
+{
+ char HeaderReq[IDENTITY_HEADER_SIZE];
+ char *p,*p1;
+
+ id_get_sip_header(req, name,HeaderReq,sizeof(HeaderReq));
+ p=strchr(HeaderReq,'<');
+ if (p!=NULL) {
+ p1=strchr(p,'>');
+ if (p1!=NULL) {
+ if (p1-p-1 > size ) {
+ ast_log(LOG_ERROR,"** id_get_addr_of_record: field too large \n");
+ out[0]='\0';
+ }
+ else {
+ memcpy(out,p+1,p1-p-1);
+ out[p1-p-1]='\0';
+ }
+ }
+ }
+}
+
+/*! \brief entry function for rfc 4474 does signing or verifying signature
+ *
+ * \param req pointer to sip_request structure
+ * \param out pointer to where signing or verifying result is placed (out)
+ * \param sdp pointer to optional sdp data which can be passed as input
+ * \param type 0 for signing 1 for verifying
+ * \param userout pointer where user name is stored in case verifying is done (out)
+ */
+static int id_main(struct sip_request *req,char *out,const char *sdp,enum identity_action type,char *userout,size_t userout_size)
+{
+ char digeststr[2*IDENTITY_HEADER_SIZE],signstr[IDENTITY_HEADER_SIZE],callid[IDENTITY_HEADER_SIZE],cseq[IDENTITY_HEADER_SIZE],date[IDENTITY_HEADER_SIZE];
+ char contactAor[IDENTITY_HEADER_SIZE],toAor[IDENTITY_HEADER_SIZE],fromAor[IDENTITY_HEADER_SIZE],user[40];
+ char idstoverify[IDENTITY_HEADER_SIZE],idinfo[IDENTITY_HEADER_SIZE];
+ char *p,*p1;
+ int rv=0; /* returned value. >1 is success */
+
+ /* ensure all buffers are null strings */
+ contactAor[0]=toAor[0]=fromAor[0]=user[0]= idstoverify[0]=idinfo[0]= digeststr[0]=signstr[0]=callid[0]=cseq[0]=date[0] ='\0';
+
+ if (type==IDENTITY_ACTION_VERIFY) { /* verify the signature*/
+ id_get_sip_header(req,"Identity-Info",idinfo,sizeof(idinfo));
+ if (strlen(idinfo)==0) {
+ return FALSE;
+ }
+ id_get_sip_header(req,"Identity",idstoverify,sizeof(idstoverify));
+ if (sipdebug) {
+ ast_log(LOG_NOTICE,"\n** id_main()\n idstoverify: %s \n idinfo: %s \n ", idstoverify, idinfo);
+ }
+ }
+
+ /* get various header fields */
+ id_get_sip_header(req, "Call-ID",callid,sizeof(callid));
+ id_get_sip_header(req, "CSeq",cseq,sizeof(cseq));
+ id_get_sip_header(req, "Date",date,sizeof(date));
+ /* get address of record */
+ id_get_addr_of_record(req,"To",toAor,sizeof(toAor));
+ id_get_addr_of_record(req,"From",fromAor,sizeof(fromAor));
+ id_get_addr_of_record(req,"Contact",contactAor,sizeof(contactAor));
+ /* get user from from header */
+ if (strlen(fromAor)>0) {
+ p1=strstr(fromAor,":"); /* find sip: */
+ if (p1!=NULL) {
+ p=strstr(p1+1,"@");
+ memcpy(user,p1+1,p-(p1+1));
+ user[p-(p1+1)]='\0';
+ strncpy(userout,user,userout_size);
+ userout[userout_size-1]='\0'; /* ensure null terminated */
+ }
+ }
+ /*build the digest string fromAOR| toAOR| Call id| cSEQ|date|Contacat AOR |SDP*/
+ strcpy(digeststr,fromAor);
+ strcat(digeststr,"|");
+ strcat(digeststr,toAor);
+ strcat(digeststr,"|");
+ strcat(digeststr,callid);
+ strcat(digeststr,"|");
+ strcat(digeststr,cseq);
+ strcat(digeststr,"|");
+ strcat(digeststr,date);
+ strcat(digeststr,"|");
+ strcat(digeststr,contactAor);
+ strcat(digeststr,"|");
+ /*if sdp is not passed then find it*/
+ if (sdp==NULL) {
+ if (find_sdp(req)) {
+ int x;
+ for (x = 0; x < (req->lines); x++) {
+ char *p,*line2;
+ line2=ast_strdup(REQ_OFFSET_TO_STR(req, line[x]));
+ p=strstr(line2,"\r\n");
+ if (p!=NULL) {
+ *p='\0';
+ }
+ strcat(digeststr,line2);
+ strcat(digeststr,"\r\n");
+ ast_free(line2);
+ }
+ }
+ }
+ else {
+ strcat(digeststr,sdp);
+ }
+
+ /* call appropriate function depending on type */
+ if (type==IDENTITY_ACTION_SIGN) { /* sign */
+ if ( (rv = id_sign(digeststr,signstr,user)) && sipdebug) {
+ ast_log(LOG_NOTICE,"** id_main() Signing Success \n");
+ }
+
+ }
+ else if (type==IDENTITY_ACTION_VERIFY) { /* verify */
+ rv = id_verify(digeststr,idstoverify,user,idinfo);
+ }
+
+ strcpy(out,signstr);
+ if (sipdebug) {
+ ast_log(LOG_NOTICE,"** Identity id_main() size: %d digeststr: %s\n", strlen(digeststr), digeststr);
+ }
+ return rv;
+}
+
+/*
+ * End RFC4474 Identity definitions configuration values & functions
+ *
+ */
+
static void tcptls_packet_destructor(void *obj)
{
@@ -10601,6 +11655,16 @@
char buf[SIPBUFSIZE];
char dummy_answer[256];
+ char signed_identity_digest[IDENTITY_SIGNBUF_SIZE]; /* signed identity digest header parameter */
+ char user[IDENTITY_SIGNBUF_SIZE]; /* user CALLERID(NUM) */
+ char idinfo[IDENTITY_SIGNBUF_SIZE]; /* identity-info header parameter */
+ char result_number[3]; /* Result code */
+ struct ast_str *sdp1; /* working copy of sdp for identity */
+
+ signed_identity_digest[0]='\0';
+ user[0]='\0';
+ idinfo[0]='\0';
+
/* Set the SDP session name */
snprintf(subject, sizeof(subject), "s=%s\r\n", ast_strlen_zero(global_sdpsession) ? "-" : global_sdpsession);
@@ -10857,6 +11921,82 @@
len += m_modem->used + a_modem->used;
add_header(resp, "Content-Type", "application/sdp");
+
+ /* add identity header can be done only here */
+
+ /* build a disposable SDP for signing. cant use real SDP because once add_line is called, cant add_header */
+ sdp1 = ast_str_create(1024);
+ ast_str_set(&sdp1,0,version);
+ ast_str_append(&sdp1,0,owner);
+ ast_str_append(&sdp1,0,subject);
+ ast_str_append(&sdp1,0,connection);
+
+ if (needvideo){
+ ast_str_append(&sdp1,0,bandwidth);
+ }
+
+ ast_str_append(&sdp1,0,session_time);
+ if (needaudio) {
+ ast_str_append(&sdp1,0,m_audio->str);
+ ast_str_append(&sdp1,0,a_audio->str);
+ ast_str_append(&sdp1,0,hold);
+ }
+
+ if (needvideo) { /* only if video response is appropriate */
+ ast_str_append(&sdp1,0,m_video->str);
+ ast_str_append(&sdp1,0,a_video->str);
+ ast_str_append(&sdp1,0,hold);
+ }
+ if (needtext) { /* only if text response is appropriate */
+ ast_str_append(&sdp1,0,m_text->str);
+ ast_str_append(&sdp1,0,a_text->str);
+ ast_str_append(&sdp1,0,hold);
+ }
+ if (add_t38) { /* only if t38 FAX response is appropriate */
+ ast_str_append(&sdp1,0,m_modem->str);
+ ast_str_append(&sdp1,0,a_modem->str);
+ ast_str_append(&sdp1,0,hold);
+ }
+
+ /* get and add the identity string */
+ if (p->method==SIP_INVITE && (sip_identity_mode==IDENTITY_SIGN || sip_identity_mode==IDENTITY_SIGN_AND_VALIDATE)) {
+ char idinfo[IDENTITY_SIGNBUF_SIZE];
+ /* call get digest string to get the signing result*/
+ id_get_sip_header(&p->initreq,"Identity-Info",idinfo,sizeof(idinfo));
+ if (debug) {
+ ast_log(LOG_NOTICE,"set idinfo [%s]\n",idinfo);
+ }
+ id_main(resp,signed_identity_digest,ast_str_buffer(sdp1),IDENTITY_ACTION_SIGN,user,sizeof(user));
+ ast_free(sdp1);
+ if (strlen(signed_identity_digest)>0) {
+ strcpy(idinfo,"<");
+ strcpy(&idinfo[1],identity_public_url_prefix);
+ strcat(idinfo,user);
+ strcpy(&idinfo[strlen(idinfo)],identity_public_url_suffix);
+ strcat(idinfo,">;alg=rsa-sha1");
+ add_header(resp, "Identity", signed_identity_digest);
+ add_header(resp, "Identity-Info",idinfo);
+ if (sipdebug) {
+ ast_log(LOG_NOTICE,"id [%s]\n idinfo [%s]\n",signed_identity_digest,idinfo);
+ }
+ p->initreq.sip_identity_result=IDENTITY_RES_SIGN_OK;
+ }
+ else {
+ p->initreq.sip_identity_result=IDENTITY_RES_SIGN_BROKEN_KEY; /* assume broken for now */
+ }
+ }
+ else {
+ p->initreq.sip_identity_result=IDENTITY_RES_SIGN_DISABLED;
+ }
+
+ /* set channel variable with result */
+ sprintf(result_number,"%2d",p->initreq.sip_identity_result);
+ pbx_builtin_setvar_helper(p->owner,"IDENTITY_RESULT",result_number);
+ if (sipdebug) {
[... 350 lines stripped ...]
More information about the asterisk-commits
mailing list