diff --git a/apps/app_directory.c b/apps/app_directory.c
index be76d4b35b035e74876f84669d099be32cd822d1..e22a2ccb400913a82acbca56b79bb38e67c54e90 100755
--- a/apps/app_directory.c
+++ b/apps/app_directory.c
@@ -262,3 +262,8 @@ int usecount(void)
 	STANDARD_USECOUNT(res);
 	return res;
 }
+
+char *key()
+{
+	return ASTERISK_GPL_KEY;
+}
diff --git a/apps/app_echo.c b/apps/app_echo.c
index 370dc68636e6b7d183f038d3f37dfee0c0394df3..7d0647d04b052e0af5e7dcbda6e9c5d66b564c7f 100755
--- a/apps/app_echo.c
+++ b/apps/app_echo.c
@@ -78,3 +78,8 @@ int usecount(void)
 	STANDARD_USECOUNT(res);
 	return res;
 }
+
+char *key()
+{
+	return ASTERISK_GPL_KEY;
+}
diff --git a/apps/app_playback.c b/apps/app_playback.c
index d7e023d944b79b799b363c8f63c7ae5e0174c45a..945686b2ca21ef194c75b93292771fb6272f2131 100755
--- a/apps/app_playback.c
+++ b/apps/app_playback.c
@@ -75,3 +75,8 @@ int usecount(void)
 	STANDARD_USECOUNT(res);
 	return res;
 }
+
+char *key()
+{
+	return ASTERISK_GPL_KEY;
+}
diff --git a/apps/app_skel.c b/apps/app_skel.c
index 419b8f40a0587d578b0cc939461f89fca526ce17..8ca411305b9e33589af60768707016dbeae7d873 100755
--- a/apps/app_skel.c
+++ b/apps/app_skel.c
@@ -68,3 +68,8 @@ int usecount(void)
 	STANDARD_USECOUNT(res);
 	return res;
 }
+
+char *key()
+{
+	return ASTERISK_GPL_KEY;
+}
diff --git a/apps/app_system.c b/apps/app_system.c
index 7e140a427833c0a922ac6ef08548e59aea0aace6..9915cbd965793288ef612c6911de3ed746d4a314 100755
--- a/apps/app_system.c
+++ b/apps/app_system.c
@@ -80,3 +80,8 @@ int usecount(void)
 	STANDARD_USECOUNT(res);
 	return res;
 }
+
+char *key()
+{
+	return ASTERISK_GPL_KEY;
+}
diff --git a/channels/chan_modem_aopen.c b/channels/chan_modem_aopen.c
index f344cb149bc6638bfe31a3351a4cf2b4f4b2b5e6..37c5a8b7f699c1043c6f62091cd9e6b20d9bcb46 100755
--- a/channels/chan_modem_aopen.c
+++ b/channels/chan_modem_aopen.c
@@ -478,3 +478,7 @@ char *description()
 	return desc;
 }
 
+char *key()
+{
+	return ASTERISK_GPL_KEY;
+}
diff --git a/channels/chan_modem_i4l.c b/channels/chan_modem_i4l.c
index 3a8dbd8c8a4eb4dbe7bdc17bd85da7f3d979ad84..f1e521adfd4caf1d954f0a53c2ab1583a5c0b7a8 100755
--- a/channels/chan_modem_i4l.c
+++ b/channels/chan_modem_i4l.c
@@ -358,6 +358,8 @@ static struct ast_frame *i4l_read(struct ast_modem_pvt *p)
 			if (f)
 				break;
 		}
+		if (f)
+			return f;
 		/* If we get here, we have a complete voice frame */
 		p->fr.frametype = AST_FRAME_VOICE;
 		p->fr.subclass = AST_FORMAT_SLINEAR;
@@ -570,3 +572,7 @@ char *description()
 	return desc;
 }
 
+char *key()
+{
+	return ASTERISK_GPL_KEY;
+}
diff --git a/codecs/codec_mp3_d.c b/codecs/codec_mp3_d.c
index 95e1fb51fc8e1a9e19ba715739ccd9e34548ba64..aad6d75703cd2e08ef640f810d81b51ef1ce439f 100755
--- a/codecs/codec_mp3_d.c
+++ b/codecs/codec_mp3_d.c
@@ -321,3 +321,8 @@ int usecount(void)
 	STANDARD_USECOUNT(res);
 	return res;
 }
+
+char *key()
+{
+	return ASTERISK_GPL_KEY;
+}
diff --git a/formats/format_g723.c b/formats/format_g723.c
index 83144376f866d85571dc351e8a4739071d1e8b1f..c437addcd115963d9c0587546f5139df71d2824f 100755
--- a/formats/format_g723.c
+++ b/formats/format_g723.c
@@ -350,3 +350,8 @@ char *description()
 	return desc;
 }
 
+
+char *key()
+{
+	return ASTERISK_GPL_KEY;
+}
diff --git a/formats/format_mp3.c b/formats/format_mp3.c
index 85abdbc1b9a3318417c5bdb151f21846244629c7..a3e25d45bc202ca3d34b1bd19d65a6d73414a57e 100755
--- a/formats/format_mp3.c
+++ b/formats/format_mp3.c
@@ -293,3 +293,8 @@ char *description()
 	return desc;
 }
 
+
+char *key()
+{
+	return ASTERISK_GPL_KEY;
+}
diff --git a/formats/format_wav.c b/formats/format_wav.c
index 9ba4fdc22a0e21acfbc24e6922193861d2ea7278..071c24f26fccfb1e8dd0c63dd9253acc33d7580d 100755
--- a/formats/format_wav.c
+++ b/formats/format_wav.c
@@ -353,3 +353,8 @@ char *description()
 	return desc;
 }
 
+
+char *key()
+{
+	return ASTERISK_GPL_KEY;
+}
diff --git a/formats/format_wav_gsm.c b/formats/format_wav_gsm.c
index b901a4028890cff6ac96214fd3d025767446a5d7..94e2bffd057911f57dab877f4425cc00a2db10c5 100755
--- a/formats/format_wav_gsm.c
+++ b/formats/format_wav_gsm.c
@@ -584,3 +584,8 @@ char *description()
 	return desc;
 }
 
+
+char *key()
+{
+	return ASTERISK_GPL_KEY;
+}
diff --git a/include/asterisk/module.h b/include/asterisk/module.h
index e7bda62d6b8cdec529f39376fe97e6fa472cd897..9a8a7153cc12d7ee5f2a1020e044dcae8786446c 100755
--- a/include/asterisk/module.h
+++ b/include/asterisk/module.h
@@ -25,6 +25,17 @@ int unload_module(void);		/* Cleanup all module structures,
 					   sockets, etc */
 int usecount(void);			/* How many channels provided by this module are in use? */
 char *description(void);		/* Description of this module */
+char *key(void);		/* Return the below mentioned key, unmodified */
+
+int reload(void);
+
+#define ASTERISK_GPL_KEY \
+	"This paragraph is Copyright (C) 2000, Linux Support Services, Inc.  \
+In order for your module to load, it must return this key via a function \
+called \"key\".  Any code which includes this paragraph must be licensed under \
+the GNU General Public License version 2 or later (at your option).   Linux \
+Support Services, Inc. reserves the right to allow other parties to license \
+this paragraph under other terms as well."
 
 #define AST_MODULE_CONFIG "modules.conf" /* Module configuration file */
 
diff --git a/loader.c b/loader.c
index 63c44df40c917c24aeb1015439917a165755d303..b216a07bc97e2b8b75d2f1c0961709cf9fa492fd 100755
--- a/loader.c
+++ b/loader.c
@@ -21,20 +21,65 @@
 #include <asterisk/config.h>
 #include <asterisk/logger.h>
 #include <dlfcn.h>
+#include <asterisk/md5.h>
 #define __USE_GNU
 #include <pthread.h>
 #include "asterisk.h"
 
+static char expected_key[] =
+{ 0x8e, 0x93, 0x22, 0x83, 0xf5, 0xc3, 0xc0, 0x75,
+  0xff, 0x8b, 0xa9, 0xbe, 0x7c, 0x43, 0x74, 0x63 };
+
 struct module {
 	int (*load_module)(void);
 	int (*unload_module)(void);
 	int (*usecount)(void);
 	char *(*description)(void);
+	char *(*key)(void);
+	int (*reload)(void);
 	void *lib;
 	char resource[256];
 	struct module *next;
 };
 
+static int printdigest(unsigned char *d)
+{
+	int x;
+	char buf[256];
+	char buf2[16];
+	snprintf(buf, sizeof(buf), "Unexpected signature:");
+	for (x=0;x<16;x++) {
+		snprintf(buf2, sizeof(buf2), " %02x", *(d++));
+		strcat(buf, buf2);
+	}
+	strcat(buf, "\n");
+	ast_log(LOG_DEBUG, buf);
+	return 0;
+}
+
+static int key_matches(char *key1, char *key2)
+{
+	int match = 1;
+	int x;
+	for (x=0;x<16;x++) {
+		match &= (key1[x] == key2[x]);
+	}
+	return match;
+}
+
+static int verify_key(char *key)
+{
+	struct MD5Context c;
+	char digest[16];
+	MD5Init(&c);
+	MD5Update(&c, key, strlen(key));
+	MD5Final(digest, &c);
+	if (key_matches(expected_key, digest))
+		return 0;
+	printdigest(digest);
+	return -1;
+}
+
 static struct loadupdate {
 	int (*updater)(void);
 	struct loadupdate *next;
@@ -94,6 +139,7 @@ int ast_load_resource(char *resource_name)
 	struct module *m;
 	int flags=0;
 	char *val;
+	char *key;
 	int o;
 	struct ast_config *cfg;
 	/* Keep the module file parsing silent */
@@ -158,6 +204,21 @@ int ast_load_resource(char *resource_name)
 		ast_log(LOG_WARNING, "No description in module %s\n", fn);
 		errors++;
 	}
+	m->key = dlsym(m->lib, "key");
+	if (!m->key) {
+		ast_log(LOG_WARNING, "No key routine in module %s\n", fn);
+		errors++;
+	}
+	m->reload = dlsym(m->lib, "reload");
+	if (m->key && !(key = m->key())) {
+		ast_log(LOG_WARNING, "Key routine returned NULL in module %s\n", fn);
+		errors++;
+	} else
+		key = NULL;
+	if (key && verify_key(key)) {
+		ast_log(LOG_WARNING, "Unexpected key returned by module %s\n", fn);
+		errors++;
+	}
 	if (errors) {
 		ast_log(LOG_WARNING, "%d error(s) loading module %s, aborted\n", errors, fn);
 		dlclose(m->lib);
@@ -175,6 +236,7 @@ int ast_load_resource(char *resource_name)
 			ast_verbose(VERBOSE_PREFIX_1 "Loaded %s => (%s)\n", fn, m->description());
 	}
 	m->next = module_list;
+	
 	module_list = m;
 	pthread_mutex_unlock(&modlock);
 	if ((res = m->load_module())) {
diff --git a/pbx/pbx_gtkconsole.c b/pbx/pbx_gtkconsole.c
index 52a4ecb980bb51350cb8f6987d508e6e1d7d7e18..f8f233cf84a9ec0eaa83395737cd281daf1c486e 100755
--- a/pbx/pbx_gtkconsole.c
+++ b/pbx/pbx_gtkconsole.c
@@ -494,3 +494,8 @@ char *description(void)
 {
 	return dtext;
 }
+
+char *key(void)
+{
+	return ASTERISK_GPL_KEY;
+}