فهرست منبع

modules/auth_ims: first commit of auth_ims module
- This module provides IMS specific authentication/authorisation functionality.

Jason Penton 12 سال پیش
والد
کامیت
1ecbbbbe2c

+ 22 - 0
modules/auth_ims/Makefile

@@ -0,0 +1,22 @@
+# $Id$
+#
+# Digest Authentication - IMS support
+#
+# 
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=auth_ims.so
+LIBS=
+
+DEFS+=-DOPENSER_MOD_INTERFACE
+
+DEFS += -I/usr/include/libxml2
+LIBS += -L$(LOCALBASE)/lib -lxml2 -lrt
+
+SERLIBPATH=../../lib
+SER_LIBS+=$(SERLIBPATH)/kcore/kcore
+SER_LIBS+=$(SERLIBPATH)/ims/kamailio_ims
+
+include ../../Makefile.modules

+ 98 - 0
modules/auth_ims/api.h

@@ -0,0 +1,98 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * 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
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * 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
+ * 
+ */
+
+#ifndef _AUTH_IMS_API_H_
+#define _AUTH_IMS_API_H_
+
+#include "../../sr_module.h"
+#include "../../parser/msg_parser.h"
+
+/**
+ * return codes to config by auth functions
+ */
+typedef enum auth_cfg_result {
+	AUTH_USER_MISMATCH = -8,    /*!< Auth user != From/To user */
+	AUTH_NONCE_REUSED = -6,     /*!< Returned if nonce is used more than once */
+	AUTH_NO_CREDENTIALS = -5,   /*!< Credentials missing */
+	AUTH_STALE_NONCE = -4,      /*!< Stale nonce */
+	AUTH_USER_UNKNOWN = -3,     /*!< User not found */
+	AUTH_INVALID_PASSWORD = -2, /*!< Invalid password */
+	AUTH_ERROR = -1,            /*!< Error occurred */
+	AUTH_DROP = 0,              /*!< Error, stop config execution */
+	AUTH_OK = 1                 /*!< Success */
+} auth_cfg_result_t;
+
+typedef int (*digest_authenticate_f)(struct sip_msg* msg, str *realm,
+				str *table, hdr_types_t hftype);
+/**
+ * @brief AUTH_IMS API structure
+ */
+typedef struct auth_ims_api {
+	digest_authenticate_f digest_authenticate;
+} auth_ims_api_t;
+
+typedef int (*bind_auth_ims_f)(auth_ims_api_t* api);
+
+/**
+ * @brief Load the AUTH_IMS API
+ */
+static inline int auth_IMS_load_api(auth_ims_api_t *api)
+{
+	bind_auth_ims_f bindauthims;
+
+	bindauthims = (bind_auth_ims_f)find_export("bind_auth_ims", 0, 0);
+	if(bindauthims == 0) {
+		LM_ERR("cannot find bind_auth_ims\n");
+		return -1;
+	}
+	if (bindauthims(api)==-1)
+	{
+		LM_ERR("cannot bind authims api\n");
+		return -1;
+	}
+	return 0;
+}
+
+#endif /* _AUTH_IMS_API_H_ */

+ 316 - 0
modules/auth_ims/authims_mod.c

