diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/lib/conf.c mydns-1.1.0-tsig/lib/conf.c
--- mydns-1.1.0/lib/conf.c	2005-04-21 18:25:45.000000000 +0200
+++ mydns-1.1.0-tsig/lib/conf.c	2006-03-13 11:31:30.000000000 +0100
@@ -135,9 +135,9 @@
 	Overwrite a value if defaulted.
 **************************************************************************************************/
 void
-conf_clobber(CONF **confp, char *name, char *value)
+conf_clobber(CONFIG **confp, char *name, char *value)
 {
-	CONF *conf = *confp, *c;									/* Start of list/found item */
+	CONFIG *conf = *confp, *c;									/* Start of list/found item */
 
 	if (!name || !value)
 		return;
@@ -157,11 +157,11 @@
 	Sets a configuration value.
 **************************************************************************************************/
 void
-conf_set(CONF **confp, char *name, char *value, int defaulted)
+conf_set(CONFIG **confp, char *name, char *value, int defaulted)
 {
-	CONF *conf = *confp;											/* Start of list */
-	CONF *new;														/* New item to add */
-	register CONF *c;
+	CONFIG *conf = *confp;											/* Start of list */
+	CONFIG *new;														/* New item to add */
+	register CONFIG *c;
 
 	if (!name)
 		return;
@@ -192,7 +192,7 @@
 		}
 
 	/* Add new item to array */
-	if (!(new = (CONF *)calloc(1, sizeof(CONF))))
+	if (!(new = (CONFIG *)calloc(1, sizeof(CONFIG))))
 		Err("calloc");
 	if (!(new->name = strdup(name)))
 		Err("strdup");
@@ -212,10 +212,10 @@
 	Returns the value associated with the specified name.
 **************************************************************************************************/
 char *
-conf_get(CONF **confp, char *name, int *defaulted)
+conf_get(CONFIG **confp, char *name, int *defaulted)
 {
-	CONF *conf = *confp;											/* Start of list */
-	register CONF *c;
+	CONFIG *conf = *confp;											/* Start of list */
+	register CONFIG *c;
 
 	if (defaulted)
 		*defaulted = 1;
@@ -238,12 +238,12 @@
 	Load the MyDNS configuration file.
 **************************************************************************************************/
 void
-conf_load(CONF **confp, const char *filename)
+conf_load(CONFIG **confp, const char *filename)
 {
 	FILE	*fp;														/* Input file pointer */
 	char	linebuf[BUFSIZ];										/* Input line buffer */
 	char	*name, *value;											/* Name and value from `linebuf' */
-	CONF	*conf = *confp;										/* Config list */
+	CONFIG	*conf = *confp;										/* Config list */
 	int	lineno = 0;												/* Current line number in config */
 	struct stat st;												/* File stat for conf file */
 
diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/lib/error.c mydns-1.1.0-tsig/lib/error.c
--- mydns-1.1.0/lib/error.c	2005-04-20 18:49:11.000000000 +0200
+++ mydns-1.1.0-tsig/lib/error.c	2006-03-14 14:00:23.000000000 +0100
@@ -418,4 +418,32 @@
 }
 /*--- ErrSQL() ----------------------------------------------------------------------------------*/
 
+/**************************************************************************************************
+	HEX
+   Dump a string representing memory in hexadecimal format
+**************************************************************************************************/
+unsigned char *hex(char *mem, int  len)
+{
+	 static char buf[BUFSIZ];
+    int idx;
+    int l = 0;
+
+    if (len <= 0)
+        return NULL;
+
+    if (mem == NULL)
+        return NULL;
+
+    memset(buf, 0, BUFSIZ);
+
+    for (idx = 0; idx < len && idx < BUFSIZ ; idx++)
+    {
+        l += sprintf (buf + l, "%02x", (unsigned char) mem[idx]);
+    }
+    buf[l - 1] = 0;
+    return(buf);
+} 
+/*--- hex() -------------------------------------------------------------------------------------*/
+
+
 /* vi:set ts=3: */
diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/lib/mydnsutil.h mydns-1.1.0-tsig/lib/mydnsutil.h
--- mydns-1.1.0/lib/mydnsutil.h	2005-04-28 20:43:46.000000000 +0200
+++ mydns-1.1.0-tsig/lib/mydnsutil.h	2006-03-13 11:31:30.000000000 +0100
@@ -346,15 +346,15 @@
 	char	*altname;	/* Alternate name for this option */
 	int	defaulted;	/* This variable was defaulted; not actually in config file */
 	struct _conflist *next;
-} CONF;
+} CONFIG;
 
 #define	CONF_FS_CHAR	'\034'
 #define	CONF_FS_STR		"\034"
 
-extern void	conf_clobber(CONF **, char *, char *);
-extern void	conf_set(CONF **, char *, char *, int);
-extern char	*conf_get(CONF **, char *, int *);
-extern void	conf_load(CONF **, const char *);
+extern void	conf_clobber(CONFIG **, char *, char *);
+extern void	conf_set(CONFIG **, char *, char *, int);
+extern char	*conf_get(CONFIG **, char *, int *);
+extern void	conf_load(CONFIG **, const char *);
 
 #define	Free(P)	if ((P)) free((P)), (P) = NULL
 
@@ -374,6 +374,7 @@
 extern void error_init(const char *argv0, int facility);
 #if DEBUG_ENABLED
 extern void Debug(const char *, ...) __printflike(1,2);
+extern unsigned char *hex(char *, int);
 #endif
 extern void Verbose(const char *, ...) __printflike(1,2);
 extern void Notice(const char *, ...) __printflike(1,2);
diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/README.TSIG mydns-1.1.0-tsig/README.TSIG
--- mydns-1.1.0/README.TSIG	1970-01-01 01:00:00.000000000 +0100
+++ mydns-1.1.0-tsig/README.TSIG	2006-03-13 14:57:54.000000000 +0100
@@ -0,0 +1,50 @@
+Secret Key Transaction Authentication for MyDNS (TSIG)
+
+This protocol allows for transaction level authentication using
+shared secrets and one way hashing (using the HMAC-MD5 algorithm). 
+
+It can be used to authenticate dynamic updates as coming from an 
+approved client.
+
+
+DATABASE SETUP
+--------------
+
+Transaction Keys are stored in the 'dnskey' table and the name of the key
+allowed to update an record is stored in column 'update_key' of the 'soa' table.
+
+You can generate you own key with dnssec-keygen tool :
+
+    dnssec-keygen  -a HMAC-MD5 -b 128 -n HOST client.domain.com
+
+Insert the value of this key in the 'dnskey' table :
+
+    INSERT INTO dnskey (name, algorithm, size, type, private) VALUES 
+    ('client.domain.com', 'HMAC-MD5', 128, 'HOST', 'IYrqgYuJaTkL2Xs34GZ7+w==');
+
+Add the 'update_acl' column in the 'soa' table :
+
+    ALTER TABLE `soa` ADD `update_key` VARCHAR( 255 ) ; 
+
+Assign the update key to an DNS entry :
+
+    UPDATE soa SET update_key = 'client.domain.com' WHERE origin='domain.com.';
+
+Restart the MyDNS server and check if the optional column 'update_key' was found :
+
+    # mydns -v
+    ...
+    mydns: optional 'update_key' column found in 'soa' table
+    ...
+ 
+USAGE 
+-----
+
+Update an domain entry with the nsupdate client :
+
+    $ nsupdate -d -y client.domain.com:IYrqgYuJaTkL2Xs34GZ7+w==
+    > server ns.domain.com
+    > zone domain.com
+    > update add entry.domain.com 60 A 192.168.0.1 
+    > send
+
diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/src/lib/bits.h mydns-1.1.0-tsig/src/lib/bits.h
--- mydns-1.1.0/src/lib/bits.h	2005-04-20 18:43:22.000000000 +0200
+++ mydns-1.1.0-tsig/src/lib/bits.h	2006-03-13 11:31:30.000000000 +0100
@@ -24,6 +24,7 @@
 
 #define SIZE16		sizeof(uint16_t)
 #define SIZE32		sizeof(uint32_t)
+#define SIZE48		SIZE16 + SIZE32
 
 
 /* Copy value of uint16_t `n' into `src'; Move `src' forward to next offset */
@@ -46,6 +47,20 @@
 	(src) += SIZE32; \
 }
 
+/* Copy value of uint32_t `n' into `dest'; Move `dest' forward to next offset */
+#define DNS_GET48(n, src) { \
+	register uchar *t_src = (uchar *)(src); \
+	(n) = ((uint64_t)t_src[0] << 40) \
+	    | ((uint64_t)t_src[1] << 32) \
+	    | ((uint64_t)t_src[2] << 24) \
+	    | ((uint64_t)t_src[3] << 16) \
+	    | ((uint64_t)t_src[4] << 8) \
+	    | ((uint64_t)t_src[5]) \
+	    ; \
+	(src) += SIZE48; \
+}
+
+
 /* Copy value of uint16_t `n' into `dest'; Move `dest' forward to next offset */
 #define DNS_PUT16(dest, n) { \
 	register uint16_t t_n = (uint16_t)(n); \
@@ -66,6 +81,19 @@
 	(dest) += SIZE32; \
 }
 
+#define DNS_PUT48(dest, n) { \
+	register uint64_t t_n = (uint64_t)(n); \
+	register uchar *t_dest = (uchar *)(dest); \
+	*t_dest++ = t_n >> 40; \
+	*t_dest++ = t_n >> 32; \
+	*t_dest++ = t_n >> 24; \
+	*t_dest++ = t_n >> 16; \
+	*t_dest++ = t_n >> 8; \
+	*t_dest   = t_n; \
+	(dest) += SIZE48; \
+}
+
+
 /* Copy `len' bytes of data from `src' to `dest'; Move `dest' forward `len' bytes */
 #define DNS_PUT(dest, src, len) { \
 	memcpy((dest), (src), (len)); \
diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/src/lib/key.c mydns-1.1.0-tsig/src/lib/key.c
--- mydns-1.1.0/src/lib/key.c	1970-01-01 01:00:00.000000000 +0100
+++ mydns-1.1.0-tsig/src/lib/key.c	2006-03-13 11:31:30.000000000 +0100
@@ -0,0 +1,23 @@
+/**************************************************************************************************
+	$Id: rr.c,v 1.65 2005/04/29 16:10:27 bboy Exp $
+
+	Copyright (C) 2002-2005  Don Moore <bboy@bboy.net>
+
+	This program 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.
+
+	This program 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 "mydns.h"
+
+char mydns_key_table_name[PATH_MAX] = MYDNS_KEY_TABLE;
diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/src/lib/Makefile.am mydns-1.1.0-tsig/src/lib/Makefile.am
--- mydns-1.1.0/src/lib/Makefile.am	2005-04-20 18:43:22.000000000 +0200
+++ mydns-1.1.0-tsig/src/lib/Makefile.am	2006-03-13 11:31:30.000000000 +0100
@@ -5,7 +5,7 @@
 noinst_LIBRARIES		=	libmydns.a
 INCLUDES					=	@INTLINCLUDE@ @UTILINCLUDE@ @SQLINCLUDE@
 noinst_HEADERS			=	bits.h header.h mydns.h
-libmydns_a_SOURCES	=	ip.c question.c rr.c soa.c sql.c str.c unencode.c
+libmydns_a_SOURCES	=	ip.c question.c rr.c soa.c sql.c str.c unencode.c key.c
 
 ctags:
 	ctags @UTILDIR@/*.[ch] *.[ch]
diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/src/lib/Makefile.in mydns-1.1.0-tsig/src/lib/Makefile.in
--- mydns-1.1.0/src/lib/Makefile.in	2006-01-18 23:08:41.000000000 +0100
+++ mydns-1.1.0-tsig/src/lib/Makefile.in	2006-03-13 11:31:30.000000000 +0100
@@ -62,7 +62,8 @@
 libmydns_a_AR = $(AR) $(ARFLAGS)
 libmydns_a_LIBADD =
 am_libmydns_a_OBJECTS = ip.$(OBJEXT) question.$(OBJEXT) rr.$(OBJEXT) \
-	soa.$(OBJEXT) sql.$(OBJEXT) str.$(OBJEXT) unencode.$(OBJEXT)
+	soa.$(OBJEXT) sql.$(OBJEXT) str.$(OBJEXT) unencode.$(OBJEXT) \
+	key.$(OBJEXT)
 libmydns_a_OBJECTS = $(am_libmydns_a_OBJECTS)
 DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/depcomp
@@ -223,7 +224,7 @@
 noinst_LIBRARIES = libmydns.a
 INCLUDES = @INTLINCLUDE@ @UTILINCLUDE@ @SQLINCLUDE@
 noinst_HEADERS = bits.h header.h mydns.h
-libmydns_a_SOURCES = ip.c question.c rr.c soa.c sql.c str.c unencode.c
+libmydns_a_SOURCES = ip.c question.c rr.c soa.c sql.c str.c unencode.c key.c
 all: all-am
 
 .SUFFIXES:
@@ -272,6 +273,7 @@
 	-rm -f *.tab.c
 
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ip.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/key.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/question.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rr.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/soa.Po@am__quote@
diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/src/lib/mydns.h mydns-1.1.0-tsig/src/lib/mydns.h
--- mydns-1.1.0/src/lib/mydns.h	2005-12-18 20:16:41.000000000 +0100
+++ mydns-1.1.0-tsig/src/lib/mydns.h	2006-03-13 14:02:06.000000000 +0100
@@ -30,14 +30,17 @@
 /* Table names */
 #define	MYDNS_SOA_TABLE	"soa"
 #define	MYDNS_RR_TABLE		"rr"
+#define	MYDNS_KEY_TABLE	"dnskey"
 
 /* Configurable table names */
 extern char mydns_soa_table_name[PATH_MAX];
 extern char mydns_rr_table_name[PATH_MAX];
+extern char mydns_key_table_name[PATH_MAX];
 
 /* Configurable WHERE clauses */
 extern char *mydns_soa_where_clause;
 extern char *mydns_rr_where_clause;
+extern char *mydns_key_where_clause;
 
 /* If this is nonzero, an 'active' field is assumed to exist in the table, and
 	only active rows will be loaded by mydns_*_load() */
@@ -58,6 +61,14 @@
 #define mydns_set_soa_use_update_acl(S)	\
 				(mydns_soa_use_update_acl = sql_iscolumn((S), mydns_soa_table_name, "update_acl"))
 
+#ifdef WITH_SSL
+/* This is set by mydns_set_soa_use_update_key */
+extern int mydns_soa_use_update_key;
+#define mydns_set_soa_use_update_key(S)	\
+				(mydns_soa_use_update_key = sql_iscolumn((S), mydns_soa_table_name, "update_key"))
+
+#endif /* WITH_SSL */
+
 
 /* This is set by mydns_set_rr_use_stamp (currently unimplemented - for future IXFR support */
 extern int mydns_rr_use_stamp;
@@ -80,6 +91,13 @@
 #	define	MYDNS_RR_FIELDS	"id,zone,name,data,aux,ttl,type"
 #endif
 
+#define	MYDNS_KEY_NUMFIELDS	7
+#ifdef DN_COLUMN_NAMES
+#	define	MYDNS_KEY_FIELDS	"key_id,name,algorithm,size,type,key,private"
+#else
+#	define	MYDNS_KEY_FIELDS	"id,name,algorithm,size,type,key,private"
+#endif
+
 /* Does the specified string end with a dot? */
 #define	ENDS_WITH_DOT(s)	(s && (s[strlen(s)-1] == '.'))
 
@@ -146,6 +164,10 @@
 	ERR_FWD_RECURSIVE,						/* "Recursive query forwarding error" */
 	ERR_NO_UPDATE,								/* "UPDATE denied" */
 	ERR_PREREQUISITE_FAILED,				/* "UPDATE prerequisite failed" */
+   ERR_TSIG_KEY_NOT_FOUND,             /* "TSIG" Key not found */
+   ERR_TSIG_KEY_TOO_SHORT,             /* "TSIG" Key is too short */
+   ERR_TSIG_CLOCKSKEW,                 /* "TSIG" ClockSkew */
+   ERR_TSIG_BADSIGN,                   /* "TSIG" Bad sign */
 
 } task_error_t;
 
diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/src/lib/soa.c mydns-1.1.0-tsig/src/lib/soa.c
--- mydns-1.1.0/src/lib/soa.c	2005-12-18 20:16:41.000000000 +0100
+++ mydns-1.1.0-tsig/src/lib/soa.c	2006-03-13 11:31:30.000000000 +0100
@@ -27,6 +27,7 @@
 int mydns_soa_use_active = 0;
 int mydns_soa_use_xfer = 0;
 int mydns_soa_use_update_acl = 0;
+int mydns_soa_use_update_key = 0;
 
 /* Make this nonzero to enable debugging within this source file */
 #define	DEBUG_LIB_SOA	0
diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/src/mydns/axfr.c mydns-1.1.0-tsig/src/mydns/axfr.c
--- mydns-1.1.0/src/mydns/axfr.c	2005-05-06 18:06:18.000000000 +0200
+++ mydns-1.1.0-tsig/src/mydns/axfr.c	2006-03-13 11:31:30.000000000 +0100
@@ -125,7 +125,7 @@
 {
 	char len[2], *l = len;
 
-	build_reply(t, 0);
+	build_reply(t, 0, 0);
 	DNS_PUT16(l, t->replylen);
 	axfr_write(t, len, SIZE16);
 	axfr_write(t, t->reply, t->replylen);
diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/src/mydns/conf.c mydns-1.1.0-tsig/src/mydns/conf.c
--- mydns-1.1.0/src/mydns/conf.c	2006-01-18 21:46:46.000000000 +0100
+++ mydns-1.1.0-tsig/src/mydns/conf.c	2006-03-13 11:31:30.000000000 +0100
@@ -27,7 +27,7 @@
 #include <pwd.h>
 #include <grp.h>
 
-CONF		*Conf = (CONF *)NULL;								/* Config options */
+CONFIG	*Conf = (CONFIG *)NULL;								/* Config options */
 int		opt_daemon = 0;										/* Run in background? (-d, --daemon) */
 char		*opt_conf = MYDNS_CONF;								/* Location of config file (-c, --conf) */
 uid_t		perms_uid = 0;											/* User permissions */
@@ -57,7 +57,7 @@
 **
 **  If the 'name' is "-", the --dump-config option treats 'desc' as a header field.
 */
-static CONF defConfig[] = {
+static CONFIG defConfig[] = {
 /* name						value							desc	*/
 {	"-",						NULL,							N_("DATABASE INFORMATION")},
 {	"db-host",				"localhost",				N_("SQL server hostname")},
@@ -95,6 +95,7 @@
 {	"ignore-minimum",		"no",							N_("Ignore minimum TTL for zone?")},
 {	"soa-table",			MYDNS_SOA_TABLE,			N_("Name of table containing SOA records")},
 {	"rr-table",				MYDNS_RR_TABLE,			N_("Name of table containing RR data")},
+{	"key-table",			MYDNS_KEY_TABLE,			N_("Name of table containing KEY")},
 
 #ifdef DN_COLUMN_NAMES
 {	"default-ns",			"ns0.example.com.",		N_("Default nameserver for all zones")},
@@ -117,7 +118,7 @@
 	time_t	time_now = time(NULL);
 	int		len = 0, w = 0, n, defaulted;
 	char		pair[512], buf[80];
-	CONF		*c;
+	CONFIG	*c;
 
 	/*
 	**	Pretty header
diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/src/mydns/db.c mydns-1.1.0-tsig/src/mydns/db.c
--- mydns-1.1.0/src/mydns/db.c	2006-01-18 21:46:47.000000000 +0100
+++ mydns-1.1.0-tsig/src/mydns/db.c	2006-03-14 11:06:48.000000000 +0100
@@ -134,10 +134,44 @@
 	printf("  data       CHAR(128) NOT NULL,\n");
 	printf("  aux        INT UNSIGNED NOT NULL,\n");
 	printf("  ttl        INT UNSIGNED NOT NULL default '%u',\n", DNS_DEFAULT_TTL);
-	printf("  UNIQUE KEY rr (zone,name,type,data)\n");
+	printf("  UNIQUE KEY %s (zone,name,type,data)\n", mydns_rr_table_name);
 	printf(") TYPE=MyISAM;\n\n");
 #endif
 
+	/* Resource record table */
+	printf("--\n--  Table structure for table '%s' (dns keys)\n--\n", mydns_key_table_name);
+
+#if USE_PGSQL
+	printf("CREATE TABLE %s (\n", mydns_key_table_name);
+	printf("    id serial NOT NULL,\n");
+	printf("    name character varying(128) NOT NULL,\n");
+	printf("    algorithm character varying(8) NOT NULL,\n");
+	printf("    size integer,\n");
+	printf("    \"type\" character varying(5),\n");
+	printf("    public character varying(255) NOT NULL,\n");
+	printf("    private character varying(255) NOT NULL,\n");
+	printf("    CONSTRAINT dnskey_algorithm CHECK\n");
+   printf("    (((((((algorithm) = 'RSA') OR ((algorithm) = 'RSAMD5'))\n"); 
+   printf("    OR ((algorithm) = 'DH')) OR ((algorithm) = 'DSA'))\n");
+   printf("    OR ((algorithm) = 'HMAC-MD5'))),\n");
+	printf("    CONSTRAINT dnskey_type CHECK ((((((\"type\") = 'ZONE')\n");
+   printf("    OR ((\"type\") = 'HOST')) OR ((\"type\") = 'ENTRY'))\n");
+   printf("    OR ((\"type\") = 'USER')))\n");
+	printf(");\n\n");
+#else
+	printf("CREATE TABLE IF NOT EXISTS %s (\n", mydns_key_table_name);
+	printf("  id         INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,\n");
+	printf("  name       CHAR(128) NOT NULL,\n");
+	printf("  algorithm  ENUM('RSA','RSAMD5','DH','DSA','HMAC-MD5') NOT NULL,\n");
+	printf("  size       INT(4) UNSIGNED NOT NULL,\n");
+	printf("  type       ENUM('ZONE','HOST','ENTRY','USER') NOT NULL,\n");
+	printf("  public     CHAR(255) NOT NULL,\n");
+	printf("  private    CHAR(255) NOT NULL,\n");
+	printf("  UNIQUE KEY %s (name)\n", mydns_key_table_name);
+	printf(") TYPE=MyISAM;\n");
+	printf("\n");
+#endif
+
 	exit(EXIT_SUCCESS);
 }
 /*--- db_output_create_tables() -----------------------------------------------------------------*/
@@ -227,6 +261,7 @@
 	int old_soa_use_active = mydns_soa_use_active;
 	int old_soa_use_xfer = mydns_soa_use_xfer;
 	int old_soa_use_update_acl = mydns_soa_use_update_acl;
+	int old_soa_use_update_key = mydns_soa_use_update_key;
 	int old_rr_use_active = mydns_rr_use_active;
 
 	/* Check for soa.active */
@@ -237,13 +272,20 @@
 	/* Check for soa.xfer */
 	mydns_set_soa_use_xfer(sql);
 	if (mydns_soa_use_xfer != old_soa_use_xfer)
-		Verbose(_("optional 'xfer' column found in '%s' table"), mydns_soa_table_name);
+		Verbose(_("optonal 'xfer' column found in '%s' table"), mydns_soa_table_name);
 
 	/* Check for soa.update_acl */
 	mydns_set_soa_use_update_acl(sql);
 	if (mydns_soa_use_update_acl != old_soa_use_update_acl)
 		Verbose(_("optional 'update_acl' column found in '%s' table"), mydns_soa_table_name);
 
+#ifdef WITH_SSL
+   /* Check for soa.update_key */
+	mydns_set_soa_use_update_key(sql);
+	if (mydns_soa_use_update_key != old_soa_use_update_key)
+		Verbose(_("optional 'update_key' column found in '%s' table"), mydns_soa_table_name);
+#endif /* WITH_SSL */
+
 	/* Check for rr.active */
 	mydns_set_rr_use_active(sql);
 	if (mydns_rr_use_active != old_rr_use_active)
diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/src/mydns/error.c mydns-1.1.0-tsig/src/mydns/error.c
--- mydns-1.1.0/src/mydns/error.c	2006-01-18 21:46:47.000000000 +0100
+++ mydns-1.1.0-tsig/src/mydns/error.c	2006-03-13 14:02:06.000000000 +0100
@@ -76,6 +76,10 @@
 		case ERR_FWD_RECURSIVE: return ((char *)_("Recursive_query_forwarding_error"));
 		case ERR_NO_UPDATE: return ((char *)_("UPDATE_denied"));
 		case ERR_PREREQUISITE_FAILED: return ((char *)_("UPDATE_prerequisite_failed"));
+		case ERR_TSIG_KEY_NOT_FOUND: return ((char *)_("TSIG_key_not_found"));
+		case ERR_TSIG_KEY_TOO_SHORT: return ((char *)_("TSIG_key_is_too_short"));
+		case ERR_TSIG_CLOCKSKEW: return ((char *)_("TSIG_clockskew"));
+		case ERR_TSIG_BADSIGN: return ((char *)_("TSIG_badsign"));
 	}
 	return ((char *)_("Unknown"));
 }
diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/src/mydns/Makefile.am mydns-1.1.0-tsig/src/mydns/Makefile.am
--- mydns-1.1.0/src/mydns/Makefile.am	2005-04-20 18:49:12.000000000 +0200
+++ mydns-1.1.0-tsig/src/mydns/Makefile.am	2006-03-13 11:31:30.000000000 +0100
@@ -12,11 +12,11 @@
 
 mydns_DEPENDENCIES	=	@LIBMYDNS@ @LIBUTIL@
 
-noinst_HEADERS	=	cache.h named.h task.h
+noinst_HEADERS	=	cache.h named.h task.h update.h tsig.h
 mydns_SOURCES	=	alias.c axfr.c cache.c conf.c data.c db.c encode.c \
 						error.c listen.c main.c queue.c recursive.c \
 						reply.c resolve.c rr.c sort.c status.c task.c \
-						tcp.c udp.c update.c
+						tcp.c udp.c update.c tsig.c
 
 CLEANFILES		=	malloc_trace gmon.out bb.out
 
diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/src/mydns/Makefile.in mydns-1.1.0-tsig/src/mydns/Makefile.in
--- mydns-1.1.0/src/mydns/Makefile.in	2006-01-18 23:08:41.000000000 +0100
+++ mydns-1.1.0-tsig/src/mydns/Makefile.in	2006-03-13 11:31:30.000000000 +0100
@@ -65,7 +65,8 @@
 	error.$(OBJEXT) listen.$(OBJEXT) main.$(OBJEXT) \
 	queue.$(OBJEXT) recursive.$(OBJEXT) reply.$(OBJEXT) \
 	resolve.$(OBJEXT) rr.$(OBJEXT) sort.$(OBJEXT) status.$(OBJEXT) \
-	task.$(OBJEXT) tcp.$(OBJEXT) udp.$(OBJEXT) update.$(OBJEXT)
+	task.$(OBJEXT) tcp.$(OBJEXT) udp.$(OBJEXT) update.$(OBJEXT) \
+	tsig.$(OBJEXT)
 mydns_OBJECTS = $(am_mydns_OBJECTS)
 mydns_LDADD = $(LDADD)
 DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
@@ -228,11 +229,11 @@
 INCLUDES = @UTILINCLUDE@ @MYDNSINCLUDE@ @INTLINCLUDE@ @SQLINCLUDE@ @SSLINCLUDE@
 LDADD = @LIBMYDNS@ @LIBUTIL@ @LIBINTL@ @LIBSQL@ @LIBSSL@ @LIBSOCKET@ @LIBNSL@ @LIBM@
 mydns_DEPENDENCIES = @LIBMYDNS@ @LIBUTIL@
-noinst_HEADERS = cache.h named.h task.h
+noinst_HEADERS = cache.h named.h task.h update.h tsig.h
 mydns_SOURCES = alias.c axfr.c cache.c conf.c data.c db.c encode.c \
 						error.c listen.c main.c queue.c recursive.c \
 						reply.c resolve.c rr.c sort.c status.c task.c \
-						tcp.c udp.c update.c
+						tcp.c udp.c update.c tsig.c
 
 CLEANFILES = malloc_trace gmon.out bb.out
 all: all-am
@@ -325,6 +326,7 @@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/status.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/task.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tcp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsig.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/udp.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/update.Po@am__quote@
 
diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/src/mydns/named.h mydns-1.1.0-tsig/src/mydns/named.h
--- mydns-1.1.0/src/mydns/named.h	2005-04-20 18:49:12.000000000 +0200
+++ mydns-1.1.0-tsig/src/mydns/named.h	2006-03-13 11:31:30.000000000 +0100
@@ -26,6 +26,11 @@
 #include "mydns.h"
 #include "task.h"
 #include "cache.h"
+#include "update.h"
+
+#if WITH_SSL
+#  include "tsig.h"
+#endif
 
 #if HAVE_SYS_RESOURCE_H
 #	include <sys/resource.h>
@@ -94,7 +101,7 @@
 
 
 /* Global variables */
-extern CONF		*Conf;											/* Config file data */
+extern CONFIG	*Conf;											/* Config file data */
 extern QUEUE	*Tasks;											/* Task queue */
 extern CACHE	*Cache;											/* Zone cache */
 extern time_t	current_time;									/* Current time */
@@ -185,7 +192,7 @@
 /* reply.c */
 extern int		reply_init(TASK *);
 extern void		build_cache_reply(TASK *);
-extern void		build_reply(TASK *, int);
+extern void		build_reply(TASK *, int, int);
 
 
 /* resolve.c */
diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/src/mydns/reply.c mydns-1.1.0-tsig/src/mydns/reply.c
--- mydns-1.1.0/src/mydns/reply.c	2006-01-18 21:46:47.000000000 +0100
+++ mydns-1.1.0-tsig/src/mydns/reply.c	2006-03-14 14:00:23.000000000 +0100
@@ -599,6 +599,138 @@
 
 
 /**************************************************************************************************
+	REPLY_ADD_SIGNATURE
+	Add TSIG signaure
+**************************************************************************************************/
+static char *
+reply_add_signature(TASK *t, char *reply)
+{
+	 unsigned char data_[DNS_MAXPACKETLEN_UDP];
+    unsigned char *data = data_;
+    unsigned char *signature;
+    unsigned char *sign;
+    unsigned short val;
+    unsigned char md[EVP_MAX_MD_SIZE];                /* Digest */
+    int mdlen;                                        /* Digest len */
+    const EVP_MD *md5;                                /* MD5 engine */
+    HMAC_CTX ctx;                                     /* HMAC Context */
+    TSIG   tsig;                                      /* Transaction signature */
+    int    headerlen, rdatalen; 
+
+    tsig.timesigned = time(NULL);
+    tsig.fudge = DNS_TSIG_FUDGE;
+    tsig.originalid = t->originalid; 
+    tsig.error = t->tsig_error;
+    tsig.macsize = 0;
+    tsig.mac = NULL;
+    tsig.otherlen = 0;
+    tsig.other = NULL;
+
+    md5 = EVP_md5();
+    HMAC_Init(&ctx, t->tsig_key, t->tsig_keylen, md5);
+
+    /* Digest the query signature */
+    DNS_PUT16(data, t->query_maclen);
+    data = data_;
+    HMAC_Update(&ctx, data, SIZE16);
+    
+    HMAC_Update(&ctx, t->query_mac, t->query_maclen);
+
+    /* Digest the reply */
+    HMAC_Update(&ctx, reply, t->replylen);
+
+
+    /* Digest the keyname */
+    HMAC_Update(&ctx, t->tsig_keyname, t->tsig_keynamelen);
+
+    /* Digest the TTL + class */
+    DNS_PUT16(data, DNS_QTYPE_ANY);
+    DNS_PUT32(data, 0); 
+    data = data_;
+    HMAC_Update(&ctx, data, SIZE16 + SIZE32); 
+
+    /* Digest algorithm */
+    HMAC_Update(&ctx, HMACMD5_ALGORITHM, HMACMD5_ALGORITHM_LEN); 
+
+    /* Digest timesigned and fudge */
+    DNS_PUT48(data, tsig.timesigned);
+    DNS_PUT16(data, tsig.fudge); 
+    data = data_;
+    HMAC_Update(&ctx, data, SIZE48 + SIZE16); 
+
+    /* Digest error and otherlen */
+    DNS_PUT16(data, tsig.error);
+    DNS_PUT16(data, tsig.otherlen);
+    data = data_;
+    HMAC_Update(&ctx, data, SIZE16 + SIZE16); 
+    
+    if (tsig.otherlen > 0) {
+        HMAC_Update(&ctx, tsig.other, tsig.otherlen); 
+    }
+    
+    HMAC_Final(&ctx, md, &mdlen);
+
+    if ( t->tsig_error == DNS_RCODE_NOERROR) {
+        tsig.mac = md;
+        tsig.macsize = mdlen; 
+#if DEBUG_ENABLED && DEBUG_REPLY
+        Debug("%s: TSIG REPLY: digest [%s] size [%d]", desctask(t), hex(md, mdlen), mdlen);
+#endif
+    }
+
+    /* Increment the additional field counter */
+    sign = reply;
+    sign += DNS_HEADERSIZE - SIZE16;
+    DNS_GET16(val, sign);
+    sign -= SIZE16;
+    DNS_PUT16(sign, ++val);
+
+    headerlen = t->tsig_keynamelen + (SIZE16 * 2) + SIZE32 + SIZE16;
+    rdatalen = HMACMD5_ALGORITHM_LEN + SIZE48 + (SIZE16 * 2) \
+               + tsig.macsize + (SIZE16 * 3) + tsig.otherlen;
+
+    /* Construct the reply with the signature */
+	 signature = sign = malloc(t->replylen + headerlen + rdatalen);
+    if (!sign)
+		Err(_("out of memory"));
+
+    memcpy(sign, reply, t->replylen);
+    free(reply);
+
+    sign += t->replylen;
+    t->replylen += headerlen + rdatalen;
+
+    
+    /* Build Signature */
+    DNS_PUT(sign, t->tsig_keyname, t->tsig_keynamelen);
+    DNS_PUT16(sign, DNS_QTYPE_TSIG);
+    DNS_PUT16(sign, DNS_CLASS_ANY);
+	 DNS_PUT32(sign, 0);
+	 DNS_PUT16(sign, rdatalen);
+
+
+    DNS_PUT(sign, HMACMD5_ALGORITHM, HMACMD5_ALGORITHM_LEN);
+    DNS_PUT48(sign, tsig.timesigned);
+    DNS_PUT16(sign, tsig.fudge);
+    DNS_PUT16(sign, tsig.macsize);
+    DNS_PUT(sign, tsig.mac, tsig.macsize);
+    DNS_PUT16(sign, tsig.originalid);
+    DNS_PUT16(sign, tsig.error);
+    DNS_PUT16(sign, tsig.otherlen);
+    DNS_PUT(sign, tsig.other, tsig.otherlen);
+
+
+
+#if DEBUG_ENABLED
+    tsig_dump(desctask(t), &tsig);
+#endif
+
+    HMAC_CTX_cleanup(&ctx);
+
+    return (signature);
+}
+
+/**************************************************************************************************
 	REPLY_PROCESS_RRLIST
 	Adds each resource record found in `rrlist' to the reply.
 **************************************************************************************************/
@@ -783,7 +915,7 @@
 	Given a task, constructs the reply data.
 **************************************************************************************************/
 void
-build_reply(TASK *t, int want_additional)
+build_reply(TASK *t, int want_additional, int sign)
 {
 	char	*dest;
 	int	ancount, nscount, arcount;
@@ -847,6 +980,10 @@
 		DNS_PUT(dest, t->qd, t->qdlen);						/* Data for QUESTION section */
 	DNS_PUT(dest, t->rdata, t->rdlen);						/* Resource record data */
 
+   if (sign) {
+        t->reply = reply_add_signature(t, t->reply);
+   }
+
 #if DEBUG_ENABLED && DEBUG_REPLY
 	Debug("%s: reply:     id = %u", desctask(t), t->id);
 	Debug("%s: reply:     qr = %u (message is a %s)", desctask(t), t->hdr.qr, t->hdr.qr ? "response" : "query");
diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/src/mydns/task.c mydns-1.1.0-tsig/src/mydns/task.c
--- mydns-1.1.0/src/mydns/task.c	2006-01-18 21:50:39.000000000 +0100
+++ mydns-1.1.0-tsig/src/mydns/task.c	2006-03-14 14:00:23.000000000 +0100
@@ -286,6 +286,10 @@
 	new->minimum_ttl = DNS_MINIMUM_TTL;
 	new->reply_cache_ok = 1;
 
+#if WITH_SSL
+   new->key = NULL;
+#endif
+
 #if DEBUG_ENABLED && DEBUG_TASK
 	Debug("%s: task_init(%p) from %s:%d", desctask(new), new, file, line);
 #endif
@@ -329,6 +333,18 @@
 	}
 #endif
 
+#if WITH_SSL
+   if (t->key) {
+       Free(t->key);
+   }
+   if (t->tsig_key) {
+       Free(t->tsig_key);
+   }
+   if (t->query_mac) {
+        Free(t->query_mac);
+   }
+#endif
+
 	Free(t->query);
 	Free(t->qd);
 	rrlist_free(&t->an);
@@ -509,7 +525,7 @@
 
 			if (t->status < NEED_RECURSIVE_FWD_CONNECT)
 			{
-				build_reply(t, 1);
+				build_reply(t, 1, 0);
 				if (t->reply_cache_ok)
 					add_reply_to_cache(t);
 				t->status = NEED_WRITE;
diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/src/mydns/task.h mydns-1.1.0-tsig/src/mydns/task.h
--- mydns-1.1.0/src/mydns/task.h	2005-04-20 18:49:12.000000000 +0200
+++ mydns-1.1.0-tsig/src/mydns/task.h	2006-03-14 14:00:23.000000000 +0100
@@ -21,6 +21,11 @@
 #ifndef _MYDNS_TASK_H
 #define _MYDNS_TASK_H
 
+#if WITH_SSL
+#  include "update.h"
+#  include "tsig.h"
+#endif 
+
 /* If defined, DYNAMIC_NAMES causes dynamic allocation of the encoded names list.  It's slow. */
 #define	DYNAMIC_NAMES	0
 
@@ -112,6 +116,18 @@
 	uint16_t			arcount;										/* "arcount", from header */
 
 	int				no_markers;									/* Do not use markers? */
+   
+#if WITH_SSL
+   KEY            *key;                               /* Transaction key */
+   unsigned char  tsig_keyname[DNS_MAXNAMELEN];       /* Transaction Key Name */
+   int            tsig_keynamelen;                    /* Transaction Key Name Len */
+   unsigned char  *tsig_key;                          /* Transaction Key */
+   int            tsig_keylen;                        /* Transaction Key Len */
+   unsigned char  *query_mac;                         /* Query digest */
+   int            query_maclen;                       /* Query digest Len */
+   uint16_t       originalid;
+   uint16_t       tsig_error;   
+#endif
 
 #if DYNAMIC_NAMES
 	char				**Names;										/* Names stored in reply */
diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/src/mydns/tsig.c mydns-1.1.0-tsig/src/mydns/tsig.c
--- mydns-1.1.0/src/mydns/tsig.c	1970-01-01 01:00:00.000000000 +0100
+++ mydns-1.1.0-tsig/src/mydns/tsig.c	2006-03-14 14:00:23.000000000 +0100
@@ -0,0 +1,168 @@
+/**************************************************************************************************
+	$Id: tsig.c,v 1.10 2005/12/18 19:16:41 bboy Exp $
+	tsig.c: Code to implement RFC 2847 (Secret Key Transaction Authentication for DNS - TSIG)
+
+	Copyright (C) 2006  Christophe Nowicki <cnowicki@easter-eggs.com>
+
+	This program 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.
+
+	This program 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 "named.h"
+
+#define	DEBUG_TSIG	1
+
+#define	DEBUG_TSIG_SQL	0
+
+#if DEBUG_ENABLED && DEBUG_TSIG
+/**************************************************************************************************
+	TSIG_DUMP
+**************************************************************************************************/
+void
+tsig_dump(unsigned char *desctask, TSIG *s)
+{
+	Debug("%s: TSIG DUMP: algorithm=[%s] timesigned=[%lld] fudge=[%d] macsize=[%d] mac=[%s] originalid=[%d] error=[%d] otherlen=[%d] other=[%s]",
+           desctask,
+           s->algorithm, 
+           s->timesigned, 
+           s->fudge, 
+           s->macsize, 
+           hex(s->mac, s->macsize), 
+           s->originalid, 
+           s->error, 
+           s->otherlen,
+           hex(s->other, s->otherlen)
+    );
+}
+/*--- tsig_dump() ---------------------------------------------------------------------------*/
+
+#endif /* DEBUG_ENABLED && DEBUG_UPDATE */
+
+/**************************************************************************************************
+	DETACH_TSIG
+**************************************************************************************************/
+void
+detach_tsig(unsigned char *query, int len, UQRR *rr, TSIG *tsig) {
+   char *start = rr->rdata;
+   char *src = rr->rdata;
+   int n;
+
+   tsig->algorithm_value = start;
+   src = name_unencode(query, len, src, tsig->algorithm, DNS_MAXNAMELEN);
+   tsig->algorithm_size = src - start;
+   DNS_GET48(tsig->timesigned, src);
+   DNS_GET16(tsig->fudge, src);
+   DNS_GET16(tsig->macsize, src);
+
+   if (!(tsig->mac = calloc(tsig->macsize, sizeof(char))))
+      Err(_("out of memory"));
+   memcpy(tsig->mac, src, tsig->macsize);
+   src += tsig->macsize;
+
+   DNS_GET16(tsig->originalid, src);
+   DNS_GET16(tsig->error, src);
+   DNS_GET16(tsig->otherlen, src);
+
+   if (tsig->otherlen > 0) 
+   {
+    if (!(tsig->other = calloc(tsig->otherlen, sizeof(char))))
+      Err(_("out of memory"));
+    memcpy(tsig->other, src, tsig->otherlen);
+   }
+
+   /* Decrement the additional field counter */
+   unsigned short val;
+   query += DNS_HEADERSIZE - SIZE16;
+   DNS_GET16(val, query);
+   query -= SIZE16;
+   DNS_PUT16(query, --val);
+}
+
+
+
+/*--- detach_tsig() ---------------------------------------------------------------------------*/
+
+/**************************************************************************************************
+	BASE64_DECODE
+	Decode base 64 encoded message
+**************************************************************************************************/
+unsigned char *base64_decode(unsigned char *buffer, unsigned int len) {
+      unsigned char *ret = (unsigned char *) malloc ((((len+2)/3)*4)+1);
+
+      if (!ret)
+        Err(_("out of memory"));
+
+      EVP_DecodeBlock (ret, buffer, len);
+      ret[(((len+2)/3)*4)] = 0;
+      return ret;
+}
+/*--- base64_decode() ---------------------------------------------------------------------------*/
+
+
+/**************************************************************************************************
+   FIND_KEY	
+   Fetch the key from the database
+**************************************************************************************************/
+KEY *
+tsig_find_key(unsigned char *desctask, unsigned char *keyname) 
+{
+   SQL_RES	*res = NULL;
+	SQL_ROW	row;
+	char		query[512];
+	size_t	querylen;
+   KEY      *key;
+
+#if DEBUG_ENABLED && DEBUG_UPDATE
+	Debug("%s: DNS UPDATE: find_key: does key [%s] exists?", desctask, keyname);
+#endif
+
+	querylen = snprintf(query, sizeof(query),
+		"SELECT name,algorithm,size,type,public,private FROM %s WHERE name='%s' LIMIT 1",
+		mydns_key_table_name, keyname);
+
+#if DEBUG_TSIG_SQL
+	Verbose("%s: DNS UPDATE: %s", desctask, query);
+#endif
+
+   if (!(res = sql_query(sql, query, querylen)))
+	{
+		WarnSQL(sql, "%s: %s", desctask, _("error searching key"));
+		return(NULL);
+	}
+   
+   if (sql_num_rows(res) != 1) {
+       sql_free(res);
+       return(NULL);
+   }
+
+   if (!(key = malloc(sizeof(KEY))))
+    Err(_("out of memory"));
+
+	if ((row = sql_getrow(res))) {
+       strncpy(key->name, row[0], DNS_MAXNAMELEN); 
+       strncpy(key->algorithm, row[1], DNS_MAXNAMELEN); 
+       key->size = atoi(row[2]); 
+       strncpy(key->public, row[3], 255); 
+       strncpy(key->private, row[4], 255); 
+   }
+   sql_free(res);
+
+#if DEBUG_ENABLED && DEBUG_TSIG
+	Debug("%s: DNS TSIG: key [%s] algorithm [%s] size [%d]", 
+           desctask, keyname, key->algorithm, key->size);
+#endif
+   return (key);
+}
+/*--- find_key() ---------------------------------------------------------------------------*/
+
diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/src/mydns/tsig.h mydns-1.1.0-tsig/src/mydns/tsig.h
--- mydns-1.1.0/src/mydns/tsig.h	1970-01-01 01:00:00.000000000 +0100
+++ mydns-1.1.0-tsig/src/mydns/tsig.h	2006-03-14 14:00:23.000000000 +0100
@@ -0,0 +1,61 @@
+/**************************************************************************************************
+	$Id: tsig.h,v 1.18 2005/04/20 16:49:12 bboy Exp $
+
+	Copyright (C) 2006  Christophe Nowicki <cnowicki@easter-eggs.com>
+
+	This program 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.
+
+	This program 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 _MYDNS_TSIG_H
+#define _MYDNS_TSIG_H
+
+#include <openssl/hmac.h>
+#include <openssl/evp.h>
+
+#define DNS_TSIG_FUDGE                 300
+#define HMACMD5_ALGORITHM              "\010hmac-md5\007sig-alg\003reg\003int"
+#define HMACMD5_ALGORITHM_LEN          26
+
+typedef struct _tansaction_signature
+{
+   char        *algorithm_value;
+	char			algorithm[DNS_MAXNAMELEN];						/* Algorithm name */
+   int         algorithm_size;
+   uint64_t    timesigned;
+   uint16_t    fudge;
+   uint16_t    macsize;
+	char			*mac;
+   uint16_t    originalid;
+   uint16_t    error;
+   uint16_t    otherlen;
+	char			*other;
+} TSIG;
+
+typedef struct _key 
+{
+	char			name[DNS_MAXNAMELEN];						/* Key name */
+	char			algorithm[DNS_MAXNAMELEN];					/* Algorithm name */
+   int         size;                                  /* Key size */
+	char			public[255];      						   /* Public key */
+	char			private[255];      						   /* Private key */
+} KEY;
+
+extern unsigned char *base64_decode(unsigned char *, unsigned int);
+extern void tsig_dump(unsigned char *, TSIG *);
+extern void detach_tsig(unsigned char *, int, UQRR *, TSIG *);
+extern KEY *tsig_find_key(unsigned char *, unsigned char *);
+
+#endif /* !_MYDNS_TSIG_H */
+/* vi:set ts=3: */
diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/src/mydns/update.c mydns-1.1.0-tsig/src/mydns/update.c
--- mydns-1.1.0/src/mydns/update.c	2005-12-18 20:16:41.000000000 +0100
+++ mydns-1.1.0-tsig/src/mydns/update.c	2006-03-14 14:00:23.000000000 +0100
@@ -26,51 +26,6 @@
 
 #define	DEBUG_UPDATE_SQL	0
 
-
-typedef struct _update_query_rr
-{
-	char				name[DNS_MAXNAMELEN];
-	dns_qtype_t		type;
-	dns_class_t		class;
-	uint32_t			ttl;
-	uint16_t			rdlength;
-	unsigned char	rdata[DNS_MAXPACKETLEN_UDP + 1];
-} UQRR;
-
-
-/* This is the temporary RRset described in RFC 2136, 3.2.3 */
-typedef struct _update_temp_rrset
-{
-	char			name[DNS_MAXNAMELEN];
-	dns_qtype_t	type;
-	char			data[DNS_MAXPACKETLEN_UDP + 1];
-	uint32_t		aux;
-
-	int			checked;											/* Have we checked this unique name/type? */
-} TMPRR;
-
-
-typedef struct _update_query
-{
-	/* Zone section */
-	char			name[DNS_MAXNAMELEN];						/* The zone name */
-	dns_qtype_t	type;												/* Must be DNS_QTYPE_SOA */
-	dns_class_t	class;											/* The zone's class */
-
-	UQRR			*PR;												/* Prerequisite section RRs */
-	int			numPR;											/* Number of items in 'PR' */
-
-	UQRR			*UP;												/* Update section RRs */
-	int			numUP;											/* Number of items in 'UP' */
-
-	UQRR			*AD;												/* Additional data section RRs */
-	int			numAD;											/* Number of items in 'AD' */
-
-	TMPRR			**tmprr;											/* Temporary RR list for prerequisite */
-	int			num_tmprr;										/* Number of items in "tmprr" */
-} UQ;
-
-
 /**************************************************************************************************
 	FREE_UQ
 	Frees a 'UQ' structure.
@@ -182,7 +137,7 @@
 /*--- check_update() ----------------------------------------------------------------------------*/
 
 
-#if DEBUG_ENABLED && DEBUG_UPDATE
+#if DEBUG_ENABLED
 /**************************************************************************************************
 	UPDATE_RRDUMP
 **************************************************************************************************/
@@ -218,11 +172,14 @@
 {
 	char *src = current;
 
+   rr->name_ptr = current;
+   
 	if (!(src = name_unencode(query, querylen, src, rr->name, sizeof(rr->name))))
 	{
 		formerr(t, DNS_RCODE_FORMERR, (task_error_t)rr->name[0], NULL);
 		return NULL;
 	}
+   rr->name_size = src - current;
 
 	DNS_GET16(rr->type, src);
 	DNS_GET16(rr->class, src);
@@ -230,6 +187,7 @@
 	DNS_GET16(rr->rdlength, src);
 	memcpy(rr->rdata, src, rr->rdlength);
 	src += rr->rdlength;
+   rr->size = src - current; 
 
 	return src;
 }
@@ -254,6 +212,8 @@
 	*/
 	if (!(src = name_unencode(query, querylen, src, q->name, sizeof(q->name))))
 		return formerr(t, DNS_RCODE_FORMERR, (task_error_t)q->name[0], NULL);
+
+   q->name_size = src - query - DNS_HEADERSIZE;
 	DNS_GET16(q->type, src);
 	DNS_GET16(q->class, src);
 #if DEBUG_ENABLED && DEBUG_UPDATE
@@ -768,6 +728,161 @@
 /*--- check_prerequisite() ----------------------------------------------------------------------*/
 
 
+
+
+
+
+#ifdef WITH_SSL
+/**************************************************************************************************
+	CHECK_TSIG
+	Check the Secret Key Transaction Authentication (TSIG) as described in RFC 2845.
+	Returns 0 on success, -1 on error.
+**************************************************************************************************/
+static int
+check_tsig(TASK *t, MYDNS_SOA *soa, UQ *q, UQRR *rr, int *do_sign)
+{
+	 unsigned char data_[DNS_MAXPACKETLEN_UDP];
+    unsigned char *data = data_;
+    unsigned char *key_decoded;
+    const EVP_MD *md5;                                /* MD5 engine */
+    char	*query = t->query;									/* Query section */
+    char	*start = query;							   		/* Start of query section */
+    int	querylen = t->len;									/* Length of 'query' */
+    HMAC_CTX ctx;                                     /* HMAC Context */
+    unsigned char md[EVP_MAX_MD_SIZE];                /* Digest */
+    int mdlen;                                        /* Digest len */
+    TSIG   tsig;                                      /* Transaction signature */
+    time_t  now;                                      /* Current time */
+
+    int  l, sum = 0;
+    
+    /* Detach transaction signature from the message */
+    detach_tsig(t->query, t->len, rr, &tsig);
+
+    t->originalid = tsig.originalid; 
+    t->query_mac = tsig.mac;
+    t->query_maclen = tsig.macsize;
+
+    t->tsig_error = DNS_RCODE_NOERROR;
+    
+
+#if DEBUG_ENABLED && DEBUG_UPDATE
+    tsig_dump(desctask(t), &tsig);
+#endif
+
+    /* Fetch key */
+    if (!(t->key = tsig_find_key(desctask(t), rr->name))) {
+#if DEBUG_ENABLED && DEBUG_UPDATE
+		  Debug("%s: DNS UPDATE: key not found", desctask(t));
+#endif
+        t->tsig_error = DNS_RCODE_BADKEY;
+        return dnserror(t, DNS_RCODE_NOTAUTH, ERR_TSIG_KEY_NOT_FOUND); 
+    }
+
+    /* Check key size */
+    if ((l = strlen(t->key->private)) < 10) { /* 10 chars = 8 octets = 64 bits */
+		  Verbose("%s: DNS UPDATE: key [%s] is too short to be secure", desctask(t), t->key->name);
+        t->tsig_error = DNS_RCODE_BADKEY;
+        return dnserror(t, DNS_RCODE_NOTAUTH, ERR_TSIG_KEY_TOO_SHORT); 
+    }
+
+    now = time(NULL);
+
+    *do_sign = 1; 
+
+    /* Check time */
+    if (now > tsig.timesigned + tsig.fudge) {
+        Verbose("%s: DNS UPDATE: signature has expired", desctask(t));
+        t->tsig_error = DNS_RCODE_BADTIME;
+        return dnserror(t, DNS_RCODE_NOTAUTH, ERR_TSIG_CLOCKSKEW); 
+    } else if (now  < tsig.timesigned - tsig.fudge) {
+        Verbose("%s: DNS UPDATE: signature is in the future", desctask(t));
+        return dnserror(t, DNS_RCODE_NOTAUTH, ERR_TSIG_CLOCKSKEW); 
+    }
+
+    /* Decode */
+    t->tsig_key = base64_decode(t->key->private, l);
+    t->tsig_keylen = (l * 3) / 4;
+    
+    md5 = EVP_md5();
+    HMAC_Init(&ctx, t->tsig_key, t->tsig_keylen, md5);
+
+
+    /* Digest Header */
+    HMAC_Update(&ctx, start, DNS_HEADERSIZE);
+
+    /* Compute message size without the TSIG record */
+    sum += q->name_size;
+    sum += SIZE16 * 2; /* Type and class */
+    for (l = 0; l < q->numPR; l++) {
+        sum += (&q->PR[l])->size;
+    }
+
+    for (l = 0; l < q->numUP; l++) {
+        sum += (&q->UP[l])->size;
+    }
+
+    for (l = 0; l < (q->numAD - 1); l++) { 
+        sum += (&q->AD[l])->size;
+    }
+
+    /* Digest message (all non-TSIG records) */
+    HMAC_Update(&ctx, query + DNS_HEADERSIZE, sum); 
+    
+    /* Digest the key name */
+    t->tsig_keynamelen = name_encode(t, t->tsig_keyname, rr->name, 0, 0);
+    HMAC_Update(&ctx, t->tsig_keyname, t->tsig_keynamelen); 
+
+    /* Digest class + ttl */
+    DNS_PUT16(data, rr->class);
+    DNS_PUT32(data, rr->ttl);
+    data = data_;
+
+    HMAC_Update(&ctx, data, SIZE16 + SIZE32); 
+    
+    /* Digest the key algorithm */ 
+    HMAC_Update(&ctx, tsig.algorithm_value, tsig.algorithm_size); 
+
+    /* Digest timesigned and fudge */
+    DNS_PUT48(data, tsig.timesigned);
+    DNS_PUT16(data, tsig.fudge);
+    data = data_;
+    HMAC_Update(&ctx, data, SIZE48 + SIZE16); 
+
+    /* Digest error and otherlen */
+    DNS_PUT16(data, tsig.error);
+    DNS_PUT16(data, tsig.otherlen);
+    data = data_;
+    HMAC_Update(&ctx, data, SIZE16 + SIZE16); 
+
+
+#if DEBUG_ENABLED && DEBUG_UPDATE
+    Debug("%s: TSIG CHECK: sizes query [%d] message [%d] signature [%d]", desctask(t), querylen, sum, querylen - sum);
+#endif
+
+    HMAC_Final(&ctx, md, &mdlen);
+
+#if DEBUG_ENABLED && DEBUG_UPDATE
+    Debug("%s: TSIG CHECK: digest [%s] size [%d]", desctask(t), hex(md, mdlen), mdlen);
+#endif
+
+    if (memcmp(md, tsig.mac, mdlen) != 0) {
+        Verbose("%s: TSIG CHECK: signature failed to verify", desctask(t));
+        t->tsig_error = DNS_RCODE_BADSIG;
+        return dnserror(t, DNS_RCODE_NOTAUTH, ERR_TSIG_BADSIGN); 
+    }
+
+#if DEBUG_ENABLED && DEBUG_UPDATE
+    Debug("%s: TSIG CHECK: signature verified !", desctask(t));
+#endif
+
+    HMAC_CTX_cleanup(&ctx);
+    return 0;
+}
+
+#endif /* WITH_SSL */
+
+
 /**************************************************************************************************
 	UPDATE_RRTYPE_OK
 	Is this RR type okay with MyDNS?  RFC 2136 3.4.1.2 specifically prohibigs ANY, AXFR, MAILA,
@@ -1408,6 +1523,7 @@
 {
 	MYDNS_SOA	*soa;												/* SOA record for zone */
 	UQ				*q;												/* Update query data */
+   int         sign_reply = 0;                        /* Sign replay? */
 	int			n;
 
 	/* Try to load SOA for zone */
@@ -1451,6 +1567,24 @@
 			goto dns_update_error;
 		}
 
+#ifdef WITH_SSL
+	/* Check the additional section */
+	for (n = 0; n < q->numAD; n++) {
+       if ( DNS_QTYPE_TSIG == (&q->AD[n])->type ) {
+           /* Check if the TSIG record is the last record -- RFC 2845 3.2 */
+           if (n != (q->numAD - 1)) {
+#if DEBUG_ENABLED && DEBUG_UPDATE
+                Debug("TSIG record is not the last record in the additional section");
+#endif
+                goto dns_update_error;
+           }
+           if (check_tsig(t, soa, q, &q->AD[n], &sign_reply) != 0) {
+                goto dns_update_error;
+           }
+       }
+   }
+#endif /* WITH_SSL */
+
 	/* Check the prerequisite RRsets -- RFC 2136 3.2.3 */
 	if (check_tmprr(t, soa, q) != 0)
 	{
@@ -1499,7 +1634,7 @@
 	cache_purge_zone(ReplyCache, soa->id);
 
 	/* Construct reply and set task status */
-	build_reply(t, 0);
+	build_reply(t, 0, sign_reply);
 	t->status = NEED_WRITE;
 
 	/* Clean up and return */
@@ -1508,7 +1643,7 @@
 	return 0;
 
 dns_update_error:
-	build_reply(t, 1);
+	build_reply(t, 1, sign_reply);
 #if DEBUG_ENABLED && DEBUG_UPDATE
 	Debug("%s: DNS UPDATE: Went to dns_update_error", desctask(t));
 #endif
diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/src/mydns/update.h mydns-1.1.0-tsig/src/mydns/update.h
--- mydns-1.1.0/src/mydns/update.h	1970-01-01 01:00:00.000000000 +0100
+++ mydns-1.1.0-tsig/src/mydns/update.h	2006-03-13 11:31:30.000000000 +0100
@@ -0,0 +1,71 @@
+/**************************************************************************************************
+	$Id: update.c,v 1.10 2005/12/18 19:16:41 bboy Exp $
+
+	Copyright (C) 2005  Don Moore <bboy@bboy.net>
+
+	This program 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.
+
+	This program 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 _MYDNS_UPDATE_H
+#define _MYDNS_UPDATE_H
+
+typedef struct _update_query_rr
+{
+   char           *name_ptr;
+	char				name[DNS_MAXNAMELEN];
+   int            name_size;                            
+	dns_qtype_t		type;
+	dns_class_t		class;
+	uint32_t			ttl;
+	uint16_t			rdlength;
+	unsigned char	rdata[DNS_MAXPACKETLEN_UDP + 1];
+   unsigned int   size;
+} UQRR;
+
+
+/* This is the temporary RRset described in RFC 2136, 3.2.3 */
+typedef struct _update_temp_rrset
+{
+	char			name[DNS_MAXNAMELEN];
+	dns_qtype_t	type;
+	char			data[DNS_MAXPACKETLEN_UDP + 1];
+	uint32_t		aux;
+
+	int			checked;											/* Have we checked this unique name/type? */
+} TMPRR;
+
+typedef struct _update_query
+{
+	/* Zone section */
+	char			name[DNS_MAXNAMELEN];						/* The zone name */
+   int         name_size;                             /* The zone name size */
+	dns_qtype_t	type;												/* Must be DNS_QTYPE_SOA */
+	dns_class_t	class;											/* The zone's class */
+
+	UQRR			*PR;												/* Prerequisite section RRs */
+	int			numPR;											/* Number of items in 'PR' */
+
+	UQRR			*UP;												/* Update section RRs */
+	int			numUP;											/* Number of items in 'UP' */
+
+	UQRR			*AD;												/* Additional data section RRs */
+	int			numAD;											/* Number of items in 'AD' */
+
+	TMPRR			**tmprr;											/* Temporary RR list for prerequisite */
+	int			num_tmprr;										/* Number of items in "tmprr" */
+} UQ;
+
+#endif /* !_MYDNS_UPDATE_H */
+/* vi:set ts=3: */
diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/src/util/util.c mydns-1.1.0-tsig/src/util/util.c
--- mydns-1.1.0/src/util/util.c	2005-04-20 18:49:12.000000000 +0200
+++ mydns-1.1.0-tsig/src/util/util.c	2006-03-13 11:31:30.000000000 +0100
@@ -22,7 +22,7 @@
 
 #include "util.h"
 
-CONF *Conf;															/* Configuration data */
+CONFIG *Conf;															/* Configuration data */
 
 
 /**************************************************************************************************
diff -x debian -x doc -x pkg -BruNa mydns-1.1.0/src/util/util.h mydns-1.1.0-tsig/src/util/util.h
--- mydns-1.1.0/src/util/util.h	2005-04-20 18:49:12.000000000 +0200
+++ mydns-1.1.0-tsig/src/util/util.h	2006-03-13 11:31:30.000000000 +0100
@@ -28,7 +28,7 @@
 	Defining this and recompiling will NOT let you import tinydns-data files! */
 #define TINYDNS_IMPORT
 
-extern CONF *Conf;												/* Configuration data */
+extern CONFIG *Conf;												/* Configuration data */
 
 extern void load_config(void);
 extern void db_connect(void);
