Browse Source

modules/auth_ephemeral: Added MI commands for shared secret management

- Can add, remove, and display shared secrets with MI commands
- This means you can add/revoke secrets without a restart
Peter Dunkley 12 năm trước cách đây
mục cha
commit
fd9fcd75c2

+ 74 - 0
modules/auth_ephemeral/README

@@ -39,6 +39,12 @@ Peter Dunkley
               4.6. autheph_check_to([username])
               4.7. autheph_check_timestamp(username)
 
+        5. MI Commands
+
+              5.1. autheph.add_secret
+              5.2. autheph.dump_secrets
+              5.3. autheph.rm_secret
+
    List of Examples
 
    1.1. Request example
@@ -84,6 +90,12 @@ Chapter 1. Admin Guide
         4.6. autheph_check_to([username])
         4.7. autheph_check_timestamp(username)
 
+   5. MI Commands
+
+        5.1. autheph.add_secret
+        5.2. autheph.dump_secrets
+        5.3. autheph.rm_secret
+
 1. Overview
 
    1.1. How ephemeral credentials work
@@ -421,3 +433,65 @@ if (!autheph_check_timestamp("$var(username)")) {
     exit;
 }
 ...
+
+5. MI Commands
+
+   5.1. autheph.add_secret
+   5.2. autheph.dump_secrets
+   5.3. autheph.rm_secret
+
+5.1. autheph.add_secret
+
+   Add a secret to the head of the shared secret list. The secret will be
+   the first one tried during future authentication attempts. This MI
+   command allows you to update the shared secret list without having to
+   restart Kamailio.
+
+Note
+
+   If you want your new shared secret list to persist across restarts you
+   must add it to your Kamailio configuration file.
+
+   Name: autheph.add_secret
+
+   Parameters:
+     * secret
+
+   MI FIFO Command Format:
+                        :autheph.add_secret:fifo_reply
+                        kamailio_rules
+                        _empty_line_
+
+5.2. autheph.dump_secrets
+
+   Dump the set of shared secrets.
+
+   Name: autheph.dump_secrets
+
+   Parameters:
+     * none
+
+   MI FIFO Command Format:
+                        :autheph.dump_secrets:fifo_reply
+                        _empty_line_
+
+5.3. autheph.rm_secret
+
+   Remove the secret with the specified integer ID. This MI command allows
+   you to update the shared secret list without having to restart
+   Kamailio.
+
+Note
+
+   If you want your new shared secret list to persist across restarts you
+   must add it to your Kamailio configuration file.
+
+   Name: autheph.rm_secret
+
+   Parameters:
+     * ID - the ID of the secret to remove
+
+   MI FIFO Command Format:
+                        :autheph.rm_secret:fifo_reply
+                        0
+                        _empty_line_

+ 248 - 9
modules/auth_ephemeral/autheph_mod.c

@@ -21,9 +21,11 @@
  *
  */
 #include "../../dprint.h"
+#include "../../locking.h"
 #include "../../mod_fix.h"
 #include "../../sr_module.h"
 #include "../../str.h"
+#include "../../lib/kmi/mi.h"
 #include "../../modules/auth/api.h"
 
 #include "autheph_mod.h"
@@ -37,6 +39,10 @@ static void destroy(void);
 
 static int secret_param(modparam_t _type, void *_val);
 struct secret *secret_list = NULL;
+static struct mi_root *mi_dump_secrets(struct mi_root *cmd, void *param);
+static struct mi_root *mi_add_secret(struct mi_root *cmd, void *param);
+static struct mi_root *mi_rm_secret(struct mi_root *cmd, void *param);
+gen_lock_t *autheph_secret_lock = NULL;
 
 autheph_username_format_t autheph_username_format = AUTHEPH_USERNAME_IETF;
 
@@ -86,6 +92,15 @@ static param_export_t params[]=
 	{0, 0, 0}
 };
 
+static mi_export_t mi_cmds[] =
+{
+	{ "autheph.add_secret",		mi_add_secret,	0, 0, 0 },
+	{ "autheph.dump_secrets",	mi_dump_secrets,0, 0, 0 },
+	{ "autheph.rm_secret",		mi_rm_secret,	0, 0, 0 },
+
+	{ 0, 0, 0, 0, 0 }
+};
+
 struct module_exports exports=
 {
 	"auth_ephemeral", 
@@ -93,7 +108,7 @@ struct module_exports exports=
 	cmds,			/* Exported functions */
 	params,			/* Exported parameters */
 	0,			/* exported statistics */
-	0,			/* exported MI functions */
+	mi_cmds,		/* exported MI functions */
 	0,			/* exported pseudo-variables */
 	0,			/* extra processes */
 	mod_init,		/* module initialization function */
@@ -106,6 +121,12 @@ static int mod_init(void)
 {
 	bind_auth_s_t bind_auth;
 
+	if (register_mi_mod(exports.name, mi_cmds) != 0)
+	{
+		LM_ERR("registering MI commands\n");
+		return -1;
+	}
+
 	if (secret_list == NULL)
 	{
 		LM_ERR("secret modparam not set\n");
@@ -153,16 +174,28 @@ static void destroy(void)
 {
 	struct secret *secret_struct;
 
-	while (secret_list != NULL)
+	if (secret_list != NULL)
 	{
-		secret_struct = secret_list;
-		secret_list = secret_struct->next;
-
-		if (secret_struct->secret_key.s != NULL)
+		SECRET_UNLOCK;
+		SECRET_LOCK;
+		while (secret_list != NULL)
 		{
-			shm_free(secret_struct->secret_key.s);
+			secret_struct = secret_list;
+			secret_list = secret_struct->next;
+
+			if (secret_struct->secret_key.s != NULL)
+			{
+				shm_free(secret_struct->secret_key.s);
+			}
+			shm_free(secret_struct);
 		}
-		shm_free(secret_struct);
+		SECRET_UNLOCK;
+	}
+
+	if (autheph_secret_lock != NULL)
+	{
+		lock_destroy(autheph_secret_lock);
+		lock_dealloc((void *) autheph_secret_lock);
 	}
 }
 
@@ -170,6 +203,21 @@ static inline int add_secret(str _secret_key)
 {
 	struct secret *secret_struct;
 
+	if (autheph_secret_lock == NULL)
+	{
+		autheph_secret_lock = lock_alloc();
+		if (autheph_secret_lock == NULL)
+		{
+			LM_ERR("allocating lock\n");
+			return -1;
+		}
+		if (lock_init(autheph_secret_lock) == 0)
+		{
+			LM_ERR("initialising lock\n");
+			return -1;
+		}
+	}
+
 	secret_struct = (struct secret *) shm_malloc(sizeof(struct secret));
 	if (secret_struct == NULL)
 	{
@@ -178,13 +226,64 @@ static inline int add_secret(str _secret_key)
 	}
 
 	memset(secret_struct, 0, sizeof (struct secret));
+	secret_struct->secret_key = _secret_key;
+	SECRET_LOCK;
+	if (secret_list != NULL)
+	{
+		secret_list->prev = secret_struct;
+	}
 	secret_struct->next = secret_list;
 	secret_list = secret_struct;
-	secret_struct->secret_key = _secret_key;
+	SECRET_UNLOCK;
 
 	return 0;
 }
 
+static inline int rm_secret(int _id)
+{
+	int pos = 0;
+	struct secret *secret_struct;
+
+	if (secret_list == NULL)
+	{
+		LM_ERR("secret list empty\n");
+		return -1;
+	}
+
+	SECRET_LOCK;
+	secret_struct = secret_list;
+	while (pos <= _id && secret_struct != NULL)
+	{
+		if (pos == _id)
+		{
+			if (secret_struct->prev != NULL)
+			{
+				secret_struct->prev->next = secret_struct->next;
+			}
+			if (secret_struct->next != NULL)
+			{
+				secret_struct->next->prev = secret_struct->prev;
+			}
+			if (pos == 0)
+			{
+				secret_list = secret_struct->next;
+			}
+			SECRET_UNLOCK;
+			shm_free(secret_struct->secret_key.s);
+			shm_free(secret_struct);
+			return 0;
+		}
+
+		pos++;
+		secret_struct = secret_struct->next;
+
+	}
+	SECRET_UNLOCK;
+
+	LM_ERR("ID %d not found\n", _id);
+	return -1;
+}
+
 static int secret_param(modparam_t _type, void *_val)
 {
 	str sval;
@@ -208,3 +307,143 @@ static int secret_param(modparam_t _type, void *_val)
 
 	return add_secret(sval);
 }
+
+static str str_status_too_many_params = str_init("Too many parameters");
+static str str_status_empty_param = str_init("Not enough parameters");
+static str str_status_no_memory = str_init("Unable to allocate shared memory");
+static str str_status_string_error = str_init("Error converting string to int");
+static str str_status_adding_secret = str_init("Error adding secret");
+static str str_status_removing_secret = str_init("Error removing secret");
+
+static struct mi_root *mi_dump_secrets(struct mi_root *cmd, void *param)
+{
+	int pos = 0;
+	struct secret *secret_struct = secret_list;
+	struct mi_root *rpl_tree;
+
+	if (cmd->node.kids != NULL)
+	{
+		LM_WARN("too many parameters\n");
+		return init_mi_tree(400, str_status_too_many_params.s,
+					str_status_too_many_params.len);
+	}
+
+	rpl_tree = init_mi_tree(200, MI_OK_S, MI_OK_LEN);
+	if (rpl_tree == NULL)
+		return 0;
+
+	SECRET_LOCK;
+	while (secret_struct != NULL)
+	{
+		if (addf_mi_node_child(&rpl_tree->node, 0, 0, 0,
+					"ID %d: %.*s", pos++,
+					secret_struct->secret_key.len,
+					secret_struct->secret_key.s) == 0)
+		{
+			free_mi_tree(rpl_tree);
+			SECRET_UNLOCK;
+			return 0;
+		}
+		secret_struct = secret_struct->next;
+	}
+	SECRET_UNLOCK;
+
+	return rpl_tree;
+}
+
+static struct mi_root *mi_add_secret(struct mi_root *cmd, void *param)
+{
+	str sval;
+	struct mi_node *node = NULL;
+
+	node = cmd->node.kids;
+	if (node == NULL)
+	{
+		LM_WARN("no secret parameter\n");
+		return init_mi_tree(400, str_status_empty_param.s,
+						str_status_empty_param.len);
+	}
+
+	if (node->value.s == NULL || node->value.len == 0)
+	{
+		LM_WARN("empty secret parameter\n");
+		return init_mi_tree(400, str_status_empty_param.s,
+					str_status_empty_param.len);
+	}
+
+	if (node->next != NULL)
+	{
+		LM_WARN("too many parameters\n");
+		return init_mi_tree(400, str_status_too_many_params.s,
+					str_status_too_many_params.len);
+	}
+
+	sval.len = node->value.len;
+	sval.s = shm_malloc(sizeof(char) * sval.len);
+	if (sval.s == NULL)
+	{
+		LM_ERR("Unable to allocate shared memory\n");
+		return init_mi_tree(400, str_status_no_memory.s,
+					str_status_no_memory.len);
+	}
+	memcpy(sval.s, node->value.s, sval.len);
+
+	if (add_secret(sval) == 0)
+	{
+		return init_mi_tree(200, MI_OK_S, MI_OK_LEN);
+	}
+	else
+	{
+		LM_ERR("Adding secret\n");
+		return init_mi_tree(400, str_status_adding_secret.s,
+					str_status_adding_secret.len);
+	}
+}
+
+static struct mi_root *mi_rm_secret(struct mi_root *cmd, void *param)
+{
+	unsigned int id;
+	struct mi_node *node = NULL;
+
+	node = cmd->node.kids;
+	if (node == NULL)
+	{
+		LM_WARN("no id parameter\n");
+		return init_mi_tree(400, str_status_empty_param.s,
+						str_status_empty_param.len);
+	}
+
+	if (node->value.s == NULL || node->value.len == 0)
+	{
+		LM_WARN("empty id parameter\n");
+		return init_mi_tree(400, str_status_empty_param.s,
+					str_status_empty_param.len);
+	}
+
+	if (str2int(&node->value, &id) < 0)
+	{
+		LM_ERR("converting string to int\n");
+		return init_mi_tree(400, str_status_string_error.s,
+					str_status_string_error.len);
+	}
+
+	if (node->next != NULL)
+	{
+		LM_WARN("too many parameters\n");
+		return init_mi_tree(400, str_status_too_many_params.s,
+					str_status_too_many_params.len);
+	}
+
+	if (rm_secret(id) == 0)
+	{
+		return init_mi_tree(200, MI_OK_S, MI_OK_LEN);
+	}
+	else
+	{
+		LM_ERR("Removing secret\n");
+		return init_mi_tree(400, str_status_removing_secret.s,
+					str_status_removing_secret.len);
+	}
+
+	return init_mi_tree(200, MI_OK_S, MI_OK_LEN);
+}

+ 6 - 0
modules/auth_ephemeral/autheph_mod.h

@@ -23,12 +23,14 @@
 #ifndef AUTHEPH_MOD_H
 #define AUTHEPH_MOD_H
 
+#include "../../locking.h"
 #include "../../str.h"
 #include "../../modules/auth/api.h"
 
 struct secret
 {
 	str secret_key;
+	struct secret *prev;
 	struct secret *next;
 };
 extern struct secret *secret_list;
@@ -41,4 +43,8 @@ extern autheph_username_format_t autheph_username_format;
 
 extern auth_api_s_t eph_auth_api;
 
+extern gen_lock_t *autheph_secret_lock;
+#define SECRET_LOCK	lock_get(autheph_secret_lock)
+#define SECRET_UNLOCK	lock_release(autheph_secret_lock)
+
 #endif /* AUTHEPH_MOD_H */

+ 9 - 2
modules/auth_ephemeral/authorize.c

@@ -170,7 +170,7 @@ static inline int digest_authenticate(struct sip_msg *_m, str *_realm,
 {
 	struct hdr_field* h;
 	int ret;
-	struct secret *secret_struct = secret_list;
+	struct secret *secret_struct;
 	str username;
 
 	LM_DBG("realm: %.*s\n", _realm->len, _realm->s);
@@ -216,6 +216,8 @@ static inline int digest_authenticate(struct sip_msg *_m, str *_realm,
 		return AUTH_ERROR;
 	}
 
+	SECRET_LOCK;
+	secret_struct = secret_list;
 	while (secret_struct != NULL)
 	{
 		ret = do_auth(_m, h, _realm, _method,
@@ -226,6 +228,7 @@ static inline int digest_authenticate(struct sip_msg *_m, str *_realm,
 		}
 		secret_struct = secret_struct->next;
 	}
+	SECRET_UNLOCK;
 
 	return ret;
 }
@@ -407,7 +410,7 @@ int autheph_authenticate(struct sip_msg *_m, char *_username, char *_password)
 	str susername, spassword;
 	char generated_password[base64_enc_len(SHA_DIGEST_LENGTH)];
 	str sgenerated_password;
-	struct secret *secret_struct = secret_list;
+	struct secret *secret_struct;
 
 	if (_m == NULL || _username == NULL || _password == NULL)
 	{
@@ -449,6 +452,8 @@ int autheph_authenticate(struct sip_msg *_m, char *_username, char *_password)
 	LM_DBG("password: %.*s\n", spassword.len, spassword.s);
 
 	sgenerated_password.s = generated_password;
+	SECRET_LOCK;
+	secret_struct = secret_list;
 	while (secret_struct != NULL)
 	{
 		LM_DBG("trying secret: %.*s\n",
@@ -462,11 +467,13 @@ int autheph_authenticate(struct sip_msg *_m, char *_username, char *_password)
 			if (strncmp(spassword.s, sgenerated_password.s,
 					spassword.len) == 0)
 			{
+				SECRET_UNLOCK;
 				return AUTH_OK;
 			}
 		}
 		secret_struct = secret_struct->next;
 	}
+	SECRET_UNLOCK;
 
 	return AUTH_ERROR;
 }

+ 68 - 0
modules/auth_ephemeral/doc/auth_ephemeral_admin.xml

@@ -502,5 +502,73 @@ if (!autheph_check_timestamp("$var(username)")) {
 		</example>
 	</section>
 	</section>
+
+	<section>
+	<title>MI Commands</title>
+	<section id="auth_eph.m.autheph.add_secret">
+		<title><function moreinfo="none">autheph.add_secret</function></title>
+		<para>Add a secret to the head of the shared secret list. The
+		secret will be the first one tried during future authentication
+		attempts. This MI command allows you to update the shared secret
+		list without having to restart Kamailio.</para>
+		<note><para>If you want your new shared secret list to persist
+		across restarts you must add it to your Kamailio configuration
+		file.</para></note>
+		<para>Name: <emphasis>autheph.add_secret</emphasis></para>
+		<para>Parameters:</para>
+		<itemizedlist>
+			<listitem>
+				<para>secret</para>
+			</listitem>
+		</itemizedlist>
+		<para>MI FIFO Command Format:</para>
+		<programlisting  format="linespecific">
+			:autheph.add_secret:fifo_reply
+			kamailio_rules
+			_empty_line_
+</programlisting>
+	</section>
+
+	<section id="auth_eph.m.autheph.dump_secrets">
+		<title><function moreinfo="none">autheph.dump_secrets</function></title>
+		<para>Dump the set of shared secrets.</para>
+		<para>Name: <emphasis>autheph.dump_secrets</emphasis></para>
+		<para>Parameters:</para>
+		<itemizedlist>
+			<listitem>
+				<para><emphasis>none</emphasis></para>
+			</listitem>
+		</itemizedlist>
+		<para>MI FIFO Command Format:</para>
+		<programlisting  format="linespecific">
+			:autheph.dump_secrets:fifo_reply
+			_empty_line_
+</programlisting>
+	</section>
+
+	<section id="auth_eph.m.autheph.rm_secret">
+		<title><function moreinfo="none">autheph.rm_secret</function></title>
+		<para>Remove the secret with the specified integer ID.  This MI
+		command allows you to update the shared secret list without
+		having to restart Kamailio.</para>
+		<note><para>If you want your new shared secret list to persist
+		across restarts you must add it to your Kamailio configuration
+		file.</para></note>
+		<para>Name: <emphasis>autheph.rm_secret</emphasis></para>
+		<para>Parameters:</para>
+		<itemizedlist>
+			<listitem>
+				<para>ID - the ID of the secret to remove</para>
+			</listitem>
+		</itemizedlist>
+		<para>MI FIFO Command Format:</para>
+		<programlisting  format="linespecific">
+			:autheph.rm_secret:fifo_reply
+			0
+			_empty_line_
+</programlisting>
+	</section>
+
+	</section>
 </chapter>