@@ -0,0 +1,316 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * 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
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * 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
+ * 
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "stats.h"
+#include "../../sr_module.h"
+#include "../../lib/srdb1/db.h"
+#include "../../dprint.h"
+#include "../../error.h"
+#include "../../mod_fix.h"
+#include "../../trim.h"
+#include "../../mem/mem.h"
+#include "../../modules/sl/sl.h"
+#include "../cdp/cdp_load.h"
+#include "../tm/tm_load.h"
+#include "authorize.h"
+#include "authims_mod.h"
+#include "cxdx_mar.h"
+#include "../../lib/ims/useful_defs.h"
+
+MODULE_VERSION
+
+static void destroy(void);
+static int mod_init(void);
+
+static int auth_fixup(void** param, int param_no);
+static int challenge_fixup(void** param, int param_no);
+
+struct cdp_binds cdpb;
+
+/*! API structures */
+struct tm_binds tmb; /**< Structure with pointers to tm funcs 				*/
+
+extern auth_hash_slot_t *auth_data; /**< authentication vectors hast table 					*/
+
+int auth_data_hash_size = 1024; /**< the size of the hash table 							*/
+int auth_vector_timeout = 60; /**< timeout for a sent auth vector to expire in sec 		*/
+int auth_used_vector_timeout = 3600; /**< timeout for a used auth vector to expire in sec 		*/
+int max_nonce_reuse = 0; /**< how many times a nonce can be reused (provided nc is incremented)	*/
+int auth_data_timeout = 60; /**< timeout for a hash entry to expire when empty in sec 	*/
+int add_authinfo_hdr = 1; /**< should an Authentication-Info header be added on 200 OK responses? 	*/
+int av_request_at_once = 1; /**< how many auth vectors to request in a MAR 				*/
+int av_request_at_sync = 1; /**< how many auth vectors to request in a sync MAR 		*/
+char *registration_qop = "auth,auth-int"; /**< the qop options to put in the authorization challenges */
+str registration_qop_str = {0, 0}; /**< the qop options to put in the authorization challenges */
+static str s_qop_s = {", qop=\"", 7};
+static str s_qop_e = {"\"", 1};
+
+char* registration_default_algorithm = "AKAv1-MD5"; /**< default algorithm for registration (if none present)*/
+unsigned char registration_default_algorithm_type = 1; /**< fixed default algorithm for registration (if none present)	 */
+
+/* parameters storage */
+char* scscf_name = "sip:scscf.ims.smilecoms.com:6060"; /**< name of the S-CSCF */
+
+/* parameters storage */
+char* cxdx_dest_realm_s = "ims.smilecoms.com";
+str cxdx_dest_realm;
+
+//Only used if we want to force the Rx peer
+//Usually this is configured at a stack level and the first request uses realm routing
+char* cxdx_forced_peer_s = "";
+str cxdx_forced_peer;
+
+
+/* fixed parameter storage */
+str scscf_name_str; /**< fixed name of the S-CSCF 							*/
+
+/* used mainly in testing - load balancing with SIPP where we dont want to worry about auth */
+int ignore_failed_auth = 0;
+
+/*
+ * Exported functions
+ */
+static cmd_export_t cmds[] = {
+    {"ims_www_authenticate", (cmd_function) www_authenticate, 1, auth_fixup, 0, REQUEST_ROUTE},
+    {"ims_www_challenge", (cmd_function) www_challenge, 1, challenge_fixup, 0, REQUEST_ROUTE},
+    {"ims_proxy_authenticate", (cmd_function) proxy_authenticate, 1, auth_fixup, 0, REQUEST_ROUTE},
+    {"ims_proxy_challenge", (cmd_function) proxy_challenge, 1, auth_fixup, 0, REQUEST_ROUTE},
+    {"bind_auth_ims", (cmd_function) bind_auth_ims, 0, 0, 0, 0},
+    {0, 0, 0, 0, 0, 0}
+};
+
+/*
+ * Exported parameters
+ */
+static param_export_t params[] = {
+    {"name", STR_PARAM, &scscf_name},
+    {"auth_data_hash_size", INT_PARAM, &auth_data_hash_size},
+    {"auth_vector_timeout", INT_PARAM, &auth_vector_timeout},
+    {"auth_used_vector_timeout", INT_PARAM, &auth_used_vector_timeout},
+    {"auth_data_timeout", INT_PARAM, &auth_data_timeout},
+    {"max_nonce_reuse", INT_PARAM, &max_nonce_reuse},
+    {"add_authinfo_hdr", INT_PARAM, &add_authinfo_hdr},
+    {"av_request_at_once", INT_PARAM, &av_request_at_once},
+    {"av_request_at_sync", INT_PARAM, &av_request_at_sync},
+    {"registration_default_algorithm", STR_PARAM, &registration_default_algorithm},
+    {"registration_qop", STR_PARAM, &registration_qop},
+    {"ignore_failed_auth", INT_PARAM, &ignore_failed_auth},
+    {"cxdx_forced_peer", STR_PARAM, &cxdx_forced_peer_s},
+    {"cxdx_dest_realm", STR_PARAM, &cxdx_dest_realm_s},
+    {0, 0, 0}
+};
+
+stat_export_t mod_stats[] = {
+	{"mar_avg_response_time" ,  STAT_IS_FUNC, 	(stat_var**)get_avg_mar_response_time	},
+	{"mar_timeouts" ,  			0, 				(stat_var**)&stat_mar_timeouts  		},
+	{0,0,0}
+};
+
+/*
+ * Module interface
+ */
+struct module_exports exports = {
+    "auth_ims",
+    DEFAULT_DLFLAGS, /* dlopen flags */
+    cmds, /* Exported functions */
+    params, /* Exported parameters */
+    0, /* exported statistics */
+    0, /* exported MI functions */
+    0, /* exported pseudo-variables */
+    0, /* extra processes */
+    mod_init, /* module initialization function */
+    0, /* response function */
+    destroy, /* destroy function */
+    0 /* child initialization function */
+};
+
+static int mod_init(void) {
+    str algo;
+
+    /*get parameters */
+
+    cxdx_forced_peer.s = cxdx_forced_peer_s;
+    cxdx_forced_peer.len = strlen(cxdx_forced_peer_s);
+
+    cxdx_dest_realm.s = cxdx_dest_realm_s;
+    cxdx_dest_realm.len = strlen(cxdx_dest_realm_s);
+
+    scscf_name_str.s = scscf_name;
+    scscf_name_str.len = strlen(scscf_name);
+
+    algo.s = registration_default_algorithm;
+    algo.len = strlen(registration_default_algorithm);
+    registration_default_algorithm_type = get_algorithm_type(algo);
+
+#ifdef STATISTICS
+	/* register statistics */
+	if (register_module_stats( exports.name, mod_stats)!=0 ) {
+		LM_ERR("failed to register core statistics\n");
+		return -1;
+	}
+
+	if (!register_stats()){
+		LM_ERR("Unable to register statistics\n");
+		return -1;
+	}
+#endif
+
+    /* check the max_nonce_reuse param */
+    if (auth_used_vector_timeout < 0) {
+        LM_WARN("bad value for auth_used_vector_timeout parameter (=%d), must be positive. Fixed to 3600\n", auth_used_vector_timeout);
+        auth_used_vector_timeout = 3600;
+    }
+
+    /* check the max_nonce_reuse param */
+    if (max_nonce_reuse < 0) {
+        LM_WARN("bad value for max_nonce_reuse parameter (=%d), must be positive. Fixed to 0\n", max_nonce_reuse);
+        max_nonce_reuse = 0;
+    }
+
+    /* load the CDP API */
+    if (load_cdp_api(&cdpb) != 0) {
+        LM_ERR("can't load CDP API\n");
+        return -1;
+    }
+
+    /* load the TM API */
+    if (load_tm_api(&tmb) != 0) {
+        LM_ERR("can't load TM API\n");
+        return -1;
+    }
+
+    /* Init the authorization data storage */
+    if (!auth_data_init(auth_data_hash_size)) {
+        LM_ERR("Unable to init auth data\n");
+        return -1;
+    }
+
+    /* set default qop */
+    if (registration_qop && strlen(registration_qop) > 0) {
+        registration_qop_str.len = s_qop_s.len + strlen(registration_qop)
+                + s_qop_e.len;
+        registration_qop_str.s = pkg_malloc(registration_qop_str.len);
+        if (!registration_qop_str.s) {
+            LM_ERR("Error allocating %d bytes\n", registration_qop_str.len);
+            registration_qop_str.len = 0;
+            return 0;
+        }
+        registration_qop_str.len = 0;
+        STR_APPEND(registration_qop_str, s_qop_s);
+        memcpy(registration_qop_str.s + registration_qop_str.len,
+                registration_qop, strlen(registration_qop));
+        registration_qop_str.len += strlen(registration_qop);
+        STR_APPEND(registration_qop_str, s_qop_e);
+    } else {
+        registration_qop_str.len = 0;
+        registration_qop_str.s = 0;
+    }
+
+    /* Register the auth vector timer */
+    if (register_timer(reg_await_timer, auth_data, 10) < 0) {
+        LM_ERR("Unable to register auth vector timer\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static void destroy(void) {
+    auth_data_destroy();
+}
+
+/*
+ * Convert the char* parameters
+ */
+static int challenge_fixup(void** param, int param_no) {
+
+    if (strlen((char*) *param) <= 0) {
+        LM_ERR("empty parameter %d not allowed\n", param_no);
+        return -1;
+    }
+
+    if (param_no == 1) {
+        if (fixup_var_str_12(param, 1) == -1) {
+            LM_ERR("Erroring doing fixup on challenge");
+            return -1;
+        }
+        mar_param_t *ap;
+        ap = (mar_param_t*) pkg_malloc(sizeof (mar_param_t));
+        if (ap == NULL) {
+            LM_ERR("no more pkg\n");
+            return -1;
+        }
+        memset(ap, 0, sizeof (mar_param_t));
+        ap->paction = get_action_from_param(param, param_no);
+
+        ap->param = (char*) *param;
+
+        *param = (void*) ap;
+    }
+
+    return 0;
+}
+
+/*
+ * Convert the char* parameters
+ */
+static int auth_fixup(void** param, int param_no) {
+    if (strlen((char*) *param) <= 0) {
+        LM_ERR("empty parameter %d not allowed\n", param_no);
+        return -1;
+    }
+
+    if (param_no == 1) {
+        //return fixup_var_str_12(param, 1);
+        if (fixup_var_str_12(param, 1) == -1) {
+            LM_ERR("Erroring doing fixup on auth");
+            return -1;
+        }
+    }
+
+    return 0;
+}

+ 55 - 0
modules/auth_ims/authims_mod.h

@@ -0,0 +1,55 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * 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
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * 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
+ * 
+ */
+
+#ifndef AUTHIMS_MOD_H
+#define AUTHIMS_MOD_H
+
+#include "../../str.h"
+#include "../../lib/srdb1/db.h"
+#include "../../parser/msg_parser.h"
+
+#define MOD_NAME "auth_ims"
+
+#endif /* AUTHIMSMOD_H */

+ 1447 - 0
modules/auth_ims/authorize.c

@@ -0,0 +1,1447 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * 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
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * 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
+ * 
+ */
+
+#include <string.h>
+#include "../../ut.h"
+#include "../../str.h"
+#include "../../basex.h"
+#include "../../lib/srdb1/db.h"
+#include "../../lib/srdb1/db_ut.h"
+#include "../../dprint.h"
+#include "../../parser/digest/digest.h"
+#include "../../parser/hf.h"
+#include "../../parser/parser_f.h"
+#include "../../usr_avp.h"
+#include "../../mod_fix.h"
+#include "../../mem/mem.h"
+#include "../cdp/diameter.h"
+#include "../cdp/diameter_ims_code_result.h"
+
+#include "cxdx_mar.h"
+#include "cxdx_avp.h"
+
+
+#include "../../lib/ims/ims_getters.h"
+#include "../tm/tm_load.h"
+#include "api.h"
+#include "authims_mod.h"
+#include "authorize.h"
+#include "utils.h"
+#include "../../action.h" /* run_actions */
+
+extern unsigned char registration_default_algorithm_type; /**< fixed default algorithm for registration (if none present)	*/
+extern struct tm_binds tmb;
+extern struct cdp_binds cdpb;
+
+extern str registration_qop_str; /**< the qop options to put in the authorization challenges */
+extern int av_request_at_sync; /**< how many auth vectors to request in a sync MAR 		*/
+extern int av_request_at_once; /**< how many auth vectors to request in a MAR 				*/
+extern int auth_vector_timeout;
+extern int auth_data_timeout; /**< timeout for a hash entry to expire when empty in sec 	*/
+extern int auth_used_vector_timeout;
+extern int add_authinfo_hdr;
+extern int max_nonce_reuse;
+extern str scscf_name_str;
+extern int ignore_failed_auth;
+
+auth_hash_slot_t *auth_data; /**< Authentication vector hash table */
+extern int auth_data_hash_size; /**< authentication vector hash table size */
+
+
+static str empty_s = {0, 0};
+
+str S_WWW = {"WWW", 3};
+str S_Proxy = {"Proxy", 5};
+str S_Authorization_AKA = {"%.*s-Authenticate: Digest realm=\"%.*s\","
+    " nonce=\"%.*s\", algorithm=%.*s, ck=\"%.*s\", ik=\"%.*s\"%.*s\r\n", 107};
+str S_Authorization_MD5 = {"%.*s-Authenticate: Digest realm=\"%.*s\","
+    " nonce=\"%.*s\", algorithm=%.*s%.*s\r\n", 102};
+
+str algorithm_types[] = {
+    {"unknown", 7},
+    {"AKAv1-MD5", 9},
+    {"AKAv2-MD5", 9},
+    {"Early-IMS", 9},
+    {"MD5", 3},
+    {"CableLabs-Digest", 16},
+    {"3GPP-Digest", 11},
+    {"TISPAN-HTTP_DIGEST_MD5", 22},
+    {"NASS-Bundled", 12},
+    {0, 0}
+};
+
+str auth_scheme_types[] = {
+    {"unknown", 7},
+    {"Digest-AKAv1-MD5", 16},
+    {"Digest-AKAv2-MD5", 16},
+    {"Early-IMS-Security", 18},
+    {"Digest-MD5", 10},
+    {"Digest", 6},
+    {"SIP Digest", 10},
+    {"HTTP_DIGEST_MD5", 15},
+    {"NASS-Bundled", 12},
+    {0, 0}
+};
+
+/**
+ * Convert the SIP Algorithm to its type
+ * @param algorithm - the SIP Algorithm
+ * @returns the algorithm type
+ */
+unsigned char get_algorithm_type(str algorithm) {
+    int i;
+    for (i = 0; algorithm_types[i].len > 0; i++)
+        if (algorithm_types[i].len == algorithm.len
+                && strncasecmp(algorithm_types[i].s, algorithm.s, algorithm.len)
+                == 0)
+            return i;
+    return AUTH_UNKNOWN;
+}
+
+/**
+ * Convert the Diameter Authorization Scheme to its type
+ * @param scheme - the Diameter Authorization Scheme
+ * @returns the SIP Algorithm
+ */
+unsigned char get_auth_scheme_type(str scheme) {
+    int i;
+    for (i = 0; auth_scheme_types[i].len > 0; i++)
+        if (auth_scheme_types[i].len == scheme.len &&
+                strncasecmp(auth_scheme_types[i].s, scheme.s, scheme.len) == 0)
+            return i;
+    return AUTH_UNKNOWN;
+}
+
+static inline int get_ha1(struct username* _username, str* _domain,
+        const str* _table, char* _ha1, db1_res_t** res) {
+
+    return 0;
+}
+
+/*
+ * Authorize digest credentials
+ */
+static int digest_authenticate(struct sip_msg* msg, str *realm,
+        str *table, hdr_types_t hftype) {
+    return 0;
+}
+
+/**
+ * Starts the reg_await_timer for an authentication vector.
+ * @param av - the authentication vector
+ */
+inline void start_reg_await_timer(auth_vector *av) {
+    av->expires = get_ticks() + auth_vector_timeout;
+    av->status = AUTH_VECTOR_SENT;
+}
+
+/**
+ * Timer callback for reg await timers.
+ * Drops the auth vectors that have been sent and are expired
+ * Also drops the useless auth vectors - used and no longer needed
+ * @param ticks - what's the time
+ * @param param - a given parameter to be called with
+ */
+void reg_await_timer(unsigned int ticks, void* param) {
+    auth_userdata *aud, *aud_next;
+    auth_vector *av, *av_next;
+    int i;
+
+    LM_DBG("Looking for expired/useless at %d\n", ticks);
+    for (i = 0; i < auth_data_hash_size; i++) {
+        auth_data_lock(i);
+        aud = auth_data[i].head;
+        while (aud) {
+            LM_DBG("Slot %4d <%.*s>\n",
+                    aud->hash, aud->private_identity.len, aud->private_identity.s);
+            aud_next = aud->next;
+            av = aud->head;
+            while (av) {
+                LM_DBG(".. AV %4d - %d Exp %3d  %p\n",
+                        av->item_number, av->status, (int) av->expires, av);
+                av_next = av->next;
+                if (av->status == AUTH_VECTOR_USELESS ||
+                        ((av->status == AUTH_VECTOR_USED || av->status == AUTH_VECTOR_SENT) && av->expires < ticks)
+                        ) {
+                    LM_DBG("... dropping av %d - %d\n",
+                            av->item_number, av->status);
+                    if (av->prev) av->prev->next = av->next;
+                    else aud->head = av->next;
+                    if (av->next) av->next->prev = av->prev;
+                    else aud->tail = av->prev;
+                    free_auth_vector(av);
+                }
+                av = av_next;
+            }
+            if (!aud->head) {
+                if (aud->expires == 0) {
+                    LM_DBG("... started empty aud drop timer\n");
+                    aud->expires = ticks + auth_data_timeout;
+                } else
+                    if (aud->expires < ticks) {
+                    LM_DBG("... dropping aud \n");
+                    if (aud->prev) aud->prev->next = aud->next;
+                    else auth_data[i].head = aud->next;
+                    if (aud->next) aud->next->prev = aud->prev;
+                    else auth_data[i].tail = aud->prev;
+                    free_auth_userdata(aud);
+                }
+            } else aud->expires = 0;
+
+            aud = aud_next;
+        }
+        auth_data_unlock(i);
+    }
+    LM_DBG("[DONE] Looking for expired/useless at %d\n", ticks);
+}
+
+/*
+ * Authenticate using Proxy-Authorize header field
+ */
+
+/*
+int proxy_authenticate(struct sip_msg* _m, char* _realm, char* _table) {
+    str srealm;
+    str stable;
+
+    if (_table == NULL) {
+        LM_ERR("invalid table parameter\n");
+        return -1;
+    }
+
+    stable.s = _table;
+    stable.len = strlen(stable.s);
+
+    if (get_str_fparam(&srealm, _m, (fparam_t*) _realm) < 0) {
+        LM_ERR("failed to get realm value\n");
+        return -1; //AUTH_ERROR;
+    }
+
+    if (srealm.len == 0) {
+        LM_ERR("invalid realm parameter - empty value\n");
+        return -1; //AUTH_ERROR;
+    }
+    LM_DBG("realm value [%.*s]\n", srealm.len, srealm.s);
+
+    return digest_authenticate(_m, &srealm, &stable, HDR_PROXYAUTH_T);
+}
+ */
+int challenge(struct sip_msg* msg, char* str1, char* str2, int is_proxy_auth) {
+
+    str realm = {0, 0};
+    unsigned int aud_hash;
+    str private_identity, public_identity, auts = {0, 0}, nonce = {0, 0};
+    auth_vector *av = 0;
+    int algo_type;
+
+    saved_transaction_t* saved_t;
+    tm_cell_t *t = 0;
+    cfg_action_t* cfg_action;
+
+    mar_param_t* ap = (mar_param_t*) str1;
+    cfg_action = ap->paction->next;
+
+    if (get_str_fparam(&realm, msg, (fparam_t*) ap->param) < 0) {
+        LM_ERR("failed to get realm value\n");
+        return CSCF_RETURN_ERROR;
+    }
+
+    if (realm.len == 0) {
+        LM_ERR("invalid realm value - empty content\n");
+        return CSCF_RETURN_ERROR;
+    }
+
+    create_return_code(CSCF_RETURN_ERROR);
+
+    LM_DBG("Need to challenge for realm [%.*s]\n", realm.len, realm.s);
+
+    if (msg->first_line.type != SIP_REQUEST) {
+        LM_ERR("This message is not a request\n");
+        return CSCF_RETURN_ERROR;
+    }
+    if (!is_proxy_auth) {
+        LM_DBG("Checking if REGISTER is authorized for realm [%.*s]...\n", realm.len, realm.s);
+
+        /* First check the parameters */
+        if (msg->first_line.u.request.method.len != 8 ||
+                memcmp(msg->first_line.u.request.method.s, "REGISTER", 8) != 0) {
+            LM_ERR("This message is not a REGISTER request\n");
+            return CSCF_RETURN_ERROR;
+        }
+    }
+
+    /* get the private_identity */
+    private_identity = get_private_identity(msg, realm, is_proxy_auth);
+    if (!private_identity.len) {
+        LM_ERR("No private identity specified (Authorization: username)\n");
+        stateful_request_reply(msg, 403, MSG_403_NO_PRIVATE);
+        return CSCF_RETURN_BREAK;
+    }
+    /* get the public_identity */
+    public_identity = get_public_identity(msg);
+    if (!public_identity.len) {
+        LM_ERR("No public identity specified (To:)\n");
+        stateful_request_reply(msg, 403, MSG_403_NO_PUBLIC);
+        return CSCF_RETURN_BREAK;
+    }
+
+    algo_type = registration_default_algorithm_type;
+
+    /* check if it is a synchronization request */
+    //TODO this is MAR syncing - have removed it currently - TOD maybe put back in
+//    auts = ims_get_auts(msg, realm, is_proxy_auth);
+//    if (auts.len) {
+//        LM_DBG("IMS Auth Synchronization requested <%.*s>\n", auts.len, auts.s);
+//
+//        nonce = ims_get_nonce(msg, realm);
+//        if (nonce.len == 0) {
+//            LM_DBG("Nonce not found (Authorization: nonce)\n");
+//            stateful_request_reply(msg, 403, MSG_403_NO_NONCE);
+//            return CSCF_RETURN_BREAK;
+//        }
+//        av = get_auth_vector(private_identity, public_identity, AUTH_VECTOR_USED, &nonce, &aud_hash);
+//        if (!av)
+//            av = get_auth_vector(private_identity, public_identity, AUTH_VECTOR_SENT, &nonce, &aud_hash);
+//
+//        if (!av) {
+//            LM_ERR("Nonce not regonized as sent, no sync!\n");
+//            auts.len = 0;
+//            auts.s = 0;
+//        } else {
+//            av->status = AUTH_VECTOR_USELESS;
+//            auth_data_unlock(aud_hash);
+//            av = 0;
+//        }
+//
+//        //RICHARD REMOVED REALM - this is diameter realm set in cxdx not SIP domain
+//        // if synchronization - force MAR - if MAR ok, old avs will be droped
+//        multimedia_auth_request(msg, public_identity, private_identity, av_request_at_sync,
+//                auth_scheme_types[algo_type], nonce, auts, scscf_name_str);
+//    }
+
+    //RICHARD changed this
+    //Previous approach sent MAR, got MAA then put auth vectors into queue
+    //Then we try and get that auth vector out the queue (it might be used by someone else so we loop)
+    //new approach
+    //we do MAR get MAA (asynchronously) get auth vector use it to pack the vector etc.
+    //set it to sent and set an expires on it
+    //then add it to the queue!
+
+    /* loop because some other process might steal the auth_vector that we just retrieved */
+    //while (!(av = get_auth_vector(private_identity, public_identity, AUTH_VECTOR_UNUSED, 0, &aud_hash))) {
+
+    if ((av = get_auth_vector(private_identity, public_identity, AUTH_VECTOR_UNUSED, 0, &aud_hash))) {
+        if (!av) {
+            LM_ERR("Error retrieving an auth vector\n");
+            return CSCF_RETURN_ERROR;
+        }
+
+        if (!pack_challenge(msg, realm, av, is_proxy_auth)) {
+            stateful_request_reply(msg, 500, MSG_500_PACK_AV);
+            auth_data_unlock(aud_hash);
+            return CSCF_RETURN_ERROR;
+        }
+
+        start_reg_await_timer(av); //start the timer to remove stale or unused Auth Vectors
+        if (is_proxy_auth) {
+            stateful_request_reply(msg, 407, MSG_407_CHALLENGE);
+        } else {
+            stateful_request_reply(msg, 401, MSG_401_CHALLENGE);
+        }
+        auth_data_unlock(aud_hash);
+
+    } else {
+
+        //before we send lets suspend the transaction
+        t = tmb.t_gett();
+        if (t == NULL || t == T_UNDEFINED) {
+            if (tmb.t_newtran(msg) < 0) {
+                LM_ERR("cannot create the transaction for MAR async\n");
+                stateful_request_reply(msg, 480, MSG_480_DIAMETER_ERROR);
+                return CSCF_RETURN_BREAK;
+            }
+            t = tmb.t_gett();
+            if (t == NULL || t == T_UNDEFINED) {
+                LM_ERR("cannot lookup the transaction\n");
+                stateful_request_reply(msg, 480, MSG_480_DIAMETER_ERROR);
+                return CSCF_RETURN_BREAK;
+            }
+        }
+
+        saved_t = shm_malloc(sizeof (saved_transaction_t));
+        if (!saved_t) {
+            LM_ERR("no more memory trying to save transaction state\n");
+            return CSCF_RETURN_ERROR;
+
+        }
+        memset(saved_t, 0, sizeof (saved_transaction_t));
+        saved_t->act = cfg_action;
+
+        saved_t->realm.s = (char*) shm_malloc(realm.len + 1);
+        if (!saved_t->realm.s) {
+            LM_ERR("no more memory trying to save transaction state : callid\n");
+            shm_free(saved_t);
+            return CSCF_RETURN_ERROR;
+        }
+        memset(saved_t->realm.s, 0, realm.len + 1);
+        memcpy(saved_t->realm.s, realm.s, realm.len);
+        saved_t->realm.len = realm.len;
+
+
+        saved_t->is_proxy_auth = is_proxy_auth;
+
+        LM_DBG("Suspending SIP TM transaction\n");
+        if (tmb.t_suspend(msg, &saved_t->tindex, &saved_t->tlabel) < 0) {
+            LM_ERR("failed to suspend the TM processing\n");
+            free_saved_transaction_data(saved_t);
+
+            stateful_request_reply(msg, 480, MSG_480_DIAMETER_ERROR);
+            return CSCF_RETURN_BREAK;
+        }
+
+        if (multimedia_auth_request(msg, public_identity, private_identity, av_request_at_once,
+                auth_scheme_types[algo_type], nonce, auts, scscf_name_str, saved_t)!=0) {
+            LM_ERR("ERR:I_MAR: Error sending MAR or MAR time-out\n");
+            tmb.t_cancel_suspend(saved_t->tindex, saved_t->tlabel);
+            free_saved_transaction_data(saved_t);
+            stateful_request_reply(msg, 480, MSG_480_DIAMETER_ERROR);
+            return CSCF_RETURN_BREAK;
+        }
+    }
+    return CSCF_RETURN_BREAK;
+}
+
+int www_challenge(struct sip_msg* msg, char* str1, char* str2) {
+    return challenge(msg, str1, str2, 0);
+}
+
+int proxy_challenge(struct sip_msg* msg, char* str1, char* str2) {
+    return challenge(msg, str1, str2, 1);
+}
+
+/**
+ * Replies to a REGISTER and also adds the need headers
+ * Path and Service-Route are added.
+ * @param msg - the SIP message to operator on
+ * @param code - Reason Code for the response
+ * @param text - Reason Phrase for the response
+ * @returns #CSCF_RETURN_TRUE on success or #CSCF_RETURN_FALSE if not added
+ */
+int stateful_request_reply(struct sip_msg *msg, int code, char *text) {
+    unsigned int hash, label;
+    struct hdr_field *h;
+    str t = {0, 0};
+    if (parse_headers(msg, HDR_EOH_F, 0) < 0) {
+        LM_ERR("Error parsing headers\n");
+        return -1;
+    }
+    h = msg->headers;
+    while (h) {
+        if (h->name.len == 4 &&
+                strncasecmp(h->name.s, "Path", 4) == 0) {
+            t.s = h->name.s;
+            t.len = h->len;
+            ims_add_header_rpl(msg, &(t));
+        }
+        h = h->next;
+    }
+
+    /*if (code==200){
+            ims_add_header_rpl(msg,&scscf_service_route);
+    }*/ //TODO: need to get the service route from somewhere - registrar?
+
+
+    if (tmb.t_get_trans_ident(msg, &hash, &label) < 0) {
+        if (tmb.t_newtran(msg) < 0)
+            LM_INFO("Failed creating SIP transaction\n");
+    }
+    return tmb.t_reply(msg, code, text);
+
+}
+
+/**
+ * Replies to a REGISTER and also adds the need headers
+ * Path and Service-Route are added.
+ * @param msg - the SIP message to operator on
+ * @param code - Reason Code for the response
+ * @param text - Reason Phrase for the response
+ * @returns #CSCF_RETURN_TRUE on success or #CSCF_RETURN_FALSE if not added
+ */
+
+int stateful_request_reply_async(struct cell* t_cell, struct sip_msg *msg, int code, char *text) {
+    struct hdr_field *h;
+    str t = {0, 0};
+    if (parse_headers(msg, HDR_EOH_F, 0) < 0) {
+        LM_ERR("Error parsing headers\n");
+        return -1;
+    }
+    h = msg->headers;
+    while (h) {
+        if (h->name.len == 4 &&
+                strncasecmp(h->name.s, "Path", 4) == 0) {
+            t.s = h->name.s;
+            t.len = h->len;
+            ims_add_header_rpl(msg, &(t));
+        }
+        h = h->next;
+    }
+
+    return tmb.t_reply_trans(t_cell, msg, code, text);
+
+}
+
+int authenticate(struct sip_msg* msg, char* _realm, char* str2, int is_proxy_auth) {
+    int ret = -1; //CSCF_RETURN_FALSE;
+    unsigned int aud_hash = 0;
+    str realm;
+    str private_identity, public_identity;
+    str nonce, response16, nc, cnonce, qop_str = {0, 0}, body, *next_nonce = &empty_s;
+    enum qop_type qop = QOP_UNSPEC;
+    str uri = {0, 0};
+    HASHHEX expected, ha1, hbody, rspauth;
+    int expected_len = 32;
+    int expires = 0;
+    auth_vector *av = 0;
+    uint32_t nc_parsed = 0; /* the numerical representation of nc */
+
+    ret = AUTH_ERROR;
+
+    if (get_str_fparam(&realm, msg, (fparam_t*) _realm) < 0) {
+        LM_ERR("failed to get realm value\n");
+        return AUTH_NO_CREDENTIALS;
+    }
+
+    if (realm.len == 0) {
+        LM_ERR("invalid realm value - empty content\n");
+        return AUTH_NO_CREDENTIALS;
+    }
+
+    if (msg->first_line.type != SIP_REQUEST) {
+        LM_ERR("This message is not a request\n");
+        ret = AUTH_ERROR;
+        goto end;
+    }
+    if (!is_proxy_auth) {
+        LM_DBG("Checking if REGISTER is authorized for realm [%.*s]...\n", realm.len, realm.s);
+
+        /* First check the parameters */
+        if (msg->first_line.u.request.method.len != 8 ||
+                memcmp(msg->first_line.u.request.method.s, "REGISTER", 8) != 0) {
+            LM_ERR("This message is not a REGISTER request\n");
+            ret = AUTH_ERROR;
+            goto end;
+        }
+    }
+
+    if (!realm.len) {
+        LM_ERR("No realm found\n");
+        return 0; //CSCF_RETURN_BREAK;
+    }
+
+    private_identity = get_private_identity(msg, realm, is_proxy_auth);
+    if (!private_identity.len) {
+        LM_ERR("private identity missing\n");
+        return AUTH_NO_CREDENTIALS;
+    }
+
+    public_identity = get_public_identity(msg);
+    if (!public_identity.len) {
+        LM_ERR("public identity missing\n");
+        return AUTH_NO_CREDENTIALS;
+    }
+
+    if (!get_nonce_response(msg, realm, &nonce, &response16, &qop, &qop_str, &nc, &cnonce, &uri, is_proxy_auth) ||
+            !nonce.len || !response16.len) {
+        LM_DBG("Nonce or response missing: nonce len [%i], response16 len[%i]\n", nonce.len, response16.len);
+        return AUTH_ERROR;
+    }
+
+    if (qop == QOP_AUTHINT) {
+        body = ims_get_body(msg);
+        calc_H(&body, hbody);
+    }
+
+    /* first, look for an already used vector (if nonce reuse is enabled) */
+    if (max_nonce_reuse > 0) {
+        LM_DBG("look for an already used vector for %.*s\n",
+                private_identity.len, private_identity.s);
+        av = get_auth_vector(private_identity, public_identity, AUTH_VECTOR_USED, &nonce, &aud_hash);
+    }
+
+    if (!av) {
+        /* if none found, or nonce reuse is disabled, look for a fresh vector
+         * We should also drop every other used vector at this point
+         * (there souldn't be more than one) */
+
+        auth_userdata *aud;
+        auth_vector *av_it;
+        aud = get_auth_userdata(private_identity, public_identity);
+        if (aud) {
+            av_it = aud->head;
+            while (av_it) {
+                if (av_it->status == AUTH_VECTOR_USED) {
+                    LM_DBG("vector %p is marked for deletion\n", av_it);
+                    av_it->status = AUTH_VECTOR_USELESS;
+                }
+                av_it = av_it->next;
+            }
+            auth_data_unlock(aud->hash);
+        }
+
+        LM_DBG("look for a fresh vector for %.*s\n",
+                private_identity.len, private_identity.s);
+        av = get_auth_vector(private_identity, public_identity, AUTH_VECTOR_SENT, &nonce, &aud_hash);
+    }
+
+    LM_INFO("uri=%.*s nonce=%.*s response=%.*s qop=%.*s nc=%.*s cnonce=%.*s hbody=%.*s\n",
+            uri.len, uri.s,
+            nonce.len, nonce.s,
+            response16.len, response16.s,
+            qop_str.len, qop_str.s,
+            nc.len, nc.s,
+            cnonce.len, cnonce.s,
+            32, hbody);
+
+    if (!av) {
+        LM_ERR("no matching auth vector found - maybe timer expired\n");
+
+        if (ignore_failed_auth) {
+            LM_WARN("NB: Ignoring all failed auth - check your config if you dont expect this\n");
+            ret = AUTH_OK;
+        }
+
+        goto end;
+    }
+
+    if (qop != QOP_UNSPEC) {
+        /* if QOP is sent, nc must be specified */
+        /* the expected nc is the last used one plus 1 */
+        int p;
+        for (p = 0; p < 8; ++p) { /* nc is 8LHEX (RFC 2617 §3.2.2) */
+            nc_parsed = (nc_parsed << 4) | UNHEX((int) nc.s[p]);
+        }
+        LM_DBG("nc is %08x, expected: %08x\n",
+                nc_parsed, av->use_nb + 1);
+        if (nc_parsed <= av->use_nb) { /* nc is lower than expected */
+            ret = AUTH_NONCE_REUSED;
+            av->status = AUTH_VECTOR_USELESS; /* invalidate this vector if any mistake/error occurs */
+            goto cleanup;
+        } else if (nc_parsed > av->use_nb + 1) { /* nc is bigger than expected */
+            ret = AUTH_ERROR;
+            av->status = AUTH_VECTOR_USELESS;
+            goto cleanup;
+        }
+    }
+
+    switch (av->type) {
+        case AUTH_AKAV1_MD5:
+        case AUTH_AKAV2_MD5:
+        case AUTH_MD5:
+            calc_HA1(HA_MD5, &private_identity, &realm, &(av->authorization), &(av->authenticate), &cnonce, ha1);
+            calc_response(ha1, &(av->authenticate),
+                    &nc,
+                    &cnonce,
+                    &qop_str,
+                    qop == QOP_AUTHINT,
+                    &msg->first_line.u.request.method, &uri, hbody, expected);
+            LM_INFO("UE said: %.*s and we  expect %.*s ha1 %.*s (%.*s)\n",
+                    response16.len, response16.s, /*av->authorization.len,av->authorization.s,*/32, expected, 32, ha1, msg->first_line.u.request.method.len, msg->first_line.u.request.method.s);
+            break;
+        default:
+            LM_ERR("algorithm %.*s is not handled.\n",
+                    algorithm_types[av->type].len, algorithm_types[av->type].s);
+            ret = AUTH_ERROR;
+            goto cleanup; /* release aud before returning */
+    }
+
+    expires = cscf_get_max_expires(msg, 0);
+
+    if (response16.len == expected_len && strncasecmp(response16.s, expected, response16.len) == 0) {
+        if (max_nonce_reuse > 0 && av->status == AUTH_VECTOR_SENT) {
+            /* first use of a reusable vector */
+            /* set the vector's new timeout */
+            LM_DBG("vector %p now expires in %d seconds\n", av, auth_used_vector_timeout);
+            av->expires = get_ticks() + auth_used_vector_timeout;
+        }
+        av->use_nb++;
+        LM_DBG("vector %p successfully used %d time(s)\n", av, av->use_nb);
+
+        if (av->use_nb == max_nonce_reuse + 1) {
+            LM_DBG("vector %p isn't fresh anymore, recycle it with a new nonce\n", av);
+
+            int i;
+            char y[NONCE_LEN];
+            for (i = 0; i < NONCE_LEN; i++)
+                y[i] = (unsigned char) ((int) (256.0 * rand() / (RAND_MAX + 1.0)));
+
+            if (unlikely((av->authenticate.len < 2 * NONCE_LEN))) {
+                if (av->authenticate.s) {
+                    shm_free(av->authenticate.s);
+                }
+                av->authenticate.len = 2 * NONCE_LEN;
+                av->authenticate.s = shm_malloc(av->authenticate.len);
+            }
+
+            if (!av->authenticate.s) {
+                LM_ERR("new_auth_vector: failed allocating %d bytes!\n", av->authenticate.len);
+                av->authenticate.len = 0;
+                goto cleanup;
+            }
+
+            av->authenticate.len = bin_to_base16(y, NONCE_LEN, av->authenticate.s);
+
+            next_nonce = &(av->authenticate);
+            av->status = AUTH_VECTOR_USED;
+            av->use_nb = 0;
+            av->expires = get_ticks() + auth_used_vector_timeout; /* reset the timer */
+
+        } else if (expires == 0) { /* de-registration */
+            LM_DBG("de-registration, vector %p isn't needed anymore\n", av);
+            av->status = AUTH_VECTOR_USELESS;
+        } else {
+            av->status = AUTH_VECTOR_USED;
+            /* nextnonce is the current nonce */
+            next_nonce = &nonce;
+        }
+        ret = AUTH_OK;
+
+        if (add_authinfo_hdr && expires != 0 /* don't add auth. info if de-registation */) {
+            /* calculate rspauth */
+            calc_response(ha1, &nonce,
+                    &nc,
+                    &cnonce,
+                    &qop_str,
+                    qop == QOP_AUTHINT,
+                    0, &uri, hbody, rspauth);
+
+            add_authinfo_resp_hdr(msg, *next_nonce, qop_str, rspauth, cnonce, nc);
+        }
+
+
+    } else {
+    	char authorise[200];
+    	char authenticate_bin[200];
+    	char authenticate_hex[200];
+    	memset(authorise, 0, 200);
+    	memset(authenticate_bin, 0, 200);
+    	memset(authenticate_hex, 0, 200);
+
+    	int authorise_len = bin_to_base16(av->authorization.s, av->authorization.len, authorise);
+    	int authenticate_len = base64_to_bin(av->authenticate.s, av->authenticate.len, authenticate_bin);
+    	int authenticate_hex_len = bin_to_base16(authenticate_bin, authenticate_len, authenticate_hex);
+    	av->status = AUTH_VECTOR_USELESS; /* first mistake, you're out! (but maybe it's synchronization) */
+        LM_DBG("UE said: %.*s, but we expect %.*s : authenticate(b64) is [%.*s], authenticate(hex) is [%.*s], authorise is [%d] [%.*s]\n",
+                response16.len, response16.s,
+                32, expected,
+                av->authenticate.len, av->authenticate.s,
+                authenticate_hex_len,authenticate_hex,
+                authorise_len,
+                authorise_len, authorise);
+        ret = AUTH_INVALID_PASSWORD;
+    }
+
+    if (ignore_failed_auth) {
+        LM_WARN("NB: Ignoring all failed auth - check your config if you dont expect this\n");
+        ret = AUTH_OK;
+    }
+
+cleanup:
+    auth_data_unlock(aud_hash);
+end:
+    return ret;
+}
+
+/*
+ * Authenticate using WWW-Authorize header field
+ */
+int www_authenticate(struct sip_msg* msg, char* _realm, char* str2) {
+    return authenticate(msg, _realm, str2, 0);
+}
+
+/*
+ * Authenticate using WWW-Authorize header field
+ */
+int proxy_authenticate(struct sip_msg* msg, char* _realm, char* str2) {
+    return authenticate(msg, _realm, str2, 1);
+}
+
+/**
+ * @brief bind functions to AUTH_IMS API structure
+ */
+int bind_auth_ims(auth_ims_api_t * api) {
+    if (!api) {
+        ERR("Invalid parameter value\n");
+        return -1;
+    }
+    api->digest_authenticate = digest_authenticate;
+
+    return 0;
+}
+
+/**
+ * Retrieve an authentication vector.
+ * \note returns with a lock, so unlock it when done
+ * @param private_identity - the private identity
+ * @param public_identity - the public identity
+ * @param status - the status of the authentication vector
+ * @param nonce - the nonce in the auth vector
+ * @param hash - the hash to unlock when done
+ * @returns the auth_vector* if found or NULL if not
+ */
+auth_vector * get_auth_vector(str private_identity, str public_identity, int status, str *nonce, unsigned int *hash) {
+    auth_userdata *aud;
+    auth_vector *av;
+    aud = get_auth_userdata(private_identity, public_identity);
+    if (!aud) {
+        LM_ERR("no auth userdata\n");
+        goto error;
+    }
+
+    av = aud->head;
+    while (av) {
+        LM_DBG("looping through AV status is %d and were looking for %d\n", av->status, status);
+        if (av->status == status && (nonce == 0 || (nonce->len == av->authenticate.len && memcmp(nonce->s, av->authenticate.s, nonce->len) == 0))) {
+            LM_DBG("Found result\n");
+            *hash = aud->hash;
+            return av;
+        }
+        av = av->next;
+    }
+
+error:
+    if (aud) auth_data_unlock(aud->hash);
+    return 0;
+}
+
+/**
+ * Locks the required slot of the auth_data.
+ * @param hash - the index of the slot
+ */
+inline void auth_data_lock(unsigned int hash) {
+    lock_get(auth_data[(hash)].lock);
+}
+
+/**
+ * UnLocks the required slot of the auth_data
+ * @param hash - the index of the slot
+ */
+inline void auth_data_unlock(unsigned int hash) {
+    lock_release(auth_data[(hash)].lock);
+}
+
+/**
+ * Initializes the Authorization Data structures.
+ * @param size - size of the hash table
+ * @returns 1 on success or 0 on error
+ */
+int auth_data_init(int size) {
+    int i;
+    auth_data = shm_malloc(sizeof (auth_hash_slot_t) * size);
+    if (!auth_data) {
+        LM_ERR("error allocating mem\n");
+        return 0;
+    }
+    memset(auth_data, 0, sizeof (auth_hash_slot_t) * size);
+    auth_data_hash_size = size;
+    for (i = 0; i < size; i++) {
+        auth_data[i].lock = lock_alloc();
+        lock_init(auth_data[i].lock);
+    }
+    return 1;
+}
+
+/**
+ * Destroy the Authorization Data structures */
+void auth_data_destroy() {
+    int i;
+    auth_userdata *aud, *next;
+    for (i = 0; i < auth_data_hash_size; i++) {
+        auth_data_lock(i);
+        lock_destroy(auth_data[i].lock);
+        lock_dealloc(auth_data[i].lock);
+        aud = auth_data[i].head;
+        while (aud) {
+            next = aud->next;
+            free_auth_userdata(aud);
+            aud = next;
+        }
+    }
+    if (auth_data) shm_free(auth_data);
+}
+
+/**
+ * Create new authorization vector
+ * @param item_number - number to index it in the vectors list
+ * @param auth_scheme - Diameter Authorization Scheme
+ * @param authenticate - the challenge
+ * @param authorization - the expected response
+ * @param ck - the cypher key
+ * @param ik - the integrity key
+ * @returns the new auth_vector* or NULL on error
+ */
+auth_vector * new_auth_vector(int item_number, str auth_scheme, str authenticate,
+        str authorization, str ck, str ik) {
+    auth_vector *x = 0;
+    x = shm_malloc(sizeof (auth_vector));
+    if (!x) {
+        LM_ERR("error allocating mem\n");
+        goto done;
+    }
+    memset(x, 0, sizeof (auth_vector));
+    x->item_number = item_number;
+    x->type = get_auth_scheme_type(auth_scheme);
+    switch (x->type) {
+        case AUTH_AKAV1_MD5:
+        case AUTH_AKAV2_MD5:
+            /* AKA */
+            x->authenticate.len = authenticate.len * 4 / 3 + 4;
+            x->authenticate.s = shm_malloc(x->authenticate.len);
+            if (!x->authenticate.s) {
+                LM_ERR("error allocating mem\n");
+                goto done;
+            }
+            x->authenticate.len = bin_to_base64(authenticate.s, authenticate.len,
+                    x->authenticate.s);
+
+            x->authorization.len = authorization.len;
+            x->authorization.s = shm_malloc(x->authorization.len);
+            if (!x->authorization.s) {
+                LM_ERR("error allocating mem\n");
+                goto done;
+            }
+            memcpy(x->authorization.s, authorization.s, authorization.len);
+            x->ck.len = ck.len;
+            x->ck.s = shm_malloc(ck.len);
+            if (!x->ck.s) {
+                LM_ERR("error allocating mem\n");
+                goto done;
+            }
+            memcpy(x->ck.s, ck.s, ck.len);
+
+            x->ik.len = ik.len;
+            x->ik.s = shm_malloc(ik.len);
+            if (!x->ik.s) {
+                LM_ERR("error allocating mem\n");
+                goto done;
+            }
+            memcpy(x->ik.s, ik.s, ik.len);
+            break;
+
+        case AUTH_MD5:
+            /* MD5 */
+            x->authenticate.len = authenticate.len * 2;
+            x->authenticate.s = shm_malloc(x->authenticate.len);
+            if (!x->authenticate.s) {
+                LM_ERR("new_auth_vector: error allocating mem\n");
+                goto done;
+            }
+            x->authenticate.len = bin_to_base16(authenticate.s, authenticate.len,
+                    x->authenticate.s);
+
+            x->authorization.len = authorization.len;
+            x->authorization.s = shm_malloc(x->authorization.len);
+            if (!x->authorization.s) {
+                LM_ERR("new_auth_vector: error allocating mem\n");
+                goto done;
+            }
+            memcpy(x->authorization.s, authorization.s, authorization.len);
+            x->authorization.len = authorization.len;
+            break;
+        case AUTH_DIGEST:
+        case AUTH_SIP_DIGEST:
+        {
+            int i;
+            char y[NONCE_LEN];
+            for (i = 0; i < NONCE_LEN; i++)
+                y[i] = (unsigned char) ((int) (256.0 * rand() / (RAND_MAX + 1.0)));
+            x->authenticate.len = 2 * NONCE_LEN;
+            x->authenticate.s = shm_malloc(x->authenticate.len);
+            if (!x->authenticate.s) {
+                LM_ERR("new_auth_vector: failed allocating %d bytes!\n", x->authenticate.len);
+                x->authenticate.len = 0;
+                goto done;
+            }
+            x->authenticate.len = bin_to_base16(y, NONCE_LEN, x->authenticate.s);
+        }
+
+            x->authorization.len = authorization.len;
+            x->authorization.s = shm_malloc(x->authorization.len);
+            if (!x->authorization.s) {
+                LM_ERR("new_auth_vector: error allocating mem\n");
+                x->authorization.len = 0;
+                goto done;
+            }
+            memcpy(x->authorization.s, authorization.s, authorization.len);
+            x->authorization.len = authorization.len;
+
+            break;
+        case AUTH_HTTP_DIGEST_MD5:
+            x->authenticate.len = authenticate.len;
+            x->authenticate.s = shm_malloc(x->authenticate.len);
+            if (!x->authenticate.s) {
+                LM_ERR("new_auth_vector: error allocating mem\n");
+                x->authenticate.len = 0;
+                goto done;
+            }
+            memcpy(x->authenticate.s, authenticate.s, authenticate.len);
+
+            x->authorization.len = authorization.len;
+            x->authorization.s = shm_malloc(x->authorization.len);
+            if (!x->authorization.s) {
+                LM_ERR("new_auth_vector: error allocating mem\n");
+                x->authorization.len = 0;
+                goto done;
+            }
+            memcpy(x->authorization.s, authorization.s, authorization.len);
+            break;
+        case AUTH_EARLY_IMS:
+            /* early IMS */
+            x->authenticate.len = 0;
+            x->authenticate.s = 0;
+            x->authorization.len = authorization.len;
+            x->authorization.s = shm_malloc(x->authorization.len);
+            if (!x->authorization.s) {
+                LM_ERR("new_auth_vector: error allocating mem\n");
+                goto done;
+            }
+            memcpy(x->authorization.s, authorization.s, authorization.len);
+            x->authorization.len = authorization.len;
+            break;
+        case AUTH_NASS_BUNDLED:
+            /* NASS-Bundled */
+            x->authenticate.len = 0;
+            x->authenticate.s = 0;
+            x->authorization.len = authorization.len;
+            x->authorization.s = shm_malloc(x->authorization.len);
+            if (!x->authorization.s) {
+                LM_ERR("new_auth_vector: error allocating mem\n");
+                goto done;
+            }
+            memcpy(x->authorization.s, authorization.s, authorization.len);
+            x->authorization.len = authorization.len;
+            break;
+
+        default:
+            /* all else */
+            x->authenticate.len = 0;
+            x->authenticate.s = 0;
+
+    }
+
+    x->use_nb = 0;
+
+    x->next = 0;
+    x->prev = 0;
+    x->status = AUTH_VECTOR_UNUSED;
+    x->expires = 0;
+
+done:
+    return x;
+}
+
+/**
+ * Frees the memory taken by a authentication vector
+ * @param av - the vector to be freed
+ */
+void free_auth_vector(auth_vector * av) {
+    if (av) {
+        if (av->authenticate.s) shm_free(av->authenticate.s);
+        if (av->authorization.s) shm_free(av->authorization.s);
+        if (av->ck.s) shm_free(av->ck.s);
+        if (av->ik.s) shm_free(av->ik.s);
+        shm_free(av);
+    }
+}
+
+/**
+ * Creates a new Authorization Userdata structure.
+ * @param private_identity - the private identity to attach to
+ * @param public_identity - the public identity to attach to
+ * @returns the new auth_userdata* on success or NULL on error
+ */
+auth_userdata * new_auth_userdata(str private_identity, str public_identity) {
+    auth_userdata *x = 0;
+
+    x = shm_malloc(sizeof (auth_userdata));
+    if (!x) {
+        LM_ERR("error allocating mem\n");
+        goto done;
+    }
+
+    x->private_identity.len = private_identity.len;
+    x->private_identity.s = shm_malloc(private_identity.len);
+    if (!x) {
+        LM_ERR("error allocating mem\n");
+        goto done;
+    }
+    memcpy(x->private_identity.s, private_identity.s, private_identity.len);
+
+    x->public_identity.len = public_identity.len;
+    x->public_identity.s = shm_malloc(public_identity.len);
+    if (!x) {
+        LM_ERR("error allocating mem\n");
+        goto done;
+    }
+    memcpy(x->public_identity.s, public_identity.s, public_identity.len);
+
+    x->head = 0;
+    x->tail = 0;
+
+    x->next = 0;
+    x->prev = 0;
+
+done:
+    return x;
+}
+
+/**
+ * Deallocates the auth_userdata.
+ * @param aud - the auth_userdata to be deallocated
+ */
+void free_auth_userdata(auth_userdata * aud) {
+    auth_vector *av, *next;
+    if (aud) {
+        if (aud->private_identity.s) shm_free(aud->private_identity.s);
+        if (aud->public_identity.s) shm_free(aud->public_identity.s);
+        av = aud->head;
+        while (av) {
+            next = av->next;
+            free_auth_vector(av);
+            av = next;
+        }
+        shm_free(aud);
+    }
+}
+
+/**
+ * Computes a hash based on the private and public identities
+ * @param private_identity - the private identity
+ * @param public_identity - the public identity
+ * @returns the hash % Auth_data->size
+ */
+inline unsigned int get_hash_auth(str private_identity, str public_identity) {
+
+#define h_inc h+=v^(v>>3)
+    char* p;
+    register unsigned v;
+    register unsigned h;
+
+    h = 0;
+    for (p = private_identity.s; p <= (private_identity.s + private_identity.len - 4); p += 4) {
+        v = (*p << 24)+(p[1] << 16)+(p[2] << 8) + p[3];
+        h_inc;
+    }
+    v = 0;
+    for (; p < (private_identity.s + private_identity.len); p++) {
+        v <<= 8;
+        v += *p;
+    }
+    h_inc;
+    for (p = public_identity.s; p <= (public_identity.s + public_identity.len - 4); p += 4) {
+        v = (*p << 24)+(p[1] << 16)+(p[2] << 8) + p[3];
+        h_inc;
+    }
+    v = 0;
+    for (; p < (public_identity.s + public_identity.len); p++) {
+        v <<= 8;
+        v += *p;
+    }
+
+    h = ((h)+(h >> 11))+((h >> 13)+(h >> 23));
+    return (h) % auth_data_hash_size;
+#undef h_inc
+}
+
+/**
+ * Retrieve the auth_userdata for a user.
+ * \note you will return with lock on the hash slot, so release it!
+ * @param private_identity - the private identity
+ * @param public_identity - the public identity
+ * @returns the auth_userdata* found or newly created on success, NULL on error
+ */
+auth_userdata * get_auth_userdata(str private_identity, str public_identity) {
+
+    unsigned int hash = 0;
+    auth_userdata *aud = 0;
+
+    hash = get_hash_auth(private_identity, public_identity);
+    auth_data_lock(hash);
+    aud = auth_data[hash].head;
+    while (aud) {
+        if (aud->private_identity.len == private_identity.len &&
+                aud->public_identity.len == public_identity.len &&
+                memcmp(aud->private_identity.s, private_identity.s, private_identity.len) == 0 &&
+                memcmp(aud->public_identity.s, public_identity.s, public_identity.len) == 0) {
+            return aud;
+        }
+        aud = aud->next;
+    }
+    /* if we get here, there is no auth_userdata for this user */
+    aud = new_auth_userdata(private_identity, public_identity);
+    if (!aud) {
+        auth_data_unlock(hash);
+        return 0;
+    }
+
+    aud->prev = auth_data[hash].tail;
+    aud->next = 0;
+    aud->hash = hash;
+
+    if (!auth_data[hash].head) auth_data[hash].head = aud;
+    if (auth_data[hash].tail) auth_data[hash].tail->next = aud;
+    auth_data[hash].tail = aud;
+
+    return aud;
+}
+
+/**
+ * Sends a Multimedia-Authentication-Response to retrieve some authentication vectors and maybe synchronize.
+ * Must respond with a SIP reply every time it returns 0
+ * @param msg - the SIP REGISTER message
+ * @param public_identity - the public identity
+ * @param private_identity - the private identity
+ * @param count - how many vectors to request
+ * @param algorithm - which algorithm to request
+ * @param nonce - the challenge that will be sent
+ * @param auts - the AKA synchronization or empty string if not a synchronization
+ * @param server_name - the S-CSCF name to be saved on the HSS
+ * @returns 1 on success, 0 on failure
+ */
+int multimedia_auth_request(struct sip_msg *msg, str public_identity, str private_identity,
+        int count, str auth_scheme, str nonce, str auts, str servername, saved_transaction_t* transaction_data) {
+
+
+    str authorization = {0, 0};
+    int result = -1;
+
+    //TODO this is MAR syncing - have removed it currently - TOD maybe put back in
+    //int is_sync = 0;
+//    if (auts.len) {
+//        authorization.s = pkg_malloc(nonce.len * 3 / 4 + auts.len * 3 / 4 + 8);
+//        if (!authorization.s) goto done;
+//        authorization.len = base64_to_bin(nonce.s, nonce.len, authorization.s);
+//        authorization.len = RAND_LEN;
+//        authorization.len += base64_to_bin(auts.s, auts.len, authorization.s + authorization.len);
+//        is_sync = 1;
+//    }
+
+
+    LM_DBG("Sending MAR\n");
+    result = cxdx_send_mar(msg, public_identity, private_identity, count, auth_scheme, authorization, servername, transaction_data);
+    if (authorization.s) pkg_free(authorization.s);
+
+    //TODO this is MAR syncing - have removed it currently - TOD maybe put back in
+    //if (is_sync)
+    //    drop_auth_userdata(private_identity, public_identity);
+
+    return result;
+}
+
+/**
+ * Adds the WWW-Authenticate header for challenge, based on the authentication vector.
+ * @param msg - SIP message to add the header to
+ * @param realm - the realm
+ * @param av - the authentication vector
+ * @returns 1 on success, 0 on error
+ */
+int pack_challenge(struct sip_msg *msg, str realm, auth_vector *av, int is_proxy_auth) {
+    str x = {0, 0};
+    char ck[32], ik[32];
+    int ck_len, ik_len;
+    str *auth_prefix = is_proxy_auth ? &S_Proxy : &S_WWW;
+    switch (av->type) {
+        case AUTH_AKAV1_MD5:
+        case AUTH_AKAV2_MD5:
+            /* AKA */
+            ck_len = bin_to_base16(av->ck.s, 16, ck);
+            ik_len = bin_to_base16(av->ik.s, 16, ik);
+            x.len = S_Authorization_AKA.len + auth_prefix->len + realm.len + av->authenticate.len
+                    + algorithm_types[av->type].len + ck_len + ik_len
+                    + registration_qop_str.len;
+            x.s = pkg_malloc(x.len);
+            if (!x.s) {
+                LM_ERR("Error allocating %d bytes\n",
+                        x.len);
+                goto error;
+            }
+            sprintf(x.s, S_Authorization_AKA.s, auth_prefix->len, auth_prefix->s, realm.len, realm.s,
+                    av->authenticate.len, av->authenticate.s,
+                    algorithm_types[av->type].len, algorithm_types[av->type].s,
+                    ck_len, ck, ik_len, ik, registration_qop_str.len,
+                    registration_qop_str.s);
+            x.len = strlen(x.s);
+            break;
+        case AUTH_HTTP_DIGEST_MD5:
+            /* ETSI HTTP_DIGEST MD5 */
+            /* this one continues into the next one */
+        case AUTH_DIGEST:
+            /* Cable-Labs MD5 */
+            /* this one continues into the next one */
+        case AUTH_SIP_DIGEST:
+            /* 3GPP MD5 */
+            /* this one continues into the next one */
+        case AUTH_MD5:
+            /* FOKUS MD5 */
+            x.len = S_Authorization_MD5.len + auth_prefix->len + realm.len + av->authenticate.len
+                    + algorithm_types[av->type].len + registration_qop_str.len;
+            x.s = pkg_malloc(x.len);
+            if (!x.s) {
+                LM_ERR("pack_challenge: Error allocating %d bytes\n", x.len);
+                goto error;
+            }
+            sprintf(x.s, S_Authorization_MD5.s, auth_prefix->len, auth_prefix->s, realm.len, realm.s,
+                    av->authenticate.len, av->authenticate.s,
+                    algorithm_types[AUTH_MD5].len, algorithm_types[AUTH_MD5].s,
+                    registration_qop_str.len, registration_qop_str.s);
+            x.len = strlen(x.s);
+            break;
+
+        default:
+            LM_CRIT("not implemented for algorithm %.*s\n",
+                    algorithm_types[av->type].len, algorithm_types[av->type].s);
+            goto error;
+    }
+
+    if (ims_add_header_rpl(msg, &x)) {
+        pkg_free(x.s);
+        return 1;
+    }
+
+error:
+    if (x.s)
+        pkg_free(x.s);
+
+    return 0;
+}
+
+/**
+ * Adds the Authentication-Info header for, based on the credentials sent by a successful REGISTER.
+ * @param msg - SIP message to add the header to
+ * @returns 1 on success, 0 on error
+ */
+int add_authinfo_resp_hdr(struct sip_msg *msg, str nextnonce, str qop, HASHHEX rspauth, str cnonce, str nc) {
+
+    str authinfo_hdr;
+    static const char authinfo_fmt[] = "Authentication-Info: "
+            "nextnonce=\"%.*s\","
+            "qop=%.*s,"
+            "rspauth=\"%.*s\","
+            "cnonce=\"%.*s\","
+            "nc=%.*s\r\n";
+
+    authinfo_hdr.len = sizeof (authinfo_fmt) + nextnonce.len + qop.len + HASHHEXLEN + cnonce.len + nc.len - 20 /* format string parameters */ - 1 /* trailing \0 */;
+    authinfo_hdr.s = pkg_malloc(authinfo_hdr.len + 1);
+
+    if (!authinfo_hdr.s) {
+        LM_ERR("add_authinfo_resp_hdr: Error allocating %d bytes\n", authinfo_hdr.len);
+        goto error;
+    }
+    snprintf(authinfo_hdr.s, authinfo_hdr.len + 1, authinfo_fmt,
+            nextnonce.len, nextnonce.s,
+            qop.len, qop.s,
+            HASHHEXLEN, rspauth,
+            cnonce.len, cnonce.s,
+            nc.len, nc.s);
+    LM_DBG("authinfo hdr built: %.*s", authinfo_hdr.len, authinfo_hdr.s);
+    if (ims_add_header_rpl(msg, &authinfo_hdr)) {
+        LM_DBG("authinfo hdr added");
+        pkg_free(authinfo_hdr.s);
+        return 1;
+    }
+error:
+    if (authinfo_hdr.s) pkg_free(authinfo_hdr.s);
+
+    return 0;
+}
+
+/**
+ * Add an authentication vector to the authentication userdata storage.
+ * @param private_identity - the private identity
+ * @param public_identity - the public identity
+ * @param av - the authentication vector
+ * @returns 1 on success or 0 on error
+ */
+int add_auth_vector(str private_identity, str public_identity, auth_vector * av) {
+    auth_userdata *aud;
+    aud = get_auth_userdata(private_identity, public_identity);
+    if (!aud) goto error;
+
+    av->prev = aud->tail;
+    av->next = 0;
+
+    if (!aud->head) aud->head = av;
+    if (aud->tail) aud->tail->next = av;
+    aud->tail = av;
+
+    auth_data_unlock(aud->hash);
+    return 1;
+error:
+
+    return 0;
+}
+
+/**
+ * Declares all auth vectors as useless when we do a synchronization
+ * @param private_identity - the private identity
+ * @param public_identity - the public identity
+ * @returns 1 on sucess, 0 on error
+ */
+int drop_auth_userdata(str private_identity, str public_identity) {
+    auth_userdata *aud;
+    auth_vector *av;
+    aud = get_auth_userdata(private_identity, public_identity);
+    if (!aud) goto error;
+
+    av = aud->head;
+    while (av) {
+        av->status = AUTH_VECTOR_USELESS;
+        av = av->next;
+    }
+    auth_data_unlock(aud->hash);
+    return 1;
+error:
+    if (aud) auth_data_unlock(aud->hash);
+    return 0;
+}

+ 190 - 0
modules/auth_ims/authorize.h

@@ -0,0 +1,190 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * 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
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * 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
+ * 
+ */
+
+
+#ifndef AUTHORIZE_H
+#define AUTHORIZE_H
+
+
+#include "../../parser/msg_parser.h"
+#include "api.h"
+#include "conversion.h"
+#include "rfc2617.h"
+#include "sip_messages.h"
+#include "cxdx_mar.h"
+
+
+#define NONCE_LEN 16
+#define RAND_LEN  16
+
+
+
+enum authorization_types {
+	AUTH_UNKNOWN			= 0,
+/* 3GPP */	
+	AUTH_AKAV1_MD5			= 1,
+	AUTH_AKAV2_MD5			= 2,
+	AUTH_EARLY_IMS			= 3,
+/* FOKUS */
+	AUTH_MD5				= 4,
+/* CableLabs */	
+	AUTH_DIGEST				= 5,
+/* 3GPP */	
+	AUTH_SIP_DIGEST			= 6,
+/* TISPAN */	
+	AUTH_HTTP_DIGEST_MD5	= 7,	
+	AUTH_NASS_BUNDLED		= 8
+};
+
+/** Enumeration for the Authorization Vector status */
+enum auth_vector_status {
+	AUTH_VECTOR_UNUSED = 0,
+	AUTH_VECTOR_SENT = 1,
+	AUTH_VECTOR_USELESS = 2,	/**< invalidated, marked for deletion 		*/
+	AUTH_VECTOR_USED = 3		/**< the vector has been successfully used	*/
+} ;
+
+/** Authorization Vector storage structure */
+typedef struct _auth_vector {
+	int item_number;	/**< index of the auth vector		*/
+	unsigned char type;	/**< type of authentication vector 	*/
+	str authenticate;	/**< challenge (rand|autn in AKA)	*/
+	str authorization; 	/**< expected response				*/
+	str ck;				/**< Cypher Key						*/
+	str ik;				/**< Integrity Key					*/
+	time_t expires;/**< expires in (after it is sent)	*/
+	uint32_t use_nb;		/**< number of use (nonce count)*/
+	
+	enum auth_vector_status status;/**< current status		*/
+	struct _auth_vector *next;/**< next av in the list		*/
+	struct _auth_vector *prev;/**< previous av in the list	*/
+} auth_vector;
+
+/** Set of auth_vectors used by a private id */
+typedef struct _auth_userdata{
+	unsigned int hash;		/**< hash of the auth data		*/
+	str private_identity;	/**< authorization username		*/
+	str public_identity;	/**< public identity linked to	*/
+	time_t expires;			/**< expires in					*/
+
+	auth_vector *head;		/**< first auth vector in list	*/
+	auth_vector *tail;		/**< last auth vector in list	*/
+	
+	struct _auth_userdata *next;/**< next element in list	*/
+	struct _auth_userdata *prev;/**< previous element in list*/
+} auth_userdata;
+
+/** Authorization user data hash slot */
+typedef struct {
+	auth_userdata *head;				/**< first in the slot			*/ 
+	auth_userdata *tail;				/**< last in the slot			*/
+	gen_lock_t *lock;			/**< slot lock 							*/	
+} auth_hash_slot_t;
+
+
+
+int auth_db_init(const str* db_url);
+int auth_db_bind(const str* db_url);
+void auth_db_close(void);
+
+/*
+ * Authorize using Proxy-Authorization header field
+ */
+int proxy_authenticate(struct sip_msg* _msg, char* _realm, char* _table);
+int proxy_challenge(struct sip_msg* msg, char* _realm, char* str2);
+
+/*
+ * Authorize using WWW-Authorization header field
+ */
+int www_authenticate(struct sip_msg* _msg, char* _realm, char* _table);
+int www_challenge(struct sip_msg* msg, char* _realm, char* str2);
+
+
+/*
+ * Bind to AUTH_IMS API
+ */
+int bind_auth_ims(auth_ims_api_t* api);
+
+auth_vector* get_auth_vector(str private_identity,str public_identity,int status,str *nonce,unsigned int *hash);
+/*
+ * Storage of authentication vectors
+ */
+
+inline void auth_data_lock(unsigned int hash);
+inline void auth_data_unlock(unsigned int hash);
+ 
+int auth_data_init(int size);
+
+void auth_data_destroy();
+
+auth_vector *new_auth_vector(int item_number,str auth_scheme,str authenticate,
+			str authorization,str ck,str ik);
+void free_auth_vector(auth_vector *av);
+
+auth_userdata *new_auth_userdata(str private_identity,str public_identity);
+void free_auth_userdata(auth_userdata *aud);					
+
+inline unsigned int get_hash_auth(str private_identity,str public_identity);
+
+int add_auth_vector(str private_identity,str public_identity,auth_vector *av);
+auth_vector* get_auth_vector(str private_identity,str public_identity,int status,str *nonce,unsigned int *hash);
+
+int drop_auth_userdata(str private_identity,str public_identity);
+auth_userdata* get_auth_userdata(str private_identity,str public_identity);
+
+int stateful_request_reply(struct sip_msg *msg, int code,  char *text);
+int stateful_request_reply_async(struct cell* t, struct sip_msg *msg, int code, char *text);
+
+int multimedia_auth_request(struct sip_msg *msg, str public_identity, str private_identity,
+					int count,str auth_scheme,str nonce,str auts,str servername, saved_transaction_t* transaction_data);
+int pack_challenge(struct sip_msg *msg,str realm,auth_vector *av, int is_proxy_auth);
+int add_authinfo_resp_hdr(struct sip_msg *msg, str nextnonce, str qop, HASHHEX rspauth, str cnonce, str nc);
+
+inline void start_reg_await_timer(auth_vector *av);
+void reg_await_timer(unsigned int ticks, void* param);
+
+unsigned char get_algorithm_type(str algorithm);
+
+#endif /* AUTHORIZE_H */

+ 44 - 0
modules/auth_ims/blurb

@@ -0,0 +1,44 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * 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
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * 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
+ * 
+ */

+ 261 - 0
modules/auth_ims/conversion.c

@@ -0,0 +1,261 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * 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
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * 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
+ * 
+ */
+ 
+/**
+ * \file
+ * 
+ * Serving-CSCF - Conversion between base16, base64 and base256
+ * 
+ *  \author Dragos Vingarzan vingarzan -at- fokus dot fraunhofer dot de
+ * 
+ */
+ 
+/** base16 char constants */ 
+char *hexchars="0123456789abcdef";
+/**
+ * Converts a binary encoded value to its base16 representation.
+ * @param from - buffer containing the input data
+ * @param len - the size of from
+ * @param to - the output buffer  !!! must have at least len*2 allocated memory 
+ * @returns the written length 
+ */
+int bin_to_base16(char *from,int len, char *to)
+{
+	int i,j;
+	for(i=0,j=0;i<len;i++,j+=2){
+		to[j] = hexchars[(((unsigned char)from[i]) >>4 )&0x0F];
+		to[j+1] = hexchars[(((unsigned char)from[i]))&0x0F];
+	}	
+	return 2*len;
+}
+
+/** from base16 char to int */
+#define HEX_DIGIT(x) \
+	((x>='0'&&x<='9')?x-'0':((x>='a'&&x<='f')?x-'a'+10:((x>='A'&&x<='F')?x-'A'+10:0)))
+/**
+ * Converts a hex encoded value to its binary value
+ * @param from - buffer containing the input data
+ * @param len - the size of from
+ * @param to - the output buffer  !!! must have at least len/2 allocated memory 
+ * @returns the written length 
+ */
+int base16_to_bin(char *from,int len, char *to)
+{
+	int i,j;
+	for(i=0,j=0;j<len;i++,j+=2){
+		to[i] = (unsigned char) ( HEX_DIGIT(from[j])<<4 | HEX_DIGIT(from[j+1]));
+	}	
+	return i;
+}
+
+/**
+ * Convert on character from base64 encoding to integer.
+ *"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ * @param x - characted to convert
+ * @returns the int value or -1 if terminal character ('=')
+ */ 
+static inline int base64_val(char x)\
+{
+	switch(x){
+		case '=': return -1;
+		case 'A': return 0;
+		case 'B': return 1;
+		case 'C': return 2;
+		case 'D': return 3;
+		case 'E': return 4;
+		case 'F': return 5;
+		case 'G': return 6;
+		case 'H': return 7;
+		case 'I': return 8;
+		case 'J': return 9;
+		case 'K': return 10;
+		case 'L': return 11;
+		case 'M': return 12;
+		case 'N': return 13;
+		case 'O': return 14;
+		case 'P': return 15;
+		case 'Q': return 16;
+		case 'R': return 17;
+		case 'S': return 18;
+		case 'T': return 19;
+		case 'U': return 20;
+		case 'V': return 21;
+		case 'W': return 22;
+		case 'X': return 23;
+		case 'Y': return 24;
+		case 'Z': return 25;
+		case 'a': return 26;
+		case 'b': return 27;
+		case 'c': return 28;
+		case 'd': return 29;
+		case 'e': return 30;
+		case 'f': return 31;
+		case 'g': return 32;
+		case 'h': return 33;
+		case 'i': return 34;
+		case 'j': return 35;
+		case 'k': return 36;
+		case 'l': return 37;
+		case 'm': return 38;
+		case 'n': return 39;
+		case 'o': return 40;
+		case 'p': return 41;
+		case 'q': return 42;
+		case 'r': return 43;
+		case 's': return 44;
+		case 't': return 45;
+		case 'u': return 46;
+		case 'v': return 47;
+		case 'w': return 48;
+		case 'x': return 49;
+		case 'y': return 50;
+		case 'z': return 51;
+		case '0': return 52;
+		case '1': return 53;
+		case '2': return 54;
+		case '3': return 55;
+		case '4': return 56;
+		case '5': return 57;
+		case '6': return 58;
+		case '7': return 59;
+		case '8': return 60;
+		case '9': return 61;
+		case '+': return 62;
+		case '/': return 63;
+	}
+	return 0;
+}
+
+/**
+ * Convert a string encoded in base64 to binary value.
+ * @param from - buffer containing the input data
+ * @param from_len - the size of from
+ * @param to - the output buffer  !!! must have at least len*2 allocated memory 
+ * @returns the written length 
+ */
+int base64_to_bin(char *from,int from_len, char *to)
+{
+	int i,j,x1,x2,x3,x4;
+
+	for(i=0,j=0;i<from_len;i+=4){
+		x1=base64_val(from[i]);
+		x2=base64_val(from[i+1]);
+		x3=base64_val(from[i+2]);
+		x4=base64_val(from[i+3]);
+		to[j++]=(x1<<2) | ((x2 & 0x30)>>4);
+		if (x3==-1) break;
+		to[j++]=((x2 & 0x0F)<<4) | ((x3 & 0x3C)>>2);
+		if (x4==-1) break;
+		to[j++]=((x3 & 0x03)<<6) | (x4 & 0x3F);
+	}
+	return j;
+}
+
+/** base64 characters constant */
+char base64[64]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+/**
+ * Convert a binary string to base64 encoding.
+ * @param src - the source buffer
+ * @param src_len - length of the source buffer
+ * @param ptr - the destination buffer - must be allocated to at least src_len/3*4+4 
+ * @returns the length of the resulted buffer 
+ */
+int bin_to_base64(char *src,int src_len,char* ptr)
+{
+	int i,k;
+	int triplets,rest;
+	char *s;
+	s=ptr;
+
+	triplets = src_len/3;
+	rest = src_len%3;
+	for(i=0;i<triplets*3;i+=3){
+		k = (((unsigned char) src[i])&0xFC)>>2;
+		*ptr=base64[k];ptr++;
+
+		k = (((unsigned char) src[i])&0x03)<<4;
+		k |=(((unsigned char) src[i+1])&0xF0)>>4;
+		*ptr=base64[k];ptr++;
+
+		k = (((unsigned char) src[i+1])&0x0F)<<2;
+		k |=(((unsigned char) src[i+2])&0xC0)>>6;
+		*ptr=base64[k];ptr++;
+
+		k = (((unsigned char) src[i+2])&0x3F);
+		*ptr=base64[k];ptr++;
+	}
+	i=triplets*3;
+	switch(rest){
+		case 0:
+			break;
+		case 1:
+			k = (((unsigned char) src[i])&0xFC)>>2;
+			*ptr=base64[k];ptr++;
+
+			k = (((unsigned char) src[i])&0x03)<<4;
+			*ptr=base64[k];ptr++;
+
+			*ptr='=';ptr++;
+
+			*ptr='=';ptr++;
+			break;
+		case 2:
+			k = (((unsigned char) src[i])&0xFC)>>2;
+			*ptr=base64[k];ptr++;
+
+			k = (((unsigned char) src[i])&0x03)<<4;
+			k |=(((unsigned char) src[i+1])&0xF0)>>4;
+			*ptr=base64[k];ptr++;
+
+			k = (((unsigned char) src[i+1])&0x0F)<<2;
+			*ptr=base64[k];ptr++;
+
+			*ptr='=';ptr++;
+			break;
+	}
+
+	return ptr-s;
+}
+

+ 64 - 0
modules/auth_ims/conversion.h

@@ -0,0 +1,64 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * 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
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * 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
+ * 
+ */
+ 
+/**
+ * \file
+ * 
+ * Serving-CSCF - Conversion between base16, base64 and base256
+ * 
+ *  \author Dragos Vingarzan vingarzan -at- fokus dot fraunhofer dot de
+ * 
+ */
+ 
+#ifndef PIS_CSCF_CONVERSION_H_
+#define PIS_CSCF_CONVERSION_H_
+
+int bin_to_base16(char *from,int len, char *to);
+int base16_to_bin(char *from,int len, char *to);
+
+int bin_to_base64(char *src,int src_len,char* ptr);
+int base64_to_bin(char *src,int src_len,char* ptr);
+
+#endif //PIS_CSCF_CONVERSION_H_

+ 1048 - 0
modules/auth_ims/cxdx_avp.c

@@ -0,0 +1,1048 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * 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
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * 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
+ * 
+ */
+
+
+
+#include "../cdp/cdp_load.h"
+#include "../../modules/tm/tm_load.h"
+#include "cxdx_avp.h"
+
+
+
+static str s_empty = {0, 0};
+
+/**
+ * Create and add an AVP to a Diameter message.
+ * @param m - Diameter message to add to 
+ * @param d - the payload data
+ * @param len - length of the payload data
+ * @param avp_code - the code of the AVP
+ * @param flags - flags for the AVP
+ * @param vendorid - the value of the vendor id or 0 if none
+ * @param data_do - what to do with the data when done
+ * @param func - the name of the calling function, for debugging purposes
+ * @returns 1 on success or 0 on failure
+ */
+static inline int cxdx_add_avp(AAAMessage *m,char *d,int len,int avp_code,
+	int flags,int vendorid,int data_do,const char *func)
+{
+	AAA_AVP *avp;
+	if (vendorid!=0) flags |= AAA_AVP_FLAG_VENDOR_SPECIFIC;
+	avp = cdpb.AAACreateAVP(avp_code,flags,vendorid,d,len,data_do);
+	if (!avp) {
+		LM_ERR("%s: Failed creating avp\n",func);
+		return 0;
+	}
+	if (cdpb.AAAAddAVPToMessage(m,avp,m->avpList.tail)!=AAA_ERR_SUCCESS) {
+		LM_ERR("%s: Failed adding avp to message\n",func);
+		cdpb.AAAFreeAVP(&avp);
+		return 0;
+	}
+	return 1;
+}
+
+/**
+ * Create and add an AVP to a list of AVPs.
+ * @param list - the AVP list to add to 
+ * @param d - the payload data
+ * @param len - length of the payload data
+ * @param avp_code - the code of the AVP
+ * @param flags - flags for the AVP
+ * @param vendorid - the value of the vendor id or 0 if none
+ * @param data_do - what to do with the data when done
+ * @param func - the name of the calling function, for debugging purposes
+ * @returns 1 on success or 0 on failure
+ */
+static inline int cxdx_add_avp_list(AAA_AVP_LIST *list,char *d,int len,int avp_code,
+	int flags,int vendorid,int data_do,const char *func)
+{
+	AAA_AVP *avp;
+	if (vendorid!=0) flags |= AAA_AVP_FLAG_VENDOR_SPECIFIC;
+	avp = cdpb.AAACreateAVP(avp_code,flags,vendorid,d,len,data_do);
+	if (!avp) {
+		LM_ERR("%s: Failed creating avp\n",func);
+		return 0;
+	}
+	if (list->tail) {
+		avp->prev=list->tail;
+		avp->next=0;	
+		list->tail->next = avp;
+		list->tail=avp;
+	} else {
+		list->head = avp;
+		list->tail = avp;
+		avp->next=0;
+		avp->prev=0;
+	}
+	
+	return 1;
+}
+
+/**
+ * Returns the value of a certain AVP from a Diameter message.
+ * @param m - Diameter message to look into
+ * @param avp_code - the code to search for
+ * @param vendorid - the value of the vendor id to look for or 0 if none
+ * @param func - the name of the calling function, for debugging purposes
+ * @returns the str with the payload on success or an empty string on failure
+ */
+static inline str cxdx_get_avp(AAAMessage *msg,int avp_code,int vendor_id,
+							const char *func)
+{
+	AAA_AVP *avp;
+	str r={0,0};
+	
+	avp = cdpb.AAAFindMatchingAVP(msg,0,avp_code,vendor_id,0);
+	if (avp==0){
+		LM_INFO("%s: Failed finding avp\n",func);
+		return r;
+	}
+	else 
+		return avp->data;
+}
+
+/**
+ * Creates and adds a Destination-Realm AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_destination_realm(AAAMessage *msg,str data)
+{
+	return 
+	cxdx_add_avp(msg,data.s,data.len,
+		AVP_Destination_Realm,
+		AAA_AVP_FLAG_MANDATORY,
+		0,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+}
+
+
+/**
+ * Creates and adds a Vendor-Specifig-Application-ID AVP.
+ * @param msg - the Diameter message to add to.
+ * @param vendor_id - the value of the vendor_id,
+ * @param auth_id - the authorization application id
+ * @param acct_id - the accounting application id
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_vendor_specific_appid(AAAMessage *msg,unsigned int vendor_id,
+	unsigned int auth_id,unsigned int acct_id)
+{
+	AAA_AVP_LIST list;
+	str group;
+	char x[4];
+
+	list.head=0;list.tail=0;
+		
+	set_4bytes(x,vendor_id);
+	cxdx_add_avp_list(&list,
+		x,4,
+		AVP_Vendor_Id,
+		AAA_AVP_FLAG_MANDATORY,
+		0,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+
+	if (auth_id) {
+		set_4bytes(x,auth_id);
+		cxdx_add_avp_list(&list,
+			x,4,
+			AVP_Auth_Application_Id,
+			AAA_AVP_FLAG_MANDATORY,
+			0,
+			AVP_DUPLICATE_DATA,
+			__FUNCTION__);
+	}
+	if (acct_id) {
+		set_4bytes(x,acct_id);
+		cxdx_add_avp_list(&list,
+			x,4,
+			AVP_Acct_Application_Id,
+			AAA_AVP_FLAG_MANDATORY,
+			0,
+			AVP_DUPLICATE_DATA,
+			__FUNCTION__);
+	}	
+	
+	group = cdpb.AAAGroupAVPS(list);
+	
+	cdpb.AAAFreeAVPList(&list);
+	
+	return 
+	cxdx_add_avp(msg,group.s,group.len,
+		AVP_Vendor_Specific_Application_Id,
+		AAA_AVP_FLAG_MANDATORY,
+		0,
+		AVP_FREE_DATA,
+		__FUNCTION__);
+}
+
+/**
+ * Creates and adds a Auth-Session-State AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_auth_session_state(AAAMessage *msg,unsigned int data)
+{
+	char x[4];
+	set_4bytes(x,data);
+	return 
+	cxdx_add_avp(msg,x,4,
+		AVP_Auth_Session_State,
+		AAA_AVP_FLAG_MANDATORY,
+		0,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+}
+
+/**
+ * Creates and adds a User-Name AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_user_name(AAAMessage *msg,str data)
+{
+	return 
+	cxdx_add_avp(msg,data.s,data.len,
+		AVP_User_Name,
+		AAA_AVP_FLAG_MANDATORY,
+		0,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+}
+
+/**
+ * Creates and adds a Public Identity AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_public_identity(AAAMessage *msg,str data)
+{
+	return 
+	cxdx_add_avp(msg,data.s,data.len,
+		AVP_IMS_Public_Identity,
+		AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+		IMS_vendor_id_3GPP,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+}
+
+/**
+ * Creates and adds a Visited-Network-ID AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_visited_network_id(AAAMessage *msg,str data)
+{
+	return 
+	cxdx_add_avp(msg,data.s,data.len,
+		AVP_IMS_Visited_Network_Identifier,
+		AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+		IMS_vendor_id_3GPP,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+}
+
+/**
+ * Creates and adds a UAR-Flags AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_UAR_flags(AAAMessage *msg, unsigned int sos_reg)
+{
+
+	char x[4];
+	/* optional AVP*/
+	if(!sos_reg)
+		return 1;
+	
+	set_4bytes(x, AVP_IMS_UAR_Flags_Emergency_Registration);
+	return 
+	cxdx_add_avp(msg,x,4,
+		AVP_IMS_UAR_Flags,
+		AAA_AVP_FLAG_VENDOR_SPECIFIC,
+		IMS_vendor_id_3GPP,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+
+}
+/**
+ * Creates and adds a Authorization-Type AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_authorization_type(AAAMessage *msg,unsigned int data)
+{
+	char x[4];
+	set_4bytes(x,data);
+	return 
+	cxdx_add_avp(msg,x,4,
+		AVP_IMS_User_Authorization_Type,
+		AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+		IMS_vendor_id_3GPP,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+}
+
+/**
+ * Returns the Result-Code AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the AVP payload on success or an empty string on error
+ */
+inline int cxdx_get_result_code(AAAMessage *msg, int *data)
+{
+	str s;
+	s = cxdx_get_avp(msg,
+		AVP_Result_Code,
+		0,
+		__FUNCTION__);
+	if (!s.s) return 0;
+	*data = get_4bytes(s.s);
+	return 1;
+}
+
+/**
+ * Returns the Experimental-Result-Code AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the AVP payload on success or an empty string on error
+ */
+inline int cxdx_get_experimental_result_code(AAAMessage *msg, int *data)
+{
+	AAA_AVP_LIST list;
+	AAA_AVP *avp;
+	str grp;
+	grp = cxdx_get_avp(msg,
+		AVP_IMS_Experimental_Result,
+		0,
+		__FUNCTION__);
+	if (!grp.s) return 0;
+
+	list = cdpb.AAAUngroupAVPS(grp);
+	
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_IMS_Experimental_Result_Code,0,0);
+	if (!avp||!avp->data.s) {
+		cdpb.AAAFreeAVPList(&list);
+		return 0;
+	}
+
+	*data = get_4bytes(avp->data.s);
+	cdpb.AAAFreeAVPList(&list);
+
+	return 1;
+}
+
+/**
+ * Returns the Server-Name AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the AVP payload on success or an empty string on error
+ */
+inline str cxdx_get_server_name(AAAMessage *msg)
+{	
+	return cxdx_get_avp(msg,
+		AVP_IMS_Server_Name,
+		IMS_vendor_id_3GPP,
+		__FUNCTION__);
+}
+
+/**
+ * Returns the Capabilities from the grouped AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @param m - array to be filled with the retrieved mandatory capabilities
+ * @param m_cnt - size of the array above to be filled
+ * @param o - array to be filled with the retrieved optional capabilities
+ * @param o_cnt - size of the array above to be filled
+ * @returns 1 on success 0 on fail
+ */
+inline int cxdx_get_capabilities(AAAMessage *msg,int **m,int *m_cnt,int **o,int *o_cnt,
+	str **p,int *p_cnt)
+{
+	AAA_AVP_LIST list;
+	AAA_AVP *avp;
+	str grp;
+	grp = cxdx_get_avp(msg,
+		AVP_IMS_Server_Capabilities,
+		IMS_vendor_id_3GPP,
+		__FUNCTION__);
+	if (!grp.s) return 0;
+
+	list = cdpb.AAAUngroupAVPS(grp);
+	
+	avp = list.head;
+	*m_cnt=0;
+	*o_cnt=0;
+	*p_cnt=0;
+	while(avp){
+		if (avp->code == AVP_IMS_Mandatory_Capability) (*m_cnt)++;
+		if (avp->code == AVP_IMS_Optional_Capability) (*o_cnt)++;		
+		if (avp->code == AVP_IMS_Server_Name) (*p_cnt)++;
+		avp = avp->next;
+	}
+	avp = list.head;
+	*m=shm_malloc(sizeof(int)*(*m_cnt));
+	if (!*m){
+		LM_ERR("cannot allocated %lx bytes of shm.\n",
+			sizeof(int)*(*m_cnt));
+		goto error;
+	}
+	*o=shm_malloc(sizeof(int)*(*o_cnt));
+	if (!*o){
+		LM_ERR("cannot allocated %lx bytes of shm.\n",
+			sizeof(int)*(*o_cnt));
+		goto error;
+	}
+	*p=shm_malloc(sizeof(str)*(*p_cnt));
+	if (!*p){
+		LM_ERR("cannot allocated %lx bytes of shm.\n",
+			sizeof(str)*(*p_cnt));
+		goto error;
+	}
+	
+	*m_cnt=0;
+	*o_cnt=0;
+	*p_cnt=0;
+	while(avp){
+		if (avp->code == AVP_IMS_Mandatory_Capability) 
+			(*m)[(*m_cnt)++]=get_4bytes(avp->data.s);
+		if (avp->code == AVP_IMS_Optional_Capability)		
+			(*o)[(*o_cnt)++]=get_4bytes(avp->data.s);
+		if (avp->code == AVP_IMS_Server_Name)		
+			(*p)[(*p_cnt)++]=avp->data;
+		avp = avp->next;
+	}
+	cdpb.AAAFreeAVPList(&list);
+	return 1;
+	
+error:
+	cdpb.AAAFreeAVPList(&list);
+	if (*m) shm_free(*m);	
+	if (*o) shm_free(*o);	
+	if (*p) shm_free(*p);
+	*m_cnt=0;
+	*o_cnt=0;
+	*p_cnt=0;
+	return 0;
+}
+
+/**
+ * Transactional SIP response - tries to create a transaction if none found.
+ * @param msg - message to reply to
+ * @param code - the Status-code for the response
+ * @param text - the Reason-Phrase for the response
+ * @returns the tmb.t_repy() result
+ */
+int cscf_reply_transactional(struct sip_msg *msg, int code, char *text)
+{
+	unsigned int hash,label;
+	if (tmb.t_get_trans_ident(msg,&hash,&label)<0){	
+	
+		if (tmb.t_newtran(msg)<0) 
+			LM_ERR("Failed creating SIP transaction\n");
+	}
+	return tmb.t_reply(msg,code,text);
+}
+
+/**
+ * Creates and adds a SIP-Number-Auth-Items AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_sip_number_auth_items(AAAMessage *msg,unsigned int data)
+{
+	char x[4];
+	set_4bytes(x,data);
+	return 
+	cxdx_add_avp(msg,x,4,
+		AVP_IMS_SIP_Number_Auth_Items,
+		AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+		IMS_vendor_id_3GPP,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+}
+
+/**
+ * Creates and adds a SIP-Auth-Data-Item AVP.
+ * @param msg - the Diameter message to add to.
+ * @param auth_scheme - the value for the authorization scheme AVP
+ * @param auth - the value for the authorization AVP
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_sip_auth_data_item_request(AAAMessage *msg, str auth_scheme, str auth, str username, str realm,str method, str server_name)
+{
+	AAA_AVP_LIST list;
+	str group;
+	str etsi_authorization = {0, 0};
+	list.head=0;list.tail=0;
+		
+	if (auth_scheme.len){
+		cxdx_add_avp_list(&list,
+			auth_scheme.s,auth_scheme.len,
+			AVP_IMS_SIP_Authentication_Scheme,
+			AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+			IMS_vendor_id_3GPP,
+			AVP_DONT_FREE_DATA,
+			__FUNCTION__);
+	}	
+	if (auth.len){
+		cxdx_add_avp_list(&list,
+			auth.s,auth.len,
+			AVP_IMS_SIP_Authorization,
+			AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+			IMS_vendor_id_3GPP,
+			AVP_DONT_FREE_DATA,
+			__FUNCTION__);
+	}
+
+	if (server_name.len) 
+	{
+		etsi_authorization = cxdx_ETSI_sip_authorization(username, realm, s_empty, server_name, s_empty, s_empty, method, s_empty);
+	
+		if (etsi_authorization.len){
+			cxdx_add_avp_list(&list,
+				etsi_authorization.s,etsi_authorization.len,
+				AVP_ETSI_SIP_Authorization,
+				AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+				IMS_vendor_id_ETSI,
+				AVP_FREE_DATA,
+				__FUNCTION__);
+		}	
+	}
+
+	if (!list.head) return 1;
+	group = cdpb.AAAGroupAVPS(list);
+	
+	cdpb.AAAFreeAVPList(&list);
+	
+	return 
+	cxdx_add_avp(msg,group.s,group.len,
+		AVP_IMS_SIP_Auth_Data_Item,
+		AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+		IMS_vendor_id_3GPP,
+		AVP_FREE_DATA,
+		__FUNCTION__);
+}
+
+/**
+ * Creates and adds a Server-Name AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_server_name(AAAMessage *msg,str data)
+{
+	return 
+	cxdx_add_avp(msg,data.s,data.len,
+		AVP_IMS_Server_Name,
+		AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+		IMS_vendor_id_3GPP,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+}
+
+/**
+ * Returns the SIP-Number-Auth-Items AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the number or 0 on error
+ */
+inline int cxdx_get_sip_number_auth_items(AAAMessage *msg, int *data)
+{
+	str s;
+	s = cxdx_get_avp(msg,
+		AVP_IMS_SIP_Number_Auth_Items,
+		IMS_vendor_id_3GPP,
+		__FUNCTION__);
+	if (!s.s) return 0;
+	*data = get_4bytes(s.s);
+	return 1;
+}
+
+/**
+ * Returns the Auth-Data-Item from a Diameter answer message.
+ * @param msg - the Diameter message
+ * @param auth_date - the string to fill with the authorization data
+ * @param item_number - the int to fill with the item number
+ * @param auth_scheme - the string to fill with the authentication scheme data
+ * @param authenticate - the string to fill with the authenticate data
+ * @param authorization - the string to fill with the authorization data
+ * @param ck - the string to fill with the cipher key
+ * @param ik - the string to fill with the integrity key
+ * @returns the AVP payload on success or an empty string on error
+ */
+int cxdx_get_auth_data_item_answer(AAAMessage *msg, AAA_AVP **auth_data,
+	int *item_number,str *auth_scheme,str *authenticate,str *authorization,
+	str *ck,str *ik,
+	str *ip, 
+	str *ha1, str *response_auth, str *digest_realm,
+	str *line_identifier)
+{
+	AAA_AVP_LIST list;
+	AAA_AVP_LIST list2;
+	AAA_AVP *avp;
+	AAA_AVP *avp2;
+	str grp;
+	ha1->s = 0; ha1->len = 0;
+	*auth_data = cdpb.AAAFindMatchingAVP(msg,*auth_data,AVP_IMS_SIP_Auth_Data_Item,
+		IMS_vendor_id_3GPP,0);
+	if (!*auth_data) return 0;
+		
+	grp = (*auth_data)->data;
+	if (!grp.len) return 0;
+
+	list = cdpb.AAAUngroupAVPS(grp);
+
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_IMS_SIP_Item_Number,
+		IMS_vendor_id_3GPP,0);
+	if (!avp||!avp->data.len==4) *item_number=0;
+	else *item_number = get_4bytes(avp->data.s);
+	
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_IMS_SIP_Authentication_Scheme,
+		IMS_vendor_id_3GPP,0);
+	if (!avp||!avp->data.s) {auth_scheme->s=0;auth_scheme->len=0;}
+	else *auth_scheme = avp->data;
+
+	/* Early-IMS */
+	ip->s=0;ip->len=0;
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_Framed_IP_Address,0,0);
+	if (avp && avp->data.s){
+		if (avp->data.len!=4){
+			LM_ERR("Invalid length of AVP Framed IP Address (should be 4 for AVP_Framed_IP_Address) >%d.\n",
+				avp->data.len);
+		}
+		ip->len = 4;
+		ip->s = avp->data.s;
+	} else { 
+		avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_Framed_IPv6_Prefix,0,0);
+		if (avp && avp->data.s){
+			if (avp->data.len==0){
+				LM_ERR("Invalid length of AVP Framed IPv6 Prefix (should be >0 for AVP_Framed_IPv6_Prefix) >%d.\n",
+					avp->data.len);
+			}
+			ip->len = avp->data.len;
+			ip->s = avp->data.s;	
+		}	
+	}
+
+	/* Digest */
+
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_CableLabs_SIP_Digest_Authenticate,IMS_vendor_id_CableLabs,0);
+	if (avp  && avp->data.s) 
+	{
+		list2 = cdpb.AAAUngroupAVPS(avp->data);
+		
+		avp2 = cdpb.AAAFindMatchingAVPList(list2,0,AVP_CableLabs_Digest_HA1,IMS_vendor_id_CableLabs,0);
+		if (!avp2||!avp2->data.s) {
+			ha1->s = 0; ha1->len = 0;
+			cdpb.AAAFreeAVPList(&list2);
+			return 0;
+		}
+		*ha1 = avp2->data;
+		cdpb.AAAFreeAVPList(&list2);
+	}
+	
+	
+	/* SIP Digest */
+
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_IMS_SIP_Digest_Authenticate,IMS_vendor_id_3GPP,0);
+	if (avp  && avp->data.s) 
+	{
+		list2 = cdpb.AAAUngroupAVPS(avp->data);
+		
+		avp2 = cdpb.AAAFindMatchingAVPList(list2,0,AVP_IMS_Digest_HA1,0,0);
+		if (!avp2||!avp2->data.s) {
+			ha1->s = 0; ha1->len = 0;
+			cdpb.AAAFreeAVPList(&list2);
+			return 0;
+		}
+		*ha1 = avp2->data;
+		cdpb.AAAFreeAVPList(&list2);
+	}
+	
+	
+	/* AKA, MD5 */
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_IMS_SIP_Authenticate,
+		IMS_vendor_id_3GPP,0);
+	if (!avp||!avp->data.s) {authenticate->s=0;authenticate->len=0;}
+	else *authenticate = avp->data;
+		
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_IMS_SIP_Authorization,
+		IMS_vendor_id_3GPP,0);
+	if (!avp||!avp->data.s) {authorization->s=0;authorization->len=0;}
+	else *authorization = avp->data;	
+
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_IMS_Confidentiality_Key,
+		IMS_vendor_id_3GPP,0);
+	if (!avp||!avp->data.s) {ck->s=0;ck->len=0;}
+	else *ck = avp->data;
+
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_IMS_Integrity_Key,
+		IMS_vendor_id_3GPP,0);
+	if (!avp||!avp->data.s) {ik->s=0;ik->len=0;}
+	else *ik = avp->data;
+
+	/* ETSI HTTP Digest */
+
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_ETSI_SIP_Authenticate,IMS_vendor_id_ETSI,0);
+	if (avp  && avp->data.s) 
+	{
+		list2 = cdpb.AAAUngroupAVPS(avp->data);
+		
+		avp2 = cdpb.AAAFindMatchingAVPList(list2,0,AVP_ETSI_Digest_Realm, IMS_vendor_id_ETSI,0);
+		if (!avp2||!avp2->data.s) {
+			digest_realm->s=0;digest_realm->len=0;
+			cdpb.AAAFreeAVPList(&list2);
+			return 0;
+		}
+		*digest_realm = avp2->data;
+
+		avp2 = cdpb.AAAFindMatchingAVPList(list2,0,AVP_ETSI_Digest_Nonce, IMS_vendor_id_ETSI,0);
+		if (!avp2||!avp2->data.s) {
+			authenticate->s=0;authenticate->len=0;
+			cdpb.AAAFreeAVPList(&list2);
+			return 0;
+		}
+		*authenticate = avp2->data;
+		
+		avp2 = cdpb.AAAFindMatchingAVPList(list2,0,AVP_ETSI_Digest_HA1, IMS_vendor_id_ETSI,0);
+		if (!avp2||!avp2->data.s) {
+			ha1->s = 0; ha1->len = 0;
+			cdpb.AAAFreeAVPList(&list2);
+			return 0;
+		}
+		*ha1 = avp2->data;
+		
+		cdpb.AAAFreeAVPList(&list2);
+	}
+
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_ETSI_SIP_Authentication_Info,IMS_vendor_id_ETSI,0);
+	if (avp  && avp->data.s) 
+	{
+		list2 = cdpb.AAAUngroupAVPS(avp->data);
+		
+		avp2 = cdpb.AAAFindMatchingAVPList(list2,0,AVP_ETSI_Digest_Response_Auth, IMS_vendor_id_ETSI,0);
+		if (!avp2||!avp2->data.s) {
+			response_auth->s=0;response_auth->len=0;
+			cdpb.AAAFreeAVPList(&list2);
+			return 0;
+		}
+		*response_auth = avp2->data;
+		cdpb.AAAFreeAVPList(&list2);
+	}
+	else
+	{
+		response_auth->s=0;response_auth->len=0;
+	}
+	
+	/* NASS Bundled */
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_ETSI_Line_Identifier, IMS_vendor_id_ETSI,0);
+	if (!avp||!avp->data.s) {line_identifier->s=0;line_identifier->len=0;}
+	else *line_identifier = avp->data;
+	
+	cdpb.AAAFreeAVPList(&list);
+	return 1;
+}
+
+/**
+ * Creates and adds a ETSI_sip_authorization AVP.
+ * @param username - UserName
+ * @param realm - Realm
+ * @param nonce - Nonce
+ * @param URI - URI
+ * @param response - Response
+ * @param algoritm - Algorithm
+ * @param method - Method
+ * @param hash - Enitity-Body-Hash
+ * @returns grouped str on success
+ */
+str cxdx_ETSI_sip_authorization(str username, str realm, str nonce, str URI, str response, str algorithm, str method, str hash)
+{
+	AAA_AVP_LIST list;
+	str group = {0, 0};
+	list.head=0;list.tail=0;
+		
+	if (username.len){
+		cxdx_add_avp_list(&list,
+			username.s,username.len,
+			AVP_ETSI_Digest_Username,
+			AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+			IMS_vendor_id_ETSI,
+			AVP_DONT_FREE_DATA,
+			__FUNCTION__);
+	}	
+
+	if (realm.len){
+		cxdx_add_avp_list(&list,
+			realm.s,realm.len,
+			AVP_ETSI_Digest_Realm,
+			AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+			IMS_vendor_id_ETSI,
+			AVP_DONT_FREE_DATA,
+			__FUNCTION__);
+	}	
+	
+	if (nonce.len){
+		cxdx_add_avp_list(&list,
+			nonce.s,nonce.len,
+			AVP_ETSI_Digest_Nonce,
+			AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+			IMS_vendor_id_ETSI,
+			AVP_DONT_FREE_DATA,
+			__FUNCTION__);
+	}
+
+	if (URI.len){
+		cxdx_add_avp_list(&list,
+			URI.s,URI.len,
+			AVP_ETSI_Digest_URI,
+			AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+			IMS_vendor_id_ETSI,
+			AVP_DONT_FREE_DATA,
+			__FUNCTION__);
+	}
+
+	if (response.len){
+		cxdx_add_avp_list(&list,
+			response.s,response.len,
+			AVP_ETSI_Digest_Response,
+			AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+			IMS_vendor_id_ETSI,
+			AVP_DONT_FREE_DATA,
+			__FUNCTION__);
+	}
+
+	if (algorithm.len){
+		cxdx_add_avp_list(&list,
+			algorithm.s,algorithm.len,
+			AVP_ETSI_Digest_Algorithm,
+			AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+			IMS_vendor_id_ETSI,
+			AVP_DONT_FREE_DATA,
+			__FUNCTION__);
+	}
+
+	if (method.len){
+		cxdx_add_avp_list(&list,
+			method.s,method.len,
+			AVP_ETSI_Digest_Method,
+			AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+			IMS_vendor_id_ETSI,
+			AVP_DONT_FREE_DATA,
+			__FUNCTION__);
+	}
+
+	if (hash.len){
+		cxdx_add_avp_list(&list,
+			hash.s,hash.len,
+			AVP_ETSI_Digest_Entity_Body_Hash,
+			AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+			IMS_vendor_id_ETSI,
+			AVP_DONT_FREE_DATA,
+			__FUNCTION__);
+	}
+
+	if (!list.head) return group;
+	group = cdpb.AAAGroupAVPS(list);
+	
+	cdpb.AAAFreeAVPList(&list);
+	
+	return group;
+}
+
+/**
+ * Returns the User-Data from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the AVP payload on success or an empty string on error
+ */
+
+inline str cxdx_get_user_data(AAAMessage *msg)
+{	
+	return cxdx_get_avp(msg,
+		AVP_IMS_User_Data_Cx,
+		IMS_vendor_id_3GPP,
+		__FUNCTION__);
+}
+
+/**
+ * Returns the Charging-Information from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the AVP payload on success or an empty string on error
+ */
+inline int cxdx_get_charging_info(AAAMessage *msg,str *ccf1,str *ccf2,str *ecf1,str *ecf2)
+{		
+	AAA_AVP_LIST list;
+	AAA_AVP *avp;
+	str grp;
+	grp = cxdx_get_avp(msg,
+		AVP_IMS_Charging_Information,
+		IMS_vendor_id_3GPP,
+		__FUNCTION__);
+	if (!grp.s) return 0;
+
+	list = cdpb.AAAUngroupAVPS(grp);
+	
+	if (ccf1){
+		avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_IMS_Primary_Charging_Collection_Function_Name,
+			IMS_vendor_id_3GPP,0);
+		if (avp) *ccf1 = avp->data;
+	}		
+	if (ccf2){
+		avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_IMS_Secondary_Charging_Collection_Function_Name,
+			IMS_vendor_id_3GPP,0);
+		if (avp) *ccf2 = avp->data;
+	}		
+	if (ecf1){
+		avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_IMS_Primary_Event_Charging_Function_Name,
+			IMS_vendor_id_3GPP,0);
+		if (avp) *ecf1 = avp->data;
+	}		
+	if (ecf2){
+		avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_IMS_Secondary_Event_Charging_Function_Name,
+			IMS_vendor_id_3GPP,0);
+		if (avp) *ecf2 = avp->data;
+	}		
+		
+	cdpb.AAAFreeAVPList(&list);
+	return 1;		
+		
+}
+
+/**
+ * Creates and adds a Server-Assignment-Type AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_server_assignment_type(AAAMessage *msg,unsigned int data)
+{
+	char x[4];
+	set_4bytes(x,data);
+	return 
+	cxdx_add_avp(msg,x,4,
+		AVP_IMS_Server_Assignment_Type,
+		AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+		IMS_vendor_id_3GPP,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+}
+
+/**
+ * Creates and adds Userdata-Available AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_userdata_available(AAAMessage *msg,unsigned int data)
+{
+	char x[4];
+	set_4bytes(x,data);
+	return 
+	cxdx_add_avp(msg,x,4,
+		AVP_IMS_User_Data_Already_Available,
+		AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+		IMS_vendor_id_3GPP,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+}
+
+/**
+ * Finds out the next Public-Identity AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @param pos - position to resume search or NULL if to start from the first AVP 
+ * @param avp_code - the code of the AVP to look for
+ * @param vendor_id - the vendor id of the AVP to look for
+ * @param func - the name of the calling function for debugging purposes
+ * @returns the AVP payload on success or an empty string on error
+ */
+inline AAA_AVP* cxdx_get_next_public_identity(AAAMessage *msg,AAA_AVP* pos,int avp_code,int vendor_id,const char *func)
+{		
+	AAA_AVP *avp;
+	
+	avp = cdpb.AAAFindMatchingAVP(msg,pos,avp_code,vendor_id,0);
+	if (avp==0){
+		LM_DBG("INFO:%s: Failed finding avp\n",func);
+		return avp;
+	}
+	else 
+		return avp;
+}
+
+/**
+ * Returns the User-Name AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the AVP payload on success or an empty string on error
+ */
+inline str cxdx_get_user_name(AAAMessage *msg)
+{
+	return cxdx_get_avp(msg,
+		AVP_User_Name,
+		0,
+		__FUNCTION__);
+}
+
+/**
+ * Creates and adds a Result-Code AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_result_code(AAAMessage *msg,unsigned int data)
+{
+	char x[4];
+	set_4bytes(x,data);
+	return 
+	cxdx_add_avp(msg,x,4,
+		AVP_Result_Code,
+		AAA_AVP_FLAG_MANDATORY,
+		0,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+}

+ 286 - 0
modules/auth_ims/cxdx_avp.h

@@ -0,0 +1,286 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * 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
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * 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
+ * 
+ */
+
+#ifndef IS_CSCF_CXDX_AVP_H
+#define IS_CSCF_CXDX_AVP_H
+
+extern struct cdp_binds cdpb;            /**< Structure with pointers to cdp funcs 		*/
+extern struct tm_binds tmb;
+
+struct AAAMessage;
+struct AAA_AVP;
+struct sip_msg;
+
+/**
+ * Creates and adds a Destination-Realm AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_destination_realm(AAAMessage *msg, str data);
+
+/**
+ * Creates and adds a Vendor-Specifig-Application-ID AVP.
+ * @param msg - the Diameter message to add to.
+ * @param vendor_id - the value of the vendor_id,
+ * @param auth_id - the authorization application id
+ * @param acct_id - the accounting application id
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_vendor_specific_appid(AAAMessage *msg,unsigned int vendor_id,unsigned int auth_id,unsigned int acct_id);
+
+/**
+ * Creates and adds a Auth-Session-State AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_auth_session_state(AAAMessage *msg,unsigned int data);
+
+/**
+ * Creates and adds a User-Name AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_user_name(AAAMessage *msg,str data);
+
+/**
+ * Creates and adds a Public Identity AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_public_identity(AAAMessage *msg,str data);
+
+/**
+ * Creates and adds a Visited-Network-ID AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_visited_network_id(AAAMessage *msg,str data);
+
+/**
+ * Creates and adds a UAR-Flags AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_UAR_flags(AAAMessage *msg, unsigned int sos_reg);
+
+/**
+ * Creates and adds a Authorization-Type AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_authorization_type(AAAMessage *msg,unsigned int data);
+
+/**
+ * Returns the Result-Code AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the AVP payload on success or an empty string on error
+ */
+inline int cxdx_get_result_code(AAAMessage *msg, int *data);
+
+/**
+ * Returns the Experimental-Result-Code AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the AVP payload on success or an empty string on error
+ */
+inline int cxdx_get_experimental_result_code(AAAMessage *msg, int *data);
+
+/**
+ * Returns the Server-Name AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the AVP payload on success or an empty string on error
+ */
+inline str cxdx_get_server_name(AAAMessage *msg);
+
+/**
+ * Returns the Capabilities from the grouped AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @param m - array to be filled with the retrieved mandatory capabilities
+ * @param m_cnt - size of the array above to be filled
+ * @param o - array to be filled with the retrieved optional capabilities
+ * @param o_cnt - size of the array above to be filled
+ * @returns 1 on success 0 on fail
+ */
+inline int cxdx_get_capabilities(AAAMessage *msg,int **m,int *m_cnt,int **o,int *o_cnt,	str **p,int *p_cnt);
+
+/**
+ * Creates and adds a SIP-Number-Auth-Items AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_sip_number_auth_items(AAAMessage *msg,unsigned int data);
+
+/**
+ * Creates and adds a SIP-Auth-Data-Item AVP.
+ * @param msg - the Diameter message to add to.
+ * @param auth_scheme - the value for the authorization scheme AVP
+ * @param auth - the value for the authorization AVP
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_sip_auth_data_item_request(AAAMessage *msg, str auth_scheme, str auth, str username, str realm,str method, str server_name);
+
+/**
+ * Creates and adds a Server-Name AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_server_name(AAAMessage *msg,str data);
+
+/**
+ * Returns the SIP-Number-Auth-Items AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the number or 0 on error
+ */
+inline int cxdx_get_sip_number_auth_items(AAAMessage *msg, int *data);
+
+/**
+ * Returns the Auth-Data-Item from a Diameter answer message.
+ * @param msg - the Diameter message
+ * @param auth_date - the string to fill with the authorization data
+ * @param item_number - the int to fill with the item number
+ * @param auth_scheme - the string to fill with the authentication scheme data
+ * @param authenticate - the string to fill with the authenticate data
+ * @param authorization - the string to fill with the authorization data
+ * @param ck - the string to fill with the cipher key
+ * @param ik - the string to fill with the integrity key
+ * @returns the AVP payload on success or an empty string on error
+ */
+int cxdx_get_auth_data_item_answer(AAAMessage *msg, AAA_AVP **auth_data,
+	int *item_number,str *auth_scheme,str *authenticate,str *authorization,
+	str *ck,str *ik,
+	str *ip, 
+	str *ha1, str *response_auth, str *digest_realm,
+	str *line_identifier);
+
+/**
+ * Creates and adds a ETSI_sip_authorization AVP.
+ * @param username - UserName
+ * @param realm - Realm
+ * @param nonce - Nonce
+ * @param URI - URI
+ * @param response - Response
+ * @param algoritm - Algorithm
+ * @param method - Method
+ * @param hash - Enitity-Body-Hash
+ * @returns grouped str on success
+ */
+str cxdx_ETSI_sip_authorization(str username, str realm, str nonce, str URI, str response, str algorithm, str method, str hash);
+
+/**
+ * Returns the User-Data from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the AVP payload on success or an empty string on error
+ */
+
+inline str cxdx_get_user_data(AAAMessage *msg);
+
+/**
+ * Returns the Charging-Information from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the AVP payload on success or an empty string on error
+ */
+inline int cxdx_get_charging_info(AAAMessage *msg,str *ccf1,str *ccf2,str *ecf1,str *ecf2);
+
+/**
+ * Creates and adds a Server-Assignment-Type AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_server_assignment_type(AAAMessage *msg,unsigned int data);
+
+/**
+ * Creates and adds Userdata-Available AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_userdata_available(AAAMessage *msg,unsigned int data);
+
+/**
+ * Finds out the next Public-Identity AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @param pos - position to resume search or NULL if to start from the first AVP 
+ * @param avp_code - the code of the AVP to look for
+ * @param vendor_id - the vendor id of the AVP to look for
+ * @param func - the name of the calling function for debugging purposes
+ * @returns the AVP payload on success or an empty string on error
+ */
+inline AAA_AVP* cxdx_get_next_public_identity(AAAMessage *msg,AAA_AVP* pos,int avp_code,int vendor_id,const char *func);
+
+/**
+ * Returns the User-Name AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the AVP payload on success or an empty string on error
+ */
+inline str cxdx_get_user_name(AAAMessage *msg);
+
+/**
+ * Creates and adds a Result-Code AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_result_code(AAAMessage *msg,unsigned int data);
+
+/**
+ * Transactional SIP response - tries to create a transaction if none found.
+ * @param msg - message to reply to
+ * @param code - the Status-code for the response
+ * @param text - the Reason-Phrase for the response
+ * @returns the tmb.t_repy() result
+ */
+int cscf_reply_transactional(struct sip_msg *msg, int code, char *text);
+
+
+#endif /* IS_CSCF_CXDX_AVP_H */

+ 525 - 0
modules/auth_ims/cxdx_mar.c

@@ -0,0 +1,525 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * 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
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * 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
+ * 
+ */
+
+#include "stats.h"
+#include "../cdp/cdp_load.h"
+#include "../../modules/tm/tm_load.h"
+#include "../../modules/dialog2/dlg_load.h"
+#include "api.h"
+#include "cxdx_avp.h"
+
+#include "cxdx_mar.h"
+#include "authorize.h"
+#include "../../lib/ims/ims_getters.h"
+#include "utils.h"
+
+static str empty_s = {0, 0};
+
+static str s_empty = {0, 0};
+extern str auth_scheme_types[];
+
+extern str scscf_name_str;
+
+//we use pseudo variables to communicate back to config file this takes the result and converys to a return code, publishes it a pseudo variable
+
+int create_return_code(int result) {
+    int rc;
+    int_str avp_val, avp_name;
+    avp_name.s.s = "maa_return_code";
+    avp_name.s.len = 15;
+
+    //build avp spec for uaa_return_code
+    avp_val.n = result;
+
+    rc = add_avp(AVP_NAME_STR, avp_name, avp_val);
+
+    if (rc < 0)
+        LM_ERR("couldnt create AVP\n");
+    else
+        LM_INFO("created AVP successfully : [%.*s] - [%d]\n", avp_name.s.len, avp_name.s.s, result);
+
+    return 1;
+}
+
+void free_saved_transaction_data(saved_transaction_t* data) {
+    if (!data)
+        return;
+    LM_DBG("Freeing saved transaction data: async\n");
+    if (data->realm.s && data->realm.len) {
+        shm_free(data->realm.s);
+        data->realm.len = 0;
+    }
+
+    shm_free(data);
+}
+
+void async_cdp_callback(int is_timeout, void *param, AAAMessage *maa, long elapsed_msecs) {
+    int i, j;
+    int rc = -1, experimental_rc = -1;
+    saved_transaction_t* data = (saved_transaction_t*) param;
+    struct cell *t = 0;
+    int result = CSCF_RETURN_TRUE;
+    int sip_number_auth_items;
+    struct auth_data_item_list *adi_list = 0;
+    AAA_AVP *auth_data;
+    auth_data = 0;
+    int item_number;
+    str authenticate = {0, 0}, authorization2 = {0, 0}, ck = {0, 0}, ik = {0, 0}, ip = {0, 0}, ha1 = {0, 0};
+    str line_identifier = {0, 0};
+    str response_auth = {0, 0}, digest_realm = {0, 0};
+    auth_vector *av = 0, **avlist = 0;
+    HASHHEX ha1_hex;
+    HASHHEX result_hex;
+    str etsi_nonce = {0, 0};
+    str private_identity, public_identity;
+    str algorithm;
+
+    if (is_timeout) {
+    	update_stat(stat_mar_timeouts, 1);
+        LM_ERR("Transaction timeout - did not get MAA\n");
+        result = CSCF_RETURN_ERROR;
+        goto error;
+    }
+    if (!maa) {
+        LM_ERR("Error sending message via CDP\n");
+        result = CSCF_RETURN_ERROR;
+        goto error;
+    }
+
+    update_stat(mar_replies_received, 1);
+    update_stat(mar_replies_response_time, elapsed_msecs);
+
+    if (tmb.t_lookup_ident(&t, data->tindex, data->tlabel) < 0) {
+        LM_ERR("t_continue: transaction not found\n");
+        result = CSCF_RETURN_ERROR;
+        goto error;
+    }
+
+    /* get the private_identity */
+    private_identity = get_private_identity(t->uas.request, data->realm, data->is_proxy_auth);
+    if (!private_identity.len) {
+        LM_ERR("No private identity specified (Authorization: username)\n");
+        stateful_request_reply_async(t, t->uas.request, 403, MSG_403_NO_PRIVATE);
+        result = CSCF_RETURN_FALSE;
+        goto error;
+    }
+    /* get the public_identity */
+    public_identity = get_public_identity(t->uas.request);
+    if (!public_identity.len) {
+        LM_ERR("No public identity specified (To:)\n");
+        stateful_request_reply_async(t, t->uas.request, 403, MSG_403_NO_PUBLIC);
+        result = CSCF_RETURN_FALSE;
+        goto error;
+    }
+
+
+    //get each individual element from the MAA
+    cxdx_get_result_code(maa, &rc);
+    cxdx_get_experimental_result_code(maa, &experimental_rc);
+    cxdx_get_sip_number_auth_items(maa, &sip_number_auth_items);
+
+    //now assign the auth_data_item elements
+    //there can be many of these in the MAA
+    struct auth_data_item *adi;
+    int adi_len;
+    char *p;
+    while ((cxdx_get_auth_data_item_answer(maa, &auth_data, &item_number,
+            &algorithm, &authenticate, &authorization2,
+            &ck, &ik,
+            &ip,
+            &ha1, &response_auth, &digest_realm,
+            &line_identifier))) {
+
+        //create an auth_data_item for each entry in the MAA
+        adi_len = sizeof (struct auth_data_item) +authenticate.len + authorization2.len + ck.len + ik.len + ip.len + ha1.len + line_identifier.len + response_auth.len + digest_realm.len + algorithm.len;
+        adi = (struct auth_data_item*) shm_malloc(adi_len);
+        if (!adi) {
+            LM_CRIT("Out of memory!\n");
+            result = CSCF_RETURN_ERROR;
+            goto done;
+        }
+
+        memset(adi, 0, adi_len);
+
+        //put all elements in the auth_data_item entry
+        p = (char*) (adi + 1);
+
+        adi->authenticate.s = p;
+        adi->authenticate.len = authenticate.len;
+        memcpy(p, authenticate.s, authenticate.len);
+        p += authenticate.len;
+
+        adi->authorization.s = p;
+        adi->authorization.len = authorization2.len;
+        memcpy(p, authorization2.s, authorization2.len);
+        p += authorization2.len;
+
+        adi->auth_scheme.s = p;
+        adi->auth_scheme.len = algorithm.len;
+        memcpy(p, algorithm.s, algorithm.len);
+        p += algorithm.len;
+
+        adi->ck.s = p;
+        adi->ck.len = ck.len;
+        memcpy(p, ck.s, ck.len);
+        p += ck.len;
+
+        adi->ik.s = p;
+        adi->ik.len = ik.len;
+        memcpy(p, ik.s, ik.len);
+        p += ik.len;
+
+        adi->ip.s = p;
+        adi->ip.len = ip.len;
+        memcpy(p, ip.s, ip.len);
+        p += ip.len;
+
+        adi->ha1.s = p;
+        adi->ha1.len = ha1.len;
+        memcpy(p, ha1.s, ha1.len);
+        p += ha1.len;
+
+        adi->line_identifier.s = p;
+        adi->line_identifier.len = line_identifier.len;
+        memcpy(p, line_identifier.s, line_identifier.len);
+        p += line_identifier.len;
+
+        adi->response_auth.s = p;
+        adi->response_auth.len = response_auth.len;
+        memcpy(p, response_auth.s, response_auth.len);
+        p += response_auth.len;
+
+        adi->digest_realm.s = p;
+        adi->digest_realm.len = digest_realm.len;
+        memcpy(p, digest_realm.s, digest_realm.len);
+        p += digest_realm.len;
+
+        if (p != (((char*) adi) + adi_len)) {
+            LM_CRIT("buffer overflow\n");
+            shm_free(adi);
+            adi = 0;
+            result = CSCF_RETURN_ERROR;
+            goto done;
+        }
+        auth_data->code = -auth_data->code;
+        adi->item_number = item_number;
+
+        int len = sizeof (struct auth_data_item_list);
+        adi_list = (struct auth_data_item_list*) shm_malloc(len);
+        memset(adi_list, 0, len);
+
+        if (adi_list->first == 0) {
+            adi_list->first = adi_list->last = adi;
+        } else {
+            adi_list->last->next = adi;
+            adi->previous = adi_list->last;
+            adi_list->last = adi;
+        }
+    }
+
+    if (!(rc) && !(experimental_rc)) {
+        stateful_request_reply_async(t, t->uas.request, 480, MSG_480_DIAMETER_MISSING_AVP);
+        result = CSCF_RETURN_FALSE;
+        goto done;
+    }
+
+    switch (rc) {
+        case -1:
+            switch (experimental_rc) {
+                case RC_IMS_DIAMETER_ERROR_USER_UNKNOWN:
+                    stateful_request_reply_async(t, t->uas.request, 403, MSG_403_USER_UNKNOWN);
+                    result = CSCF_RETURN_FALSE;
+                    break;
+                case RC_IMS_DIAMETER_ERROR_IDENTITIES_DONT_MATCH:
+                    stateful_request_reply_async(t, t->uas.request, 403, MSG_403_IDENTITIES_DONT_MATCH);
+                    result = CSCF_RETURN_FALSE;
+                    break;
+                case RC_IMS_DIAMETER_ERROR_AUTH_SCHEME_NOT_SUPPORTED:
+                    stateful_request_reply_async(t, t->uas.request, 403, MSG_403_AUTH_SCHEME_UNSOPPORTED);
+                    result = CSCF_RETURN_FALSE;
+                    break;
+
+                default:
+                    stateful_request_reply_async(t, t->uas.request, 403, MSG_403_UNKOWN_EXPERIMENTAL_RC);
+                    result = CSCF_RETURN_FALSE;
+            }
+            break;
+
+        case AAA_UNABLE_TO_COMPLY:
+            stateful_request_reply_async(t, t->uas.request, 403, MSG_403_UNABLE_TO_COMPLY);
+            result = CSCF_RETURN_FALSE;
+            break;
+
+        case AAA_SUCCESS:
+            goto success;
+            break;
+
+        default:
+            stateful_request_reply_async(t, t->uas.request, 403, MSG_403_UNKOWN_RC);
+            result = CSCF_RETURN_FALSE;
+    }
+
+    goto done;
+
+success:
+
+    if (!sip_number_auth_items) {
+        stateful_request_reply_async(t, t->uas.request, 403, MSG_403_NO_AUTH_DATA);
+        result = CSCF_RETURN_FALSE;
+        goto done;
+    }
+
+    avlist = shm_malloc(sizeof (auth_vector *) * sip_number_auth_items);
+    if (!avlist) {
+        stateful_request_reply_async(t, t->uas.request, 403, MSG_480_HSS_ERROR);
+        result = CSCF_RETURN_FALSE;
+        goto done;
+    }
+
+    sip_number_auth_items = 0;
+
+    struct auth_data_item *tmp;
+    tmp = adi_list->first;
+
+    while (tmp) {
+
+        if (tmp->ip.len)
+            av = new_auth_vector(tmp->item_number, tmp->auth_scheme, empty_s,
+                tmp->ip, empty_s, empty_s);
+        else if (tmp->line_identifier.len)
+            av = new_auth_vector(tmp->item_number, tmp->auth_scheme, empty_s,
+                line_identifier, empty_s, empty_s);
+        else if (tmp->ha1.len) {
+            if (tmp->response_auth.len) //HSS check
+            {
+                memset(ha1_hex, 0, HASHHEXLEN + 1);
+                memcpy(ha1_hex, tmp->ha1.s,
+                        tmp->ha1.len > HASHHEXLEN ? 32 : tmp->ha1.len);
+
+                etsi_nonce.len = tmp->authenticate.len / 2;
+                etsi_nonce.s = pkg_malloc(etsi_nonce.len);
+                if (!etsi_nonce.s) {
+                    LM_ERR("error allocating %d bytes\n", etsi_nonce.len);
+                    goto done;
+                }
+                etsi_nonce.len = base16_to_bin(tmp->authenticate.s,
+                        tmp->authenticate.len, etsi_nonce.s);
+
+                calc_response(ha1_hex, &etsi_nonce, &empty_s, &empty_s,
+                        &empty_s, 0, &(t->uas.request->first_line.u.request.method),
+                        &scscf_name_str, 0, result_hex);
+                pkg_free(etsi_nonce.s);
+
+                if (!tmp->response_auth.len == 32
+                        || strncasecmp(tmp->response_auth.s, result_hex, 32)) {
+                    LM_ERR("The HSS' Response-Auth is different from what we compute locally!\n"
+                            " BUT! If you sent an MAR with auth scheme unknown (HSS-Selected Authentication), this is normal.\n"
+                            "HA1=\t|%s|\nNonce=\t|%.*s|\nMethod=\t|%.*s|\nuri=\t|%.*s|\nxresHSS=\t|%.*s|\nxresSCSCF=\t|%s|\n",
+                            ha1_hex,
+                            tmp->authenticate.len, tmp->authenticate.s,
+                            t->uas.request->first_line.u.request.method.len, t->uas.request->first_line.u.request.method.s,
+                            scscf_name_str.len, scscf_name_str.s,
+                            tmp->response_auth.len, tmp->response_auth.s,
+                            result_hex);
+                    //stateful_register_reply(msg,514,MSG_514_HSS_AUTH_FAILURE);
+                    //goto done;
+                }
+            }
+            av = new_auth_vector(tmp->item_number, tmp->auth_scheme,
+                    tmp->authenticate, tmp->ha1, empty_s, empty_s);
+        } else
+            av = new_auth_vector(tmp->item_number, tmp->auth_scheme,
+                tmp->authenticate, tmp->authorization, tmp->ck, tmp->ik);
+
+        if (sip_number_auth_items == 0)
+            avlist[sip_number_auth_items++] = av;
+        else {
+            i = sip_number_auth_items;
+            while (i > 0 && avlist[i - 1]->item_number > av->item_number)
+                i--;
+            for (j = sip_number_auth_items; j > i; j--)
+                avlist[j] = avlist[j - 1];
+            avlist[i] = av;
+            sip_number_auth_items++;
+        }
+
+        //TODO need to confirm that removing this has done no problems
+        //tmp->auth_data->code = -tmp->auth_data->code;
+
+        tmp = tmp->next;
+    }
+
+    //MAA returns a whole list of av! Which should we use?
+    //right now we take the first and put the rest in the AV queue
+    //then we use the first one and then add it to the queue as sent!
+
+    for (i = 1; i < sip_number_auth_items; i++)
+        if (!add_auth_vector(private_identity, public_identity, avlist[i]))
+            free_auth_vector(avlist[i]);
+
+    if (!pack_challenge(t->uas.request, data->realm, avlist[0], data->is_proxy_auth)) {
+        stateful_request_reply_async(t, t->uas.request, 500, MSG_500_PACK_AV);
+        result = CSCF_RETURN_FALSE;
+        goto done;
+    }
+
+    if (data->is_proxy_auth)
+        stateful_request_reply_async(t, t->uas.request, 407, MSG_407_CHALLENGE);
+    else
+        stateful_request_reply_async(t, t->uas.request, 401, MSG_401_CHALLENGE);
+
+done:
+    start_reg_await_timer(avlist[0]); //start the timer to remove stale or unused Auth Vectors
+
+    //now we add it to the queue as sent as we have already sent the challenge and used it and set the status to SENT
+    if (!add_auth_vector(private_identity, public_identity, avlist[0]))
+        free_auth_vector(avlist[0]);
+
+    //free memory
+    if (maa) cdpb.AAAFreeMessage(&maa);
+    if (avlist) {
+        shm_free(avlist);
+        avlist = 0;
+    }
+    struct auth_data_item *tmp1 = adi_list->first;
+    while (tmp1) {
+        struct auth_data_item *tmp2 = tmp1->next;
+        shm_free(tmp1);
+        tmp1 = tmp2;
+    }
+    if (adi_list) {
+        shm_free(adi_list);
+        adi_list = 0;
+    }
+
+    LM_DBG("DBG:UAR Async CDP callback: ... Done resuming transaction\n");
+    set_avp_list(AVP_TRACK_FROM | AVP_CLASS_URI, &t->uri_avps_from);
+    set_avp_list(AVP_TRACK_TO | AVP_CLASS_URI, &t->uri_avps_to);
+    set_avp_list(AVP_TRACK_FROM | AVP_CLASS_USER, &t->user_avps_from);
+    set_avp_list(AVP_TRACK_TO | AVP_CLASS_USER, &t->user_avps_to);
+    set_avp_list(AVP_TRACK_FROM | AVP_CLASS_DOMAIN, &t->domain_avps_from);
+    set_avp_list(AVP_TRACK_TO | AVP_CLASS_DOMAIN, &t->domain_avps_to);
+
+    //make sure we delete any private lumps we created
+    create_return_code(result);
+    if (t) {
+        del_nonshm_lump_rpl(&t->uas.request->reply_lump);
+        tmb.unref_cell(t);
+    }
+    tmb.t_continue(data->tindex, data->tlabel, data->act);
+    free_saved_transaction_data(data);
+    return;
+
+error:
+    //don't need to set result code as by default it is ERROR!
+
+    if (t) {
+        del_nonshm_lump_rpl(&t->uas.request->reply_lump);
+        tmb.unref_cell(t);
+    }
+    tmb.t_continue(data->tindex, data->tlabel, data->act);
+    free_saved_transaction_data(data);
+}
+
+/**
+ * Create and send a Multimedia-Authentication-Request and returns the parsed Answer structure.
+ * This function retrieves authentication vectors from the HSS.
+ * @param msg - the SIP message to send for
+ * @parma public_identity - the public identity of the user
+ * @param private_identity - the private identity of the user
+ * @param count - how many authentication vectors to ask for
+ * @param algorithm - for which algorithm
+ * @param authorization - the authorization value
+ * @param server_name - local name of the S-CSCF to save on the HSS
+ * @returns the parsed maa struct
+ */
+int cxdx_send_mar(struct sip_msg *msg, str public_identity, str private_identity,
+        unsigned int count, str algorithm, str authorization, str server_name, saved_transaction_t* transaction_data) {
+    AAAMessage *mar = 0;
+    AAASession *session = 0;
+
+    session = cdpb.AAACreateSession(0);
+
+    mar = cdpb.AAACreateRequest(IMS_Cx, IMS_MAR, Flag_Proxyable, session);
+    if (session) {
+        cdpb.AAADropSession(session);
+        session = 0;
+    }
+    if (!mar) goto error1;
+
+    if (!cxdx_add_destination_realm(mar, cxdx_dest_realm)) goto error1;
+
+    if (!cxdx_add_vendor_specific_appid(mar, IMS_vendor_id_3GPP, IMS_Cx, 0 /*IMS_Cx*/)) goto error1;
+    if (!cxdx_add_auth_session_state(mar, 1)) goto error1;
+
+    if (!cxdx_add_public_identity(mar, public_identity)) goto error1;
+    if (!cxdx_add_user_name(mar, private_identity)) goto error1;
+    if (!cxdx_add_sip_number_auth_items(mar, count)) goto error1;
+    if (algorithm.len == auth_scheme_types[AUTH_HTTP_DIGEST_MD5].len &&
+            strncasecmp(algorithm.s, auth_scheme_types[AUTH_HTTP_DIGEST_MD5].s, algorithm.len) == 0) {
+        if (!cxdx_add_sip_auth_data_item_request(mar, algorithm, authorization, private_identity, cxdx_dest_realm,
+                msg->first_line.u.request.method, server_name)) goto error1;
+    } else {
+        if (!cxdx_add_sip_auth_data_item_request(mar, algorithm, authorization, private_identity, cxdx_dest_realm,
+                msg->first_line.u.request.method, s_empty)) goto error1;
+    }
+    if (!cxdx_add_server_name(mar, server_name)) goto error1;
+
+    if (cxdx_forced_peer.len)
+        cdpb.AAASendMessageToPeer(mar, &cxdx_forced_peer, (void*) async_cdp_callback, (void*) transaction_data);
+    else
+        cdpb.AAASendMessage(mar, (void*) async_cdp_callback, (void*) transaction_data);
+
+
+    LM_DBG("Successfully sent async diameter\n");
+
+    return 0;
+
+error1: //Only free MAR IFF is has not been passed to CDP
+    if (mar) cdpb.AAAFreeMessage(&mar);
+    LM_ERR("Error occurred trying to send MAR\n");
+    return -1;
+}
+

+ 121 - 0
modules/auth_ims/cxdx_mar.h

@@ -0,0 +1,121 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * 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
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * 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
+ * 
+ */
+
+
+#ifndef CXDX_MAR_H
+#define CXDX_MAR_H
+
+#include "../../mod_fix.h"
+
+struct sip_msg;
+struct parsed_maa;
+
+extern struct cdp_binds cdpb;
+extern str cxdx_forced_peer; /**< FQDN of the Diameter peer to send requests to */
+extern str cxdx_dest_realm;
+extern struct tm_binds tmb;
+
+typedef struct mar_param {
+	int type;
+        char* param;
+	cfg_action_t *paction;
+} mar_param_t;
+
+typedef struct saved_transaction {
+	unsigned int tindex;
+	unsigned int tlabel;
+	unsigned int ticks;
+	cfg_action_t *act;
+	int is_proxy_auth;
+    str realm;
+} saved_transaction_t;
+
+//linked list of auth_data information for maa structure
+struct auth_data_item {
+    int item_number;
+    struct auth_data_item *next;
+    struct auth_data_item *previous;
+    str authenticate;
+    str authorization;
+    str auth_scheme;
+    str ck;
+    str ik;
+    str ip;
+    str ha1;
+    str line_identifier;
+    str response_auth;
+    str digest_realm;
+
+};
+
+/*! list of auth data item entries in the parsed maa structure */
+struct auth_data_item_list {
+    struct auth_data_item *first; /*!< auth data list */
+    struct auth_data_item *last; /*!< optimisation, end of the auth data list */
+};
+
+void free_saved_transaction_data(saved_transaction_t* data);
+int create_return_code(int result);
+
+/**
+ * Create and send a Multimedia-Authentication-Request and returns the parsed Answer structure.
+ * This function retrieves authentication vectors from the HSS.
+ * @param msg - the SIP message to send for
+ * @parma public_identity - the public identity of the user
+ * @param private_identity - the private identity of the user
+ * @param count - how many authentication vectors to ask for
+ * @param algorithm - for which algorithm
+ * @param authorization - the authorization value
+ * @param server_name - local name of the S-CSCF to save on the HSS
+ * @param realm - Realm of the user
+ * @returns the parsed maa struct
+ */
+int cxdx_send_mar(struct sip_msg *msg, str public_identity, str private_identity,
+					unsigned int count,str algorithm,str authorization,str server_name, saved_transaction_t* transaction_data);
+
+void async_cdp_callback(int is_timeout, void *param, AAAMessage *maa, long elapsed_msecs);
+
+#endif
+

+ 4 - 0
modules/auth_ims/doc/Makefile

@@ -0,0 +1,4 @@
+docs = auth_ims.xml
+
+docbook_dir = ../../../docbook
+include $(docbook_dir)/Makefile.module

+ 65 - 0
modules/auth_ims/doc/auth_ims.xml

@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+]>
+<book>
+  <bookinfo>
+    <title>auth_ims Module</title>
+
+    <authorgroup>
+      <author>
+        <firstname>Dragos Vingarzan</firstname>
+
+        <surname/>
+
+        <affiliation>
+          <orgname>FhG Fokus</orgname>
+        </affiliation>
+
+        <email>[email protected]</email>
+      </author>
+
+      <author>
+        <firstname>Jason</firstname>
+
+        <surname>Penton</surname>
+
+        <affiliation>
+          <orgname>Smile Communications</orgname>
+        </affiliation>
+
+        <email>[email protected]</email>
+      </author>
+
+      <author>
+        <firstname>Richard</firstname>
+
+        <surname>Good</surname>
+        <affiliation>
+          <orgname>Smile Communications</orgname>
+        </affiliation>
+
+        <email>[email protected]</email>
+      </author>
+    </authorgroup>
+
+    <copyright>
+      <year>2007</year>
+      <holder>FhG FOKUS</holder>
+    </copyright>
+
+    <copyright>
+      <year>2012</year>
+
+      <holder>Smile Communications</holder>
+    </copyright>
+  </bookinfo>
+
+  <toc/>
+
+  <xi:include href="auth_ims_admin.xml"
+              xmlns:xi="http://www.w3.org/2001/XInclude"/>
+</book>

+ 509 - 0
modules/auth_ims/doc/auth_ims_admin.xml

@@ -0,0 +1,509 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+]>
+<!-- Auth_db Module User's Guide -->
+<chapter>
+  <title>&adminguide;</title>
+
+  <section>
+    <title>Overview</title>
+
+    <para>This module contains all authentication related functions for an IMS
+    environment. The module does not depend on the base Kamailio auth modules
+    as other auth modules do. Instead auth_ims is dependent on the CDP (C
+    Diameter Peer) modules for communicating with HSS as specified in 3GPP
+    specs.</para>
+  </section>
+
+  <section>
+    <title>Dependencies</title>
+
+    <section>
+      <title>&kamailio; Modules</title>
+
+      <para>The Following mouldes must be loaded before this module:</para>
+
+      <itemizedlist>
+        <listitem>
+          <para>TM - Transaction Manager</para>
+        </listitem>
+
+        <listitem>
+          <para>CDP - C Diameter Peer</para>
+        </listitem>
+
+        <listitem>
+          <para>CDP_AVP - CDP AVP Applications</para>
+        </listitem>
+      </itemizedlist>
+    </section>
+
+    <section>
+      <title>External Libraries or Applications</title>
+
+      <para>This modules requires the internal IMS library.</para>
+    </section>
+  </section>
+
+  <section>
+    <title>Parameters</title>
+
+    <section>
+      <title><varname>name</varname> (string)</title>
+
+      <para>This is the name of the SCSCF as identified in communication with
+      the HSS (Server-Name AVP of MAR).</para>
+
+      <para><emphasis> Default value is 'sip:scscf.ims.smilecoms.com:6060'.
+      </emphasis></para>
+
+      <example>
+        <title><varname>name</varname> parameter usage</title>
+
+        <programlisting format="linespecific">
+...
+modparam("auth_ims", "name", "sip:scscf3.ims.smilecoms.com:6060")
+...
+</programlisting>
+      </example>
+    </section>
+
+    <section>
+      <title><varname>name</varname> (string)</title>
+
+      <para>This is the name of the SCSCF as identified in communication with
+      the HSS (Server-Name AVP of MAR).</para>
+
+      <para><emphasis> Default value is 'sip:scscf.ims.smilecoms.com:6060'.
+      </emphasis></para>
+
+      <example>
+        <title><varname>name</varname> parameter usage</title>
+
+        <programlisting format="linespecific">
+...
+modparam("auth_ims", "name", "sip:scscf3.ims.smilecoms.com:6060")
+...
+</programlisting>
+      </example>
+    </section>
+
+    <section>
+      <title><varname>auth_data_hash_size</varname> (integer)</title>
+
+      <para>This is the size of the hash table used to store auth vectors
+      (AV). Default value is fine for most people. Use the parameter if you
+      really need to change it.</para>
+
+      <para>Default value is <quote>1024</quote>.</para>
+
+      <example>
+        <title><varname>auth_data_hash_size</varname> parameter usage</title>
+
+        <programlisting format="linespecific">
+...
+modparam("auth_ims", "auth_data_hash_size", 1024)
+...
+</programlisting>
+      </example>
+    </section>
+
+    <section>
+      <title><varname>auth_vector_timeout</varname> (integer)</title>
+
+      <para>This is the time, in seconds, that a SENTauth vector is valid for.
+      If there is no response ...</para>
+
+      <para>Default value is <quote>60</quote>.</para>
+
+      <example>
+        <title><varname>auth_vector_timeout</varname> parameter usage</title>
+
+        <programlisting format="linespecific">
+...
+modparam("auth_ims", "auth_vector_timeout", "domain")
+...
+</programlisting>
+      </example>
+    </section>
+
+    <section>
+      <title><varname>auth_data_timeout</varname> (int)</title>
+
+      <para>Time, in seconds, an used auth vector is valid for.</para>
+
+      <para>Default value is <quote>60</quote>.</para>
+
+      <example>
+        <title><varname>password_column</varname> parameter usage</title>
+
+        <programlisting format="linespecific">
+...
+modparam("auth_ims", "auth_data_timeout", 60)
+...
+</programlisting>
+      </example>
+    </section>
+
+    <section>
+      <title><varname>av_request_at_once</varname> (integer)</title>
+
+      <para>How many auth vectors to request in MAR.</para>
+
+      <para>Default value is 1</para>
+
+      <example>
+        <title><varname>av_request_at_once</varname> parameter usage</title>
+
+        <programlisting format="linespecific">
+...
+modparam("auth_ims", "av_request_at_once", 1)
+...
+</programlisting>
+      </example>
+    </section>
+
+    <section>
+      <title><varname>av_request_at_sync</varname> (integer)</title>
+
+      <para>How many auth vectors to request at sync. Default value is
+      1.</para>
+
+      <example>
+        <title><varname>av_request_at_sync</varname> parameter usage</title>
+
+        <programlisting format="linespecific">
+...
+modparam("auth_ims", "av_request_at_sync", 1)
+...
+</programlisting>
+      </example>
+    </section>
+
+    <section>
+      <title><varname>registration_default_algorithm</varname>
+      (string)</title>
+
+      <para>The default authentication algorithm to use for registration if
+      one is not specified.</para>
+
+      <para>Options are: <itemizedlist>
+          <listitem>
+            <para><emphasis>AKAV1-MD5</emphasis></para>
+          </listitem>
+
+          <listitem>
+            <para><emphasis>AKAV2-MD5</emphasis></para>
+          </listitem>
+
+          <listitem>
+            <para><emphasis>MD5</emphasis></para>
+          </listitem>
+
+          <listitem>
+            <para>HSS-Selected - HSS will decide on auth algorithm</para>
+          </listitem>
+        </itemizedlist>Default value is <quote>AKAv1-MD5</quote>.</para>
+
+      <example>
+        <title><varname>registration_default_algorithm</varname> parameter
+        usage</title>
+
+        <programlisting format="linespecific">
+...
+modparam("auth_ims", "registration_default_algorithm", "HSS-Selected")
+...
+</programlisting>
+      </example>
+    </section>
+
+    <section>
+      <title><varname>registration_qop</varname> (string)</title>
+
+      <para>The QOP options to put in the authorisation challenges.</para>
+
+      <para>Default value of this parameter is
+      <quote>auth,auth-int</quote>.</para>
+
+      <example>
+        <title><varname>load_credentials</varname> parameter usage</title>
+
+        <programlisting format="linespecific">
+...
+modparam("auth_ims", "load_credentials", "auth-int")
+...
+</programlisting>
+      </example>
+    </section>
+
+    <section>
+      <title><varname>cxdx_forced_peer</varname> (string)</title>
+
+      <para>FQDN of Diameter Peer (HSS) to use for communication (MAR)</para>
+
+      <para>Default value is <quote/>.</para>
+
+      <example>
+        <title><varname>cxdx_forced_peer</varname> parameter usage</title>
+
+        <programlisting format="linespecific">
+...
+modparam("auth_ims", "cxdx_forced_peer", "hss.ims.smilecoms.com")
+...
+</programlisting>
+      </example>
+    </section>
+
+    <section>
+      <title><varname>cxdx_dest_realm</varname> (string)</title>
+
+      <para>Destination realm to be used in Diameter messags to HSS</para>
+
+      <para>Default value is <quote>ims.smilecoms.com</quote>.</para>
+
+      <example>
+        <title><varname>version_table</varname> parameter usage</title>
+
+        <programlisting format="linespecific">
+...
+modparam("auth_ims", "cxdx_dest_realm", "ims.smilecoms.com")
+...
+</programlisting>
+      </example>
+    </section>
+  </section>
+
+  <section>
+    <title>Functions</title>
+
+    <section>
+      <title><function moreinfo="none">ims_www_authorize(realm,
+      table)</function></title>
+
+      <para>The function verifies credentials according to <ulink
+      url="http://www.ietf.org/rfc/rfc2617.txt">RFC2617</ulink>. If the
+      credentials are verified successfully then the function will succeed and
+      mark the credentials as authorized (marked credentials can be later used
+      by some other functions). If the function was unable to verify the
+      credentials for some reason then it will fail and the script should call
+      <function moreinfo="none">www_challenge</function> which will challenge
+      the user again.</para>
+
+      <para>Negative codes may be interpreted as follows:</para>
+
+      <itemizedlist>
+        <listitem>
+          <para><emphasis>-1 (generic error)</emphasis> - some generic error
+          occurred and no reply was sent out;</para>
+        </listitem>
+
+        <listitem>
+          <para><emphasis>-2 (invalid password)</emphasis> - valid user, but
+          wrong password;</para>
+        </listitem>
+
+        <listitem>
+          <para><emphasis>-3 (invalid user)</emphasis> - authentication user
+          does not exist.</para>
+        </listitem>
+      </itemizedlist>
+
+      <para>Meaning of the parameters is as follows:</para>
+
+      <itemizedlist>
+        <listitem>
+          <para><emphasis>realm</emphasis> - Realm is a opaque string that the
+          user agent should present to the user so he can decide what username
+          and password to use. Usually this is domain of the host the server
+          is running on.</para>
+
+          <para>It must not be empty string <quote/>. In case of REGISTER
+          requests To header field domain (e.g., variable $td) can be used
+          (because this header field represents the user being registered),
+          for all other messages From header field domain can be used (e.g.,
+          variable $fd).</para>
+
+          <para>The string may contain pseudo variables.</para>
+        </listitem>
+
+        <listitem>
+          <para><emphasis>table</emphasis> - Table to be used to lookup
+          usernames and passwords (usually subscribers table).</para>
+        </listitem>
+      </itemizedlist>
+
+      <para>This function can be used from REQUEST_ROUTE.</para>
+
+      <example>
+        <title><function moreinfo="none">www_authorize</function>
+        usage</title>
+
+        <programlisting format="linespecific">
+...
+if (!www_authorize("kamailio.org", "subscriber")) {
+	www_challenge("kamailio.org", "1");
+};
+...
+</programlisting>
+      </example>
+    </section>
+
+    <section>
+      <title><function moreinfo="none">ims_www_authenticate(realm,
+      table)</function></title>
+
+      <para>It is same function as www_authenticate(realm, table). This name
+      is kept for backward compatibility, since it was named this way first
+      time by it actually does user authentication.</para>
+    </section>
+
+    <section>
+      <title><function moreinfo="none">ims_www_challenge(realm,
+      table)</function></title>
+
+      <para>Name alias: proxy_authorize(realm, table)</para>
+
+      <para>The function verifies credentials according to <ulink
+      url="http://www.ietf.org/rfc/rfc2617.txt">RFC2617</ulink>. If the
+      credentials are verified successfully then the function will succeed and
+      mark the credentials as authorized (marked credentials can be later used
+      by some other functions). If the function was unable to verify the
+      credentials for some reason then it will fail and the script should call
+      <function moreinfo="none">proxy_challenge</function> which will
+      challenge the user again.</para>
+
+      <para>Negative return codes have the same meaning as for
+      www_authenticate().</para>
+
+      <para>Meaning of the parameters is as follows:</para>
+
+      <itemizedlist>
+        <listitem>
+          <para><emphasis>realm</emphasis> - Realm is a opaque string that the
+          user agent should present to the user so he can decide what username
+          and password to use. Usually this is domain of the host the server
+          is running on.</para>
+
+          <para>It must not be empty string <quote/>. Apart of a static
+          strinh, typical value is From header field domain (e.g., variable
+          $fd).</para>
+
+          <para>If an empty string <quote/> is used then the server will
+          generate it from the request. From header field domain will be used
+          as realm.</para>
+
+          <para>The string may contain pseudo variables.</para>
+        </listitem>
+
+        <listitem>
+          <para><emphasis>table</emphasis> - Table to be used to lookup
+          usernames and passwords (usually subscribers table).</para>
+        </listitem>
+      </itemizedlist>
+
+      <para>This function can be used from REQUEST_ROUTE.</para>
+
+      <example>
+        <title>proxy_authorize usage</title>
+
+        <programlisting format="linespecific">
+...
+if (!proxy_authorize("$fd", "subscriber)) {
+	proxy_challenge("$fd", "1");  # Realm will be autogenerated
+};
+...
+</programlisting>
+      </example>
+    </section>
+
+    <section>
+      <title><function moreinfo="none">ims_proxy_challenge(realm,
+      table)</function></title>
+
+      <para>Name alias: proxy_authorize(realm, table)</para>
+
+      <para>The function verifies credentials according to <ulink
+      url="http://www.ietf.org/rfc/rfc2617.txt">RFC2617</ulink>. If the
+      credentials are verified successfully then the function will succeed and
+      mark the credentials as authorized (marked credentials can be later used
+      by some other functions). If the function was unable to verify the
+      credentials for some reason then it will fail and the script should call
+      <function moreinfo="none">proxy_challenge</function> which will
+      challenge the user again.</para>
+
+      <para>Negative return codes have the same meaning as for
+      www_authenticate().</para>
+
+      <para>Meaning of the parameters is as follows:</para>
+
+      <itemizedlist>
+        <listitem>
+          <para><emphasis>realm</emphasis> - Realm is a opaque string that the
+          user agent should present to the user so he can decide what username
+          and password to use. Usually this is domain of the host the server
+          is running on.</para>
+
+          <para>It must not be empty string <quote/>. Apart of a static
+          strinh, typical value is From header field domain (e.g., variable
+          $fd).</para>
+
+          <para>If an empty string <quote/> is used then the server will
+          generate it from the request. From header field domain will be used
+          as realm.</para>
+
+          <para>The string may contain pseudo variables.</para>
+        </listitem>
+
+        <listitem>
+          <para><emphasis>table</emphasis> - Table to be used to lookup
+          usernames and passwords (usually subscribers table).</para>
+        </listitem>
+      </itemizedlist>
+
+      <para>This function can be used from REQUEST_ROUTE.</para>
+
+      <example>
+        <title>proxy_authorize usage</title>
+
+        <programlisting format="linespecific">
+...
+if (!proxy_authorize("$fd", "subscriber)) {
+	proxy_challenge("$fd", "1");  # Realm will be autogenerated
+};
+...
+</programlisting>
+      </example>
+    </section>
+
+    <section>
+      <title><function moreinfo="none">ims_proxy_authenticate(realm,
+      table)</function></title>
+
+      <para>It is same function as proxy_authenticate(realm, table). This name
+      is kept for backward compatibility, since it was named this way first
+      time but it actually does user authentication.</para>
+    </section>
+  </section>
+
+  <section>
+    <title>Statistics</title>
+
+    <section>
+      <title>MAR Timeouts (mar_timeouts)</title>
+
+      <para>The number of timeouts on sending a MAR. i.e. no response to
+      MAR.</para>
+    </section>
+
+    <section>
+      <title>Average MAR Response Time (mar_avg_response_time)</title>
+
+      <para>The average response time in milliseconds for MAR-MAA
+      transaction.</para>
+    </section>
+  </section>
+</chapter>

+ 189 - 0
modules/auth_ims/rfc2617.c

@@ -0,0 +1,189 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * 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
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * 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
+ * 
+ */
+
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "rfc2617.h"
+#include "../../md5.h"
+#include "../../dprint.h"
+
+
+inline void cvt_hex(HASH _b, HASHHEX _h)
+{
+	unsigned short i;
+	unsigned char j;
+	
+	for (i = 0; i < HASHLEN; i++) {
+		j = (_b[i] >> 4) & 0xf;
+		if (j <= 9) {
+			_h[i * 2] = (j + '0');
+		} else {
+			_h[i * 2] = (j + 'a' - 10);
+		}
+
+		j = _b[i] & 0xf;
+
+		if (j <= 9) {
+			_h[i * 2 + 1] = (j + '0');
+		} else {
+			_h[i * 2 + 1] = (j + 'a' - 10);
+		}
+	};
+
+	_h[HASHHEXLEN] = '\0';
+}
+
+
+/* 
+ * calculate H(A1) as per spec 
+ */
+void calc_HA1(ha_alg_t _alg, str* _username, str* _realm, str* _password,
+	      str* _nonce, str* _cnonce, HASHHEX _sess_key)
+{
+	MD5_CTX Md5Ctx;
+	HASH HA1;
+	
+	MD5Init(&Md5Ctx);
+	MD5Update(&Md5Ctx, _username->s, _username->len);
+	MD5Update(&Md5Ctx, ":", 1);
+	MD5Update(&Md5Ctx, _realm->s, _realm->len);
+	MD5Update(&Md5Ctx, ":", 1);
+	MD5Update(&Md5Ctx, _password->s, _password->len);
+	MD5Final(HA1, &Md5Ctx);
+
+	if (_alg == HA_MD5_SESS) {
+		MD5Init(&Md5Ctx);
+		MD5Update(&Md5Ctx, HA1, HASHLEN);
+		MD5Update(&Md5Ctx, ":", 1);
+		MD5Update(&Md5Ctx, _nonce->s, _nonce->len);
+		MD5Update(&Md5Ctx, ":", 1);
+		MD5Update(&Md5Ctx, _cnonce->s, _cnonce->len);
+		MD5Final(HA1, &Md5Ctx);
+	};
+
+	cvt_hex(HA1, _sess_key);
+}
+
+void calc_H(str *ent, HASHHEX hash)
+{
+	MD5_CTX Md5Ctx;
+	HASH HA1;
+	MD5Init(&Md5Ctx);
+	MD5Update(&Md5Ctx, ent->s, ent->len);
+	MD5Final(HA1, &Md5Ctx);
+	cvt_hex(HA1, hash);
+}
+
+
+/* 
+ * calculate request-digest/response-digest as per HTTP Digest spec 
+ */
+void calc_response(HASHHEX _ha1,      /* H(A1) */
+		   str* _nonce,       /* nonce from server */
+		   str* _nc,          /* 8 hex digits */
+		   str* _cnonce,      /* client nonce */
+		   str* _qop,         /* qop-value: "", "auth", "auth-int" */
+		   int _auth_int,     /* 1 if auth-int is used */
+		   str* _method,      /* method from the request */
+		   str* _uri,         /* requested URL */
+		   HASHHEX _hentity,  /* H(entity body) if qop="auth-int" */
+		   HASHHEX _response) /* request-digest or response-digest */
+{
+	LM_DBG("calc_response(_ha1=%.*s, _nonce=%.*s, _nc=%.*s,_cnonce=%.*s, _qop=%.*s, _auth_int=%d,_method=%.*s,_uri=%.*s,_hentity=%.*s)\n",
+			HASHHEXLEN, _ha1,
+			_nonce->len, _nonce->s,
+			_nc->len, _nc->s,
+			_cnonce->len, _cnonce->s,
+			_qop->len, _qop->s,
+			_auth_int,
+			_method ? _method->len : 4, _method ? _method->s : "null",
+			_uri->len, _uri->s,
+			_auth_int ? HASHHEXLEN : 0, _hentity);
+
+	MD5_CTX Md5Ctx;
+	HASH HA2;
+	HASH RespHash;
+	HASHHEX HA2Hex;
+	
+	     /* calculate H(A2) */
+	MD5Init(&Md5Ctx);
+	if (_method) { /* _method is NULL when calculating H(A2) for rspauth in Authentication-Info */
+		MD5Update(&Md5Ctx, _method->s, _method->len);
+	}
+	MD5Update(&Md5Ctx, ":", 1);
+	MD5Update(&Md5Ctx, _uri->s, _uri->len);
+
+	if (_auth_int) {
+		MD5Update(&Md5Ctx, ":", 1);
+		MD5Update(&Md5Ctx, _hentity, HASHHEXLEN);
+	};
+
+	MD5Final(HA2, &Md5Ctx);
+	cvt_hex(HA2, HA2Hex);
+
+	     /* calculate response */
+	MD5Init(&Md5Ctx);
+	MD5Update(&Md5Ctx, _ha1, HASHHEXLEN);
+	MD5Update(&Md5Ctx, ":", 1);
+	MD5Update(&Md5Ctx, _nonce->s, _nonce->len);
+	MD5Update(&Md5Ctx, ":", 1);
+
+	if (_qop->len) {
+		MD5Update(&Md5Ctx, _nc->s, _nc->len);
+		MD5Update(&Md5Ctx, ":", 1);
+		MD5Update(&Md5Ctx, _cnonce->s, _cnonce->len);
+		MD5Update(&Md5Ctx, ":", 1);
+		MD5Update(&Md5Ctx, _qop->s, _qop->len);
+		MD5Update(&Md5Ctx, ":", 1);
+	};
+	MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN);
+	MD5Final(RespHash, &Md5Ctx);
+	cvt_hex(RespHash, _response);
+	LM_DBG("H(A1) = %.*s, H(A2) = %.*s, rspauth = %.*s\n",
+			HASHHEXLEN, _ha1, HASHHEXLEN, HA2Hex, HASHHEXLEN, _response);
+}

+ 119 - 0
modules/auth_ims/rfc2617.h

@@ -0,0 +1,119 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * 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
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * 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
+ * 
+ */
+
+
+#ifndef RFC2617_H
+#define RFC2617_H
+
+#include "../../str.h"
+
+
+#define HASHLEN 16
+typedef char HASH[HASHLEN];
+
+
+#define HASHHEXLEN 32
+typedef char HASHHEX[HASHHEXLEN+1];
+
+
+/*
+ * Type of algorithm used
+ */
+typedef enum {
+	HA_MD5,      /* Plain MD5 */
+	HA_MD5_SESS, /* MD5-Session */
+} ha_alg_t;
+
+
+/*
+ * Convert to hex form
+ */
+void cvt_hex(HASH Bin, HASHHEX Hex);
+
+
+/* 
+ * calculate H(A1) as per HTTP Digest spec 
+ */
+typedef void (*calc_HA1_t)(ha_alg_t _alg,      /* Type of algorithm */
+	      str* _username,     /* username */
+	      str* _realm,        /* realm */
+	      str* _password,     /* password */
+	      str* _nonce,        /* nonce string */
+	      str* _cnonce,       /* cnonce */
+	      HASHHEX _sess_key); /* Result will be stored here */
+void calc_HA1(ha_alg_t _alg,      /* Type of algorithm */
+	      str* _username,     /* username */
+	      str* _realm,        /* realm */
+	      str* _password,     /* password */
+	      str* _nonce,        /* nonce string */
+	      str* _cnonce,       /* cnonce */
+	      HASHHEX _sess_key); /* Result will be stored here */
+
+void calc_H(str *ent, HASHHEX hash);
+
+/* calculate request-digest/response-digest as per HTTP Digest spec */
+typedef void (*calc_response_t)(HASHHEX _ha1,       /* H(A1) */
+		   str* _nonce,        /* nonce from server */
+		   str* _nc,           /* 8 hex digits */
+		   str* _cnonce,       /* client nonce */
+		   str* _qop,          /* qop-value: "", "auth", "auth-int" */
+		   int _auth_int,      /* 1 if auth-int is used */
+		   str* _method,       /* method from the request */
+		   str* _uri,          /* requested URL */
+		   HASHHEX _hentity,   /* H(entity body) if qop="auth-int" */
+		   HASHHEX _response); /* request-digest or response-digest */
+void calc_response(HASHHEX _ha1,       /* H(A1) */
+		   str* _nonce,        /* nonce from server */
+		   str* _nc,           /* 8 hex digits */
+		   str* _cnonce,       /* client nonce */
+		   str* _qop,          /* qop-value: "", "auth", "auth-int" */
+		   int _auth_int,      /* 1 if auth-int is used */
+		   str* _method,       /* method from the request */
+		   str* _uri,          /* requested URL */
+		   HASHHEX _hentity,   /* H(entity body) if qop="auth-int" */
+		   HASHHEX _response); /* request-digest or response-digest */
+
+
+#endif /* RFC2617_H */

+ 83 - 0
modules/auth_ims/sip_messages.h

@@ -0,0 +1,83 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * 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
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * 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
+ * 
+ */
+
+
+#ifndef S_CSCF_SIP_MESSAGES_H_
+#define S_CSCF_SIP_MESSAGES_H_
+
+
+#define MSG_200_SAR_OK					"OK - SAR succesful and registrar saved"
+
+#define MSG_400_BAD_REQUEST				"Bad Request"
+#define MSG_400_BAD_Contact 			"Bad Request - Error parsing Contact header" 
+
+#define MSG_401_CHALLENGE				"Unauthorized - Challenging the UE"
+#define MSG_407_CHALLENGE				"Unauthorized - Challenging the UE"
+
+#define MSG_403_NO_PRIVATE				"Forbidden - Private identity not found (Authorization: username)"
+#define MSG_403_NO_PUBLIC				"Forbidden - Public identity not found (To:)"
+#define MSG_403_NO_NONCE				"Forbidden - Nonce not found (Authorization: nonce)"
+#define MSG_403_UNKOWN_RC				"Forbidden - HSS responded with unknown Result Code"
+#define MSG_403_UNKOWN_EXPERIMENTAL_RC	"Forbidden - HSS responded with unknown Experimental Result Code"
+#define MSG_403_USER_UNKNOWN			"Forbidden - HSS User Unknown"
+#define MSG_403_IDENTITIES_DONT_MATCH	"Forbidden - HSS Identities don't match"
+#define MSG_403_AUTH_SCHEME_UNSOPPORTED "Forbidden - HSS Authentication Scheme Unsupported"
+#define MSG_403_UNABLE_TO_COMPLY		"Forbidden - HSS Unable to comply"
+#define MSG_403_NO_AUTH_DATA			"Forbidden - HSS returned no authentication vectors"
+
+#define MSG_423_INTERVAL_TOO_BRIEF 		"Interval too brief"
+
+#define MSG_480_HSS_ERROR 				"Temporarily unavailable - error retrieving av"
+#define MSG_480_DIAMETER_ERROR			"Temporarily Unavailable - Diameter Cx interface failed"
+#define MSG_480_DIAMETER_TIMEOUT		"Temporarily unavailable - TimeOut in MAR/A HSS"
+#define MSG_480_DIAMETER_TIMEOUT_SAR	"Temporarily unavailable - TimeOut in SAR/A HSS"
+#define MSG_480_DIAMETER_MISSING_AVP	"Temporarily unavailable - Missing AVP in UAA from HSS"
+
+#define MSG_500_PACK_AV					"Server Internal Error - while packing auth vectors"
+#define MSG_500_SAR_FAILED				"Server Internal Error - Server Assignment failed"
+#define MSG_500_UPDATE_CONTACTS_FAILED	"Server Internal Error - Update Contacts failed"
+#define MSG_514_HSS_AUTH_FAILURE		"HSS unauthenticated - did not provide the right H(A1) in MAA"
+
+#endif //S_CSCF_SIP_MESSAGES_H_

+ 71 - 0
modules/auth_ims/stats.c

@@ -0,0 +1,71 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * 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
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * 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
+ * 
+ */
+
+#include "stats.h"
+
+int register_stats() {
+	//MAR
+	if (register_stat(MOD_NAME, "mar_replies_response_time", &mar_replies_response_time,0 )
+			!= 0) {
+		LM_ERR("failed to register stat\n");
+		return -1;
+	}
+	if (register_stat(MOD_NAME, "mar_replies_received", &mar_replies_received, 0)
+			!= 0) {
+		LM_ERR("failed to register stat\n");
+		return -1;
+	}
+
+	return 1;
+}
+
+unsigned long get_avg_mar_response_time() {
+
+	long rpls_received = get_stat_val(mar_replies_received);
+	if (!rpls_received)
+		return 0;
+
+	return get_stat_val(mar_replies_response_time)/rpls_received;
+}

+ 60 - 0
modules/auth_ims/stats.h

@@ -0,0 +1,60 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * 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
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * 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
+ * 
+ */
+
+#ifndef STATISTICS_H_
+#define STATISTICS_H_
+
+#include "../../lib/kcore/statistics.h"
+#include "authims_mod.h"
+
+stat_var* stat_mar_timeouts;
+stat_var* mar_replies_received;
+stat_var* mar_replies_response_time;
+
+int register_stats();
+unsigned long get_avg_mar_response_time();
+
+
+#endif /* STATISTICS_H_ */

+ 404 - 0
modules/auth_ims/utils.c

@@ -0,0 +1,404 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * 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
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * 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
+ * 
+ */
+
+
+#include "utils.h"
+
+/*
+ * Find credentials with given realm in a SIP message header
+ */
+inline int ims_find_credentials(struct sip_msg* _m, str* _realm,
+		hdr_types_t _hftype, struct hdr_field** _h) {
+	struct hdr_field** hook, *ptr, *prev;
+	hdr_flags_t hdr_flags;
+	int res;
+	str* r;
+
+	LM_DBG("Searching credentials in realm [%.*s]\n", _realm->len, _realm->s);
+
+	/*
+	 * Determine if we should use WWW-Authorization or
+	 * Proxy-Authorization header fields, this parameter
+	 * is set in www_authorize and proxy_authorize
+	 */
+	switch (_hftype) {
+	case HDR_AUTHORIZATION_T:
+		hook = &(_m->authorization);
+		hdr_flags = HDR_AUTHORIZATION_F;
+		break;
+	case HDR_PROXYAUTH_T:
+		hook = &(_m->proxy_auth);
+		hdr_flags = HDR_PROXYAUTH_F;
+		break;
+	default:
+		hook = &(_m->authorization);
+		hdr_flags = HDR_T2F(_hftype);
+		break;
+	}
+
+	/*
+	 * If the credentials haven't been parsed yet, do it now
+	 */
+	if (*hook == 0) {
+		/* No credentials parsed yet */
+		LM_DBG("*hook == 0, No credentials parsed yet\n");
+		if (parse_headers(_m, hdr_flags, 0) == -1) {
+			LM_ERR("Error while parsing headers\n");
+			return -1;
+		}
+	}
+
+	ptr = *hook;
+	LM_DBG("*hook = %p\n", ptr);
+	/*
+	 * Iterate through the credentials in the message and
+	 * find credentials with given realm
+	 */
+	while (ptr) {
+		res = parse_credentials(ptr);
+		if (res < 0) {
+			LM_ERR("Error while parsing credentials\n");
+			return (res == -1) ? -2 : -3;
+		} else if (res == 0) {
+			LM_DBG("Credential parsed successfully\n");
+			if (_realm->len) {
+				r = &(((auth_body_t*) (ptr->parsed))->digest.realm);
+				LM_DBG("Comparing realm <%.*s> and <%.*s>\n", _realm->len, _realm->s, r->len, r->s);
+				if (r->len == _realm->len) {
+					if (!strncasecmp(_realm->s, r->s, r->len)) {
+						*_h = ptr;
+						return 0;
+					}
+				}
+			} else {
+				*_h = ptr;
+				return 0;
+			}
+
+		}
+
+		prev = ptr;
+		if (parse_headers(_m, hdr_flags, 1) == -1) {
+			LM_ERR("Error while parsing headers\n");
+			return -4;
+		} else {
+			if (prev != _m->last_header) {
+				if (_m->last_header->type == _hftype)
+					ptr = _m->last_header;
+				else
+					break;
+			} else
+				break;
+		}
+	}
+
+	/*
+	 * Credentials with given realm not found
+	 */
+	LM_DBG("Credentials with given realm not found\n");
+	return 1;
+}
+
+
+/**
+ * Returns the Private Identity extracted from the Authorization header.
+ * If none found there takes the SIP URI in To without the "sip:" prefix
+ * \todo - remove the fallback case to the To header
+ * @param msg - the SIP message
+ * @param realm - the realm to match in an Authorization header
+ * @param is_proxy_auth 0 if the header is Authorization, anything else for Proxy-Authorization
+ * @returns the str containing the private id, no mem dup
+ */
+str get_private_identity(struct sip_msg *msg, str realm, int is_proxy_auth)
+{
+	str pi={0,0};
+	struct hdr_field* h=0;
+	int ret,i;
+
+	if (parse_headers(msg, is_proxy_auth ? HDR_PROXYAUTH_F : HDR_AUTHORIZATION_F,0)!=0) {
+		return pi;
+	}
+
+	if (!(is_proxy_auth ? msg->proxy_auth : msg->authorization)){
+		goto fallback;
+	}
+
+	ret = find_credentials(msg, &realm, is_proxy_auth ? HDR_PROXYAUTH_F : HDR_AUTHORIZATION_F, &h);
+	if (ret < 0) {
+		goto fallback;
+	} else 
+		if (ret > 0) {
+			goto fallback;
+		}
+	
+	if (h) pi=((auth_body_t*)h->parsed)->digest.username.whole;
+
+	goto done;
+		
+fallback:
+	pi = get_public_identity(msg);
+	if (pi.len>4&&strncasecmp(pi.s,"sip:",4)==0) {pi.s+=4;pi.len-=4;}
+	for(i=0;i<pi.len;i++)
+		if (pi.s[i]==';') {
+			pi.len=i;
+			break;
+		}
+done:	
+	return pi;	
+}
+
+/**
+ * Returns the Public Identity extracted from the From header
+ * @param msg - the SIP message
+ * @returns the str containing the public id, no mem dup
+ */
+str get_public_identity(struct sip_msg *msg)
+{
+	str pu={0,0};
+	struct to_body *from;
+	int i;
+	
+	if (parse_headers(msg,HDR_TO_F,0)!=0) {
+		return pu;
+	}
+	
+	if ( get_from(msg) == NULL ) {
+		from = (struct to_body*) pkg_malloc(sizeof(struct to_body));
+		if (!from) {
+			LM_ERR("out of pkg memory\n");
+			return pu;
+		}
+		parse_to( msg->from->body.s, msg->from->body.s + msg->from->body.len, from );
+        msg->from->parsed = from;
+	}
+	else from=(struct to_body *) msg->from->parsed;
+
+	pu = from->uri;
+	
+	/* truncate to sip:username@host or tel:number */
+	for(i=4;i<pu.len;i++)
+		if (pu.s[i]==';' || pu.s[i]=='?' ||pu.s[i]==':'){
+			pu.len = i;
+		}
+	return pu;
+}
+
+/**
+ * Looks for the nonce and response parameters in the Authorization header and returns them
+ * @param msg - the SIP message
+ * @param realm - realm to match the right Authorization header
+ * @param nonce - param to fill with the nonce found
+ * @param response - param to fill with the response
+ * @returns 1 if found, 0 if not
+ */
+int get_nonce_response(struct sip_msg *msg, str realm,str *nonce,str *response,
+	enum qop_type *qop,str *qop_str,str *nc,str *cnonce,str *uri, int is_proxy_auth)
+{
+	struct hdr_field* h = 0;
+	int ret;
+
+
+	ret = parse_headers(msg, is_proxy_auth ? HDR_PROXYAUTH_F : HDR_AUTHORIZATION_F, 0);
+
+	if (ret != 0) {
+		return 0;
+	}
+
+	if ((!is_proxy_auth && !msg->authorization)
+		|| (is_proxy_auth && !msg->proxy_auth)) {
+		return 0;
+	}
+
+	LM_DBG("Calling find_credentials with realm [%.*s]\n", realm.len, realm.s);
+	ret = ims_find_credentials(msg, &realm, is_proxy_auth ? HDR_PROXYAUTH_T : HDR_AUTHORIZATION_T, &h);
+	if (ret < 0) {
+		return 0;
+	} else if (ret > 0) {
+		LM_DBG("ret > 0");
+		return 0;
+	}
+
+	if (h && h->parsed) {
+		if (nonce)
+			*nonce = ((auth_body_t*) h->parsed)->digest.nonce;
+		if (response)
+			*response = ((auth_body_t*) h->parsed)->digest.response;
+		if (qop)
+			*qop = ((auth_body_t*) h->parsed)->digest.qop.qop_parsed;
+		if (qop_str)
+			*qop_str = ((auth_body_t*) h->parsed)->digest.qop.qop_str;
+		if (nc)
+			*nc = ((auth_body_t*) h->parsed)->digest.nc;
+		if (cnonce)
+			*cnonce = ((auth_body_t*) h->parsed)->digest.cnonce;
+		if (uri)
+			*uri = ((auth_body_t*) h->parsed)->digest.uri;
+	}
+	LM_DBG("Found nonce response\n");
+	return 1;
+}
+
+str ims_get_body(struct sip_msg * msg)
+{		
+	str x={0,0};
+	x.s = get_body(msg);	
+	if (x.s==0) return x;
+	if (parse_headers(msg,HDR_CONTENTLENGTH_F,0)!=0) {
+		LM_DBG("Error parsing until header Content-Length: \n");
+		return x;
+	}
+	if  (msg->content_length->parsed==NULL) {
+		LM_ERR(" body <%.*s>\n",msg->content_length->body.len,msg->content_length->body.s);
+		parse_content_length(msg->content_length->body.s,
+			msg->content_length->body.s+msg->content_length->body.len,&(x.len));
+		msg->content_length->parsed=(void*)(long)(x.len);
+	}else 
+		x.len = (long)msg->content_length->parsed;
+	return x;
+}
+
+
+/**
+ * Looks for the auts parameter in the Authorization header and returns its value.
+ * @param msg - the SIP message
+ * @param realm - realm to match the right Authorization header
+ * @returns the auts value or an empty string if not found
+ */
+str ims_get_auts(struct sip_msg *msg, str realm, int is_proxy_auth)
+{
+	str name={"auts=\"",6};
+	struct hdr_field* h=0;
+	int i,ret;
+	str auts={0,0};
+
+	if (parse_headers(msg, is_proxy_auth ? HDR_PROXYAUTH_F : HDR_AUTHORIZATION_F,0)!=0) {
+		LM_ERR("Error parsing until header Authorization: \n");
+		return auts;
+	}
+
+	if ((!is_proxy_auth && !msg->authorization)
+			|| (is_proxy_auth && !msg->proxy_auth)){
+		LM_ERR("Message does not contain Authorization nor Proxy-Authorization header.\n");
+		return auts;
+	}
+
+	ret = find_credentials(msg, &realm, is_proxy_auth ? HDR_PROXYAUTH_F : HDR_AUTHORIZATION_F, &h);
+	if (ret < 0) {
+		LM_ERR("Error while looking for credentials.\n");
+		return auts;
+	} else 
+		if (ret > 0) {
+			LM_ERR("No credentials for this realm found.\n");
+			return auts;
+		}
+	
+	if (h) {
+		for(i=0;i<h->body.len-name.len;i++)
+			if (strncasecmp(h->body.s+i,name.s,name.len)==0){
+				auts.s = h->body.s+i+name.len;
+				while(i+auts.len<h->body.len && auts.s[auts.len]!='\"')
+					auts.len++;
+			}
+	}
+	
+	return auts;	
+}
+
+/**
+ * Looks for the nonce parameter in the Authorization header and returns its value.
+ * @param msg - the SIP message
+ * @param realm - realm to match the right Authorization header
+ * @returns the nonce or an empty string if none found
+ */
+str ims_get_nonce(struct sip_msg *msg, str realm)
+{
+	struct hdr_field* h=0;
+	int ret;
+	str nonce={0,0};
+
+	if (parse_headers(msg,HDR_AUTHORIZATION_F,0)!=0) {
+		LM_ERR("Error parsing until header Authorization: \n");
+		return nonce;
+	}
+
+	if (!msg->authorization){
+		LM_ERR("Message does not contain Authorization header.\n");
+		return nonce;
+	}
+
+	ret = find_credentials(msg, &realm, HDR_AUTHORIZATION_F, &h);
+	if (ret < 0) {
+		LM_ERR("Error while looking for credentials.\n");
+		return nonce;
+	} else 
+		if (ret > 0) {
+			LM_ERR("No credentials for this realm found.\n");
+			return nonce;
+		}
+	
+	if (h&&h->parsed) {
+		nonce = ((auth_body_t*)h->parsed)->digest.nonce;
+	}
+	
+	return nonce;	
+}
+
+/**
+ * Adds a header to the reply message
+ * @param msg - the request to add a header to its reply
+ * @param content - the str containing the new header
+ * @returns 1 on succes, 0 on failure
+ */
+int ims_add_header_rpl(struct sip_msg *msg, str *hdr)
+{
+	if (add_lump_rpl( msg, hdr->s, hdr->len, LUMP_RPL_HDR)==0) {
+		LM_ERR("Can't add header <%.*s>\n",
+			hdr->len,hdr->s);
+ 		return 0;
+ 	}
+ 	return 1;
+}
+
+

+ 67 - 0
modules/auth_ims/utils.h

@@ -0,0 +1,67 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * 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
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * 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
+ * 
+ */
+
+#ifndef UTILS_H
+#define	UTILS_H
+
+#include "../../parser/hf.h"
+#include "../../parser/parse_to.h"
+#include "../../parser/parse_from.h"
+#include "../../parser/digest/digest.h"
+#include "../../parser/digest/digest_parser.h"
+#include "../../parser/parse_content.h"
+#include "../../data_lump_rpl.h"
+
+str get_private_identity(struct sip_msg *msg, str realm, int is_proxy_auth);
+str get_public_identity(struct sip_msg *msg);
+int get_nonce_response(struct sip_msg *msg, str realm,str *nonce,str *response,
+                enum qop_type *qop,str *qop_str,str *nc,str *cnonce,str *uri, int is_proxy_auth);
+str ims_get_body(struct sip_msg * msg);
+str ims_get_auts(struct sip_msg *msg, str realm, int is_proxy_auth);
+str ims_get_nonce(struct sip_msg *msg, str realm);
+int ims_add_header_rpl(struct sip_msg *msg, str *hdr);
+
+#endif	/* UTILS_H */
+