Commit 289ae548 authored by Mark Andrews's avatar Mark Andrews

2105. [func] GSS-TSIG support (RFC 3645).

parent 6292befa
2105. [func] GSS-TSIG support (RFC 3645).
2104. [port] Fix Solaris SMF error message. 2104. [port] Fix Solaris SMF error message.
2103. [port] Add /usr/sfw to list of locations for OpenSSL 2103. [port] Add /usr/sfw to list of locations for OpenSSL
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE. * PERFORMANCE OF THIS SOFTWARE.
*/ */
/* $Id: client.c,v 1.239 2006/07/22 01:00:04 marka Exp $ */ /* $Id: client.c,v 1.240 2006/12/04 01:52:45 marka Exp $ */
#include <config.h> #include <config.h>
...@@ -1227,6 +1227,7 @@ ns_client_isself(dns_view_t *myview, dns_tsigkey_t *mykey, ...@@ -1227,6 +1227,7 @@ ns_client_isself(dns_view_t *myview, dns_tsigkey_t *mykey,
{ {
dns_view_t *view; dns_view_t *view;
dns_tsigkey_t *key; dns_tsigkey_t *key;
dns_name_t *tsig = NULL;
isc_netaddr_t netsrc; isc_netaddr_t netsrc;
isc_netaddr_t netdst; isc_netaddr_t netdst;
...@@ -1241,7 +1242,6 @@ ns_client_isself(dns_view_t *myview, dns_tsigkey_t *mykey, ...@@ -1241,7 +1242,6 @@ ns_client_isself(dns_view_t *myview, dns_tsigkey_t *mykey,
for (view = ISC_LIST_HEAD(ns_g_server->viewlist); for (view = ISC_LIST_HEAD(ns_g_server->viewlist);
view != NULL; view != NULL;
view = ISC_LIST_NEXT(view, link)) { view = ISC_LIST_NEXT(view, link)) {
dns_name_t *tsig = NULL;
if (view->matchrecursiveonly) if (view->matchrecursiveonly)
continue; continue;
...@@ -1253,14 +1253,14 @@ ns_client_isself(dns_view_t *myview, dns_tsigkey_t *mykey, ...@@ -1253,14 +1253,14 @@ ns_client_isself(dns_view_t *myview, dns_tsigkey_t *mykey,
isc_boolean_t match; isc_boolean_t match;
isc_result_t result; isc_result_t result;
tsig = &mykey->name; result = dns_view_gettsig(view, &mykey->name, &key);
result = dns_view_gettsig(view, tsig, &key);
if (result != ISC_R_SUCCESS) if (result != ISC_R_SUCCESS)
continue; continue;
match = dst_key_compare(mykey->key, key->key); match = dst_key_compare(mykey->key, key->key);
dns_tsigkey_detach(&key); dns_tsigkey_detach(&key);
if (!match) if (!match)
continue; continue;
tsig = dns_tsigkey_identity(mykey);
} }
if (allowed(&netsrc, tsig, view->matchclients) && if (allowed(&netsrc, tsig, view->matchclients) &&
...@@ -1590,11 +1590,12 @@ client_request(isc_task_t *task, isc_event_t *event) { ...@@ -1590,11 +1590,12 @@ client_request(isc_task_t *task, isc_event_t *event) {
client->message->rdclass == dns_rdataclass_any) client->message->rdclass == dns_rdataclass_any)
{ {
dns_name_t *tsig = NULL; dns_name_t *tsig = NULL;
sigresult = dns_message_rechecksig(client->message, sigresult = dns_message_rechecksig(client->message,
view); view);
if (sigresult == ISC_R_SUCCESS) if (sigresult == ISC_R_SUCCESS)
tsig = client->message->tsigname; tsig = dns_tsigkey_identity(client->message->tsigkey);
if (allowed(&netaddr, tsig, view->matchclients) && if (allowed(&netaddr, tsig, view->matchclients) &&
allowed(&destaddr, tsig, view->matchdestinations) && allowed(&destaddr, tsig, view->matchdestinations) &&
!((client->message->flags & DNS_MESSAGEFLAG_RD) !((client->message->flags & DNS_MESSAGEFLAG_RD)
...@@ -1672,12 +1673,28 @@ client_request(isc_task_t *task, isc_event_t *event) { ...@@ -1672,12 +1673,28 @@ client_request(isc_task_t *task, isc_event_t *event) {
/* There is a signature, but it is bad. */ /* There is a signature, but it is bad. */
if (dns_message_gettsig(client->message, &name) != NULL) { if (dns_message_gettsig(client->message, &name) != NULL) {
char namebuf[DNS_NAME_FORMATSIZE]; char namebuf[DNS_NAME_FORMATSIZE];
char cnamebuf[DNS_NAME_FORMATSIZE];
dns_name_format(name, namebuf, sizeof(namebuf)); dns_name_format(name, namebuf, sizeof(namebuf));
ns_client_log(client, DNS_LOGCATEGORY_SECURITY, if (client->message->tsigkey->generated) {
NS_LOGMODULE_CLIENT, ISC_LOG_ERROR, dns_name_format(client->message->tsigkey->creator,
"request has invalid signature: " cnamebuf, sizeof(cnamebuf));
"TSIG %s: %s (%s)", namebuf, ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
isc_result_totext(result), tsigrcode); NS_LOGMODULE_CLIENT,
ISC_LOG_ERROR,
"request has invalid signature: "
"TSIG %s (%s): %s (%s)", namebuf,
cnamebuf,
isc_result_totext(result),
tsigrcode);
} else {
ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
NS_LOGMODULE_CLIENT,
ISC_LOG_ERROR,
"request has invalid signature: "
"TSIG %s: %s (%s)", namebuf,
isc_result_totext(result),
tsigrcode);
}
} else { } else {
ns_client_log(client, DNS_LOGCATEGORY_SECURITY, ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
NS_LOGMODULE_CLIENT, ISC_LOG_ERROR, NS_LOGMODULE_CLIENT, ISC_LOG_ERROR,
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE. * PERFORMANCE OF THIS SOFTWARE.
*/ */
/* $Id: control.c,v 1.28 2006/03/10 00:23:21 marka Exp $ */ /* $Id: control.c,v 1.29 2006/12/04 01:52:45 marka Exp $ */
/*! \file */ /*! \file */
...@@ -158,6 +158,10 @@ ns_control_docommand(isccc_sexpr_t *message, isc_buffer_t *text) { ...@@ -158,6 +158,10 @@ ns_control_docommand(isccc_sexpr_t *message, isc_buffer_t *text) {
result = ns_server_flushname(ns_g_server, command); result = ns_server_flushname(ns_g_server, command);
} else if (command_compare(command, NS_COMMAND_STATUS)) { } else if (command_compare(command, NS_COMMAND_STATUS)) {
result = ns_server_status(ns_g_server, text); result = ns_server_status(ns_g_server, text);
} else if (command_compare(command, NS_COMMAND_TSIGLIST)) {
result = ns_server_tsiglist(ns_g_server, text);
} else if (command_compare(command, NS_COMMAND_TSIGDELETE)) {
result = ns_server_tsigdelete(ns_g_server, command, text);
} else if (command_compare(command, NS_COMMAND_FREEZE)) { } else if (command_compare(command, NS_COMMAND_FREEZE)) {
result = ns_server_freeze(ns_g_server, ISC_TRUE, command); result = ns_server_freeze(ns_g_server, ISC_TRUE, command);
} else if (command_compare(command, NS_COMMAND_UNFREEZE) || } else if (command_compare(command, NS_COMMAND_UNFREEZE) ||
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE. * PERFORMANCE OF THIS SOFTWARE.
*/ */
/* $Id: control.h,v 1.22 2006/03/09 23:39:00 marka Exp $ */ /* $Id: control.h,v 1.23 2006/12/04 01:52:45 marka Exp $ */
#ifndef NAMED_CONTROL_H #ifndef NAMED_CONTROL_H
#define NAMED_CONTROL_H 1 #define NAMED_CONTROL_H 1
...@@ -47,6 +47,8 @@ ...@@ -47,6 +47,8 @@
#define NS_COMMAND_FLUSH "flush" #define NS_COMMAND_FLUSH "flush"
#define NS_COMMAND_FLUSHNAME "flushname" #define NS_COMMAND_FLUSHNAME "flushname"
#define NS_COMMAND_STATUS "status" #define NS_COMMAND_STATUS "status"
#define NS_COMMAND_TSIGLIST "tsig-list"
#define NS_COMMAND_TSIGDELETE "tsig-delete"
#define NS_COMMAND_FREEZE "freeze" #define NS_COMMAND_FREEZE "freeze"
#define NS_COMMAND_UNFREEZE "unfreeze" #define NS_COMMAND_UNFREEZE "unfreeze"
#define NS_COMMAND_THAW "thaw" #define NS_COMMAND_THAW "thaw"
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE. * PERFORMANCE OF THIS SOFTWARE.
*/ */
/* $Id: server.h,v 1.83 2006/03/09 23:39:00 marka Exp $ */ /* $Id: server.h,v 1.84 2006/12/04 01:52:45 marka Exp $ */
#ifndef NAMED_SERVER_H #ifndef NAMED_SERVER_H
#define NAMED_SERVER_H 1 #define NAMED_SERVER_H 1
...@@ -203,6 +203,18 @@ ns_server_flushname(ns_server_t *server, char *args); ...@@ -203,6 +203,18 @@ ns_server_flushname(ns_server_t *server, char *args);
isc_result_t isc_result_t
ns_server_status(ns_server_t *server, isc_buffer_t *text); ns_server_status(ns_server_t *server, isc_buffer_t *text);
/*%
* Report a list of dynamic and static tsig keys, per view.
*/
isc_result_t
ns_server_tsiglist(ns_server_t *server, isc_buffer_t *text);
/*%
* Delete a specific key (with optional view).
*/
isc_result_t
ns_server_tsigdelete(ns_server_t *server, char *command, isc_buffer_t *text);
/*% /*%
* Enable or disable updates for a zone. * Enable or disable updates for a zone.
*/ */
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE. * PERFORMANCE OF THIS SOFTWARE.
*/ */
/* $Id: notify.c,v 1.33 2005/04/29 00:22:29 marka Exp $ */ /* $Id: notify.c,v 1.34 2006/12/04 01:52:45 marka Exp $ */
#include <config.h> #include <config.h>
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <dns/message.h> #include <dns/message.h>
#include <dns/rdataset.h> #include <dns/rdataset.h>
#include <dns/result.h> #include <dns/result.h>
#include <dns/tsig.h>
#include <dns/view.h> #include <dns/view.h>
#include <dns/zone.h> #include <dns/zone.h>
#include <dns/zt.h> #include <dns/zt.h>
...@@ -80,7 +81,7 @@ ns_notify_start(ns_client_t *client) { ...@@ -80,7 +81,7 @@ ns_notify_start(ns_client_t *client) {
dns_zone_t *zone = NULL; dns_zone_t *zone = NULL;
char namebuf[DNS_NAME_FORMATSIZE]; char namebuf[DNS_NAME_FORMATSIZE];
char tsigbuf[DNS_NAME_FORMATSIZE + sizeof(": TSIG ''")]; char tsigbuf[DNS_NAME_FORMATSIZE + sizeof(": TSIG ''")];
dns_name_t *tsigname; dns_tsigkey_t *tsigkey;
/* /*
* Interpret the question section. * Interpret the question section.
...@@ -119,10 +120,20 @@ ns_notify_start(ns_client_t *client) { ...@@ -119,10 +120,20 @@ ns_notify_start(ns_client_t *client) {
goto formerr; goto formerr;
} }
tsigname = NULL; tsigkey = dns_message_gettsigkey(request);
if (dns_message_gettsig(request, &tsigname) != NULL) { if (tsigkey != NULL) {
dns_name_format(tsigname, namebuf, sizeof(namebuf)); dns_name_format(&tsigkey->name, namebuf, sizeof(namebuf));
snprintf(tsigbuf, sizeof(tsigbuf), ": TSIG '%s'", namebuf);
if (tsigkey->generated) {
char cnamebuf[DNS_NAME_FORMATSIZE];
dns_name_format(tsigkey->creator, cnamebuf,
sizeof(cnamebuf));
snprintf(tsigbuf, sizeof(tsigbuf), ": TSIG '%s' (%s)",
namebuf, cnamebuf);
} else {
snprintf(tsigbuf, sizeof(tsigbuf), ": TSIG '%s'",
namebuf);
}
} else } else
tsigbuf[0] = '\0'; tsigbuf[0] = '\0';
dns_name_format(zonename, namebuf, sizeof(namebuf)); dns_name_format(zonename, namebuf, sizeof(namebuf));
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE. * PERFORMANCE OF THIS SOFTWARE.
*/ */
/* $Id: server.c,v 1.466 2006/07/24 05:51:22 marka Exp $ */ /* $Id: server.c,v 1.467 2006/12/04 01:52:45 marka Exp $ */
/*! \file */ /*! \file */
...@@ -60,6 +60,7 @@ ...@@ -60,6 +60,7 @@
#include <dns/order.h> #include <dns/order.h>
#include <dns/peer.h> #include <dns/peer.h>
#include <dns/portlist.h> #include <dns/portlist.h>
#include <dns/rbt.h>
#include <dns/rdataclass.h> #include <dns/rdataclass.h>
#include <dns/rdataset.h> #include <dns/rdataset.h>
#include <dns/rdatastruct.h> #include <dns/rdatastruct.h>
...@@ -68,6 +69,7 @@ ...@@ -68,6 +69,7 @@
#include <dns/secalg.h> #include <dns/secalg.h>
#include <dns/stats.h> #include <dns/stats.h>
#include <dns/tkey.h> #include <dns/tkey.h>
#include <dns/tsig.h>
#include <dns/view.h> #include <dns/view.h>
#include <dns/zone.h> #include <dns/zone.h>
#include <dns/zt.h> #include <dns/zt.h>
...@@ -4687,6 +4689,235 @@ ns_server_status(ns_server_t *server, isc_buffer_t *text) { ...@@ -4687,6 +4689,235 @@ ns_server_status(ns_server_t *server, isc_buffer_t *text) {
return (ISC_R_SUCCESS); return (ISC_R_SUCCESS);
} }
static isc_result_t
delete_keynames(dns_tsig_keyring_t *ring, char *target,
unsigned int *foundkeys)
{
char namestr[DNS_NAME_FORMATSIZE];
isc_result_t result;
dns_rbtnodechain_t chain;
dns_name_t foundname;
dns_fixedname_t fixedorigin;
dns_name_t *origin;
dns_rbtnode_t *node;
dns_tsigkey_t *tkey;
dns_name_init(&foundname, NULL);
dns_fixedname_init(&fixedorigin);
origin = dns_fixedname_name(&fixedorigin);
again:
dns_rbtnodechain_init(&chain, ring->mctx);
result = dns_rbtnodechain_first(&chain, ring->keys, &foundname,
origin);
if (result == ISC_R_NOTFOUND) {
dns_rbtnodechain_invalidate(&chain);
return (ISC_R_SUCCESS);
}
if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
dns_rbtnodechain_invalidate(&chain);
return (result);
}
for (;;) {
node = NULL;
dns_rbtnodechain_current(&chain, &foundname, origin, &node);
tkey = node->data;
if (tkey != NULL) {
if (!tkey->generated)
goto nextkey;
dns_name_format(&tkey->name, namestr, sizeof(namestr));
if (strcmp(namestr, target) == 0) {
(*foundkeys)++;
dns_rbtnodechain_invalidate(&chain);
(void)dns_rbt_deletename(ring->keys,
&tkey->name,
ISC_FALSE);
goto again;
}
}
nextkey:
result = dns_rbtnodechain_next(&chain, &foundname, origin);
if (result == ISC_R_NOMORE)
break;
if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
dns_rbtnodechain_invalidate(&chain);
return (result);
}
}
return (ISC_R_SUCCESS);
}
isc_result_t
ns_server_tsigdelete(ns_server_t *server, char *command, isc_buffer_t *text) {
isc_result_t result;
unsigned int n;
dns_view_t *view;
unsigned int foundkeys = 0;
char *target;
char *viewname;
(void)next_token(&command, " \t"); /* skip command name */
target = next_token(&command, " \t");
if (target == NULL)
return (ISC_R_UNEXPECTEDEND);
viewname = next_token(&command, " \t");
result = isc_task_beginexclusive(server->task);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
for (view = ISC_LIST_HEAD(server->viewlist);
view != NULL;
view = ISC_LIST_NEXT(view, link)) {
if (viewname == NULL || strcmp(view->name, viewname) == 0) {
RWLOCK(&view->dynamickeys->lock, isc_rwlocktype_write);
result = delete_keynames(view->dynamickeys, target,
&foundkeys);
RWUNLOCK(&view->dynamickeys->lock,
isc_rwlocktype_write);
if (result != ISC_R_SUCCESS) {
isc_task_endexclusive(server->task);
return (result);
}
}
}
isc_task_endexclusive(server->task);
n = snprintf((char *)isc_buffer_used(text),
isc_buffer_availablelength(text),
"%d tsig keys deleted.\n", foundkeys);
if (n >= isc_buffer_availablelength(text)) {
isc_task_endexclusive(server->task);
return (ISC_R_NOSPACE);
}
isc_buffer_add(text, n);
return (ISC_R_SUCCESS);
}
static isc_result_t
list_keynames(dns_view_t *view, dns_tsig_keyring_t *ring, isc_buffer_t *text,
unsigned int *foundkeys)
{
char namestr[DNS_NAME_FORMATSIZE];
char creatorstr[DNS_NAME_FORMATSIZE];
isc_result_t result;
dns_rbtnodechain_t chain;
dns_name_t foundname;
dns_fixedname_t fixedorigin;
dns_name_t *origin;
dns_rbtnode_t *node;
dns_tsigkey_t *tkey;
unsigned int n;
const char *viewname;
if (view != NULL)
viewname = view->name;
else
viewname = "(global)";
dns_name_init(&foundname, NULL);
dns_fixedname_init(&fixedorigin);
origin = dns_fixedname_name(&fixedorigin);
dns_rbtnodechain_init(&chain, ring->mctx);
result = dns_rbtnodechain_first(&chain, ring->keys, &foundname,
origin);
if (result == ISC_R_NOTFOUND) {
dns_rbtnodechain_invalidate(&chain);
return (ISC_R_SUCCESS);
}
if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
dns_rbtnodechain_invalidate(&chain);
return (result);
}
for (;;) {
node = NULL;
dns_rbtnodechain_current(&chain, &foundname, origin, &node);
tkey = node->data;
if (tkey != NULL) {
(*foundkeys)++;
dns_name_format(&tkey->name, namestr, sizeof(namestr));
if (tkey->generated) {
dns_name_format(tkey->creator, creatorstr,
sizeof(creatorstr));
n = snprintf((char *)isc_buffer_used(text),
isc_buffer_availablelength(text),
"view \"%s\"; type \"dynamic\"; key \"%s\"; creator \"%s\";\n",
viewname, namestr, creatorstr);
} else {
n = snprintf((char *)isc_buffer_used(text),
isc_buffer_availablelength(text),
"view \"%s\"; type \"static\"; key \"%s\";\n",
viewname, namestr);
}
if (n >= isc_buffer_availablelength(text)) {
dns_rbtnodechain_invalidate(&chain);
return (ISC_R_NOSPACE);
}
isc_buffer_add(text, n);
}
result = dns_rbtnodechain_next(&chain, &foundname, origin);
if (result == ISC_R_NOMORE)
break;
if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
dns_rbtnodechain_invalidate(&chain);
return (result);
}
}
return (ISC_R_SUCCESS);
}
isc_result_t
ns_server_tsiglist(ns_server_t *server, isc_buffer_t *text) {
isc_result_t result;
unsigned int n;
dns_view_t *view;
unsigned int foundkeys = 0;
result = isc_task_beginexclusive(server->task);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
for (view = ISC_LIST_HEAD(server->viewlist);
view != NULL;
view = ISC_LIST_NEXT(view, link)) {
RWLOCK(&view->statickeys->lock, isc_rwlocktype_read);
result = list_keynames(view, view->statickeys, text,
&foundkeys);
RWUNLOCK(&view->statickeys->lock, isc_rwlocktype_read);
if (result != ISC_R_SUCCESS) {
isc_task_endexclusive(server->task);
return (result);
}
RWLOCK(&view->dynamickeys->lock, isc_rwlocktype_read);
result = list_keynames(view, view->dynamickeys, text,
&foundkeys);
RWUNLOCK(&view->dynamickeys->lock, isc_rwlocktype_read);
if (result != ISC_R_SUCCESS) {
isc_task_endexclusive(server->task);
return (result);
}
}
isc_task_endexclusive(server->task);
if (foundkeys == 0) {
n = snprintf((char *)isc_buffer_used(text),
isc_buffer_availablelength(text),
"no tsig keys found.\n");
if (n >= isc_buffer_availablelength(text)) {
isc_task_endexclusive(server->task);
return (ISC_R_NOSPACE);
}
isc_buffer_add(text, n);
}
return (ISC_R_SUCCESS);
}
/* /*
* Act on a "freeze" or "thaw" command from the command channel. * Act on a "freeze" or "thaw" command from the command channel.
*/ */
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE. * PERFORMANCE OF THIS SOFTWARE.
*/ */
/* $Id: tkeyconf.c,v 1.26 2006/03/02 00:37:23 marka Exp $ */ /* $Id: tkeyconf.c,v 1.27 2006/12/04 01:52:45 marka Exp $ */
/*! \file */ /*! \file */
...@@ -42,6 +42,13 @@ ...@@ -42,6 +42,13 @@
goto failure; \ goto failure; \
} while (0) } while (0)
#include<named/log.h>
#define LOG(msg) \
isc_log_write(ns_g_lctx, \
NS_LOGCATEGORY_GENERAL, \
NS_LOGMODULE_SERVER, \
ISC_LOG_ERROR, \
"%s", msg)
isc_result_t isc_result_t
ns_tkeyctx_fromconfig(const cfg_obj_t *options, isc_mem_t *mctx, ns_tkeyctx_fromconfig(const cfg_obj_t *options, isc_mem_t *mctx,
...@@ -100,6 +107,7 @@ ns_tkeyctx_fromconfig(const cfg_obj_t *options, isc_mem_t *mctx, ...@@ -100,6 +107,7 @@ ns_tkeyctx_fromconfig(const cfg_obj_t *options, isc_mem_t *mctx,
result = cfg_map_get(options, "tkey-gssapi-credential", &obj); result = cfg_map_get(options, "tkey-gssapi-credential", &obj);
if (result == ISC_R_SUCCESS) { if (result == ISC_R_SUCCESS) {
s = cfg_obj_asstring(obj); s = cfg_obj_asstring(obj);
isc_buffer_init(&b, s, strlen(s)); isc_buffer_init(&b, s, strlen(s));
isc_buffer_add(&b, strlen(s)); isc_buffer_add(&b, strlen(s));
dns_fixedname_init(&fname); dns_fixedname_init(&fname);
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE. * PERFORMANCE OF THIS SOFTWARE.
*/ */
/* $Id: update.c,v 1.129 2006/03/06 01:27:51 marka Exp $ */ /* $Id: update.c,v 1.130 2006/12/04 01:52:45 marka Exp $ */
#include <config.h> #include <config.h>