From e199f4cef2bac3b244d08d445421975313229283 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sat, 12 Feb 2005 11:33:42 +0000 Subject: r5358: - added initial WINS server code. It passes most of the NBT-WINS test, but doesn't yet do secure server WACK responses - added a ldap_string_to_time() function, for converting a LDAP formatted time to a time_t (This used to be commit 9aa3313b3f93e47e3f93028e072f6a23b3c22385) --- source4/include/structs.h | 2 + source4/lib/time.c | 28 ++++++- source4/libcli/nbt/nbtname.c | 4 +- source4/librpc/idl/nbt.idl | 1 + source4/nbt_server/config.mk | 5 +- source4/nbt_server/defense.c | 4 +- source4/nbt_server/nbt_server.h | 3 +- source4/nbt_server/packet.c | 58 +++++++++++-- source4/nbt_server/winsdb.c | 178 ++++++++++++++++++++++++++++++++++++++++ source4/nbt_server/winsdb.h | 46 +++++++++++ source4/nbt_server/winsserver.c | 127 ++++++++++++++++++++++++---- 11 files changed, 423 insertions(+), 33 deletions(-) create mode 100644 source4/nbt_server/winsdb.c create mode 100644 source4/nbt_server/winsdb.h (limited to 'source4') diff --git a/source4/include/structs.h b/source4/include/structs.h index 61c1f4211c..32a30d9d9d 100644 --- a/source4/include/structs.h +++ b/source4/include/structs.h @@ -173,7 +173,9 @@ struct stream_server_ops; struct nbtd_server; struct nbtd_interface; +struct wins_server; struct mutex_ops; struct ads_struct; + diff --git a/source4/lib/time.c b/source4/lib/time.c index 7a4059f646..67397e953a 100644 --- a/source4/lib/time.c +++ b/source4/lib/time.c @@ -292,9 +292,9 @@ char *http_timestring(TALLOC_CTX *mem_ctx, time_t t) return buf; } -/*************************************************************************** -return a LDAP time string - ***************************************************************************/ +/* + return a LDAP time string +*/ char *ldap_timestring(TALLOC_CTX *mem_ctx, time_t t) { struct tm *tm = gmtime(&t); @@ -311,6 +311,28 @@ char *ldap_timestring(TALLOC_CTX *mem_ctx, time_t t) tm->tm_sec); } + +/* + convert a LDAP time string to a time_t. Return 0 if unable to convert +*/ +time_t ldap_string_to_time(const char *s) +{ + struct tm tm; + + if (s == NULL) return 0; + + ZERO_STRUCT(tm); + if (sscanf(s, "%04u%02u%02u%02u%02u%02u.0Z", + &tm.tm_year, &tm.tm_mon, &tm.tm_mday, + &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) { + return 0; + } + tm.tm_year -= 1900; + tm.tm_mon -= 1; + + return timegm(&tm); +} + /**************************************************************************** Return the date and time as a string ****************************************************************************/ diff --git a/source4/libcli/nbt/nbtname.c b/source4/libcli/nbt/nbtname.c index adc66980c4..b937d0aa26 100644 --- a/source4/libcli/nbt/nbtname.c +++ b/source4/libcli/nbt/nbtname.c @@ -359,10 +359,10 @@ static const char *nbt_hex_encode(TALLOC_CTX *mem_ctx, const char *s) /* form a string for a NBT name */ -const char *nbt_name_string(TALLOC_CTX *mem_ctx, const struct nbt_name *name) +char *nbt_name_string(TALLOC_CTX *mem_ctx, const struct nbt_name *name) { TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); - const char *ret; + char *ret; if (name->scope) { ret = talloc_asprintf(mem_ctx, "%s<%02x>-%s", nbt_hex_encode(tmp_ctx, name->name), diff --git a/source4/librpc/idl/nbt.idl b/source4/librpc/idl/nbt.idl index 859cecd593..04fe0c4f90 100644 --- a/source4/librpc/idl/nbt.idl +++ b/source4/librpc/idl/nbt.idl @@ -38,6 +38,7 @@ interface nbt /* rcode values */ typedef enum { + NBT_RCODE_OK = 0x0, NBT_RCODE_FMT = 0x1, NBT_RCODE_SVR = 0x2, NBT_RCODE_NAM = 0x3, diff --git a/source4/nbt_server/config.mk b/source4/nbt_server/config.mk index 8054ebc4de..12d0a09b6b 100644 --- a/source4/nbt_server/config.mk +++ b/source4/nbt_server/config.mk @@ -10,10 +10,11 @@ ADD_OBJ_FILES = \ nbt_server/register.o \ nbt_server/query.o \ nbt_server/nodestatus.o \ - nbt_server/winsserver.o \ nbt_server/winsclient.o \ nbt_server/defense.o \ - nbt_server/packet.o + nbt_server/packet.o \ + nbt_server/winsserver.o \ + nbt_server/winsdb.o REQUIRED_SUBSYSTEMS = \ LIBCLI_NBT # End SUBSYSTEM SMB diff --git a/source4/nbt_server/defense.c b/source4/nbt_server/defense.c index 00e0e740af..c59877e4b7 100644 --- a/source4/nbt_server/defense.c +++ b/source4/nbt_server/defense.c @@ -60,8 +60,8 @@ void nbtd_request_defense(struct nbt_name_socket *nbtsock, DEBUG(2,("Defending name %s on %s against %s\n", nbt_name_string(packet, name), iface->bcast_address, src_address)); - nbtd_negative_name_registration_reply(nbtsock, packet, - src_address, src_port); + nbtd_name_registration_reply(nbtsock, packet, + src_address, src_port, NBT_RCODE_ACT); } else { nbtd_winsserver_request(nbtsock, packet, src_address, src_port); } diff --git a/source4/nbt_server/nbt_server.h b/source4/nbt_server/nbt_server.h index 309c01a639..a698ebf1a0 100644 --- a/source4/nbt_server/nbt_server.h +++ b/source4/nbt_server/nbt_server.h @@ -67,8 +67,7 @@ struct nbtd_server { our names with a WINS server */ struct nbtd_interface *wins_interface; - /* wins server database handle, if configured */ - struct ldb_wrap *wins_db; + struct wins_server *winssrv; }; diff --git a/source4/nbt_server/packet.c b/source4/nbt_server/packet.c index e6eec27fdc..6383909149 100644 --- a/source4/nbt_server/packet.c +++ b/source4/nbt_server/packet.c @@ -178,11 +178,12 @@ failed: } /* - send a name defense reply + send a name registration reply */ -void nbtd_negative_name_registration_reply(struct nbt_name_socket *nbtsock, - struct nbt_name_packet *request_packet, - const char *src_address, int src_port) +void nbtd_name_registration_reply(struct nbt_name_socket *nbtsock, + struct nbt_name_packet *request_packet, + const char *src_address, int src_port, + uint8_t rcode) { struct nbt_name_packet *packet; struct nbt_name *name = &request_packet->questions[0].name; @@ -198,7 +199,7 @@ void nbtd_negative_name_registration_reply(struct nbt_name_socket *nbtsock, NBT_FLAG_AUTHORITIVE | NBT_FLAG_RECURSION_DESIRED | NBT_FLAG_RECURSION_AVAIL | - NBT_RCODE_ACT; + rcode; packet->answers = talloc_array(packet, struct nbt_res_rec, 1); if (packet->answers == NULL) goto failed; @@ -206,10 +207,53 @@ void nbtd_negative_name_registration_reply(struct nbt_name_socket *nbtsock, packet->answers[0].name = *name; packet->answers[0].rr_type = NBT_QTYPE_NETBIOS; packet->answers[0].rr_class = NBT_QCLASS_IP; - packet->answers[0].ttl = 0; + packet->answers[0].ttl = request_packet->additional[0].ttl; packet->answers[0].rdata = request_packet->additional[0].rdata; - DEBUG(7,("Sending negative name registration reply for %s to %s:%d\n", + DEBUG(7,("Sending %s name registration reply for %s to %s:%d\n", + rcode==0?"positive":"negative", + nbt_name_string(packet, name), src_address, src_port)); + + nbt_name_reply_send(nbtsock, src_address, src_port, packet); + +failed: + talloc_free(packet); +} + + +/* + send a name release reply +*/ +void nbtd_name_release_reply(struct nbt_name_socket *nbtsock, + struct nbt_name_packet *request_packet, + const char *src_address, int src_port, + uint8_t rcode) +{ + struct nbt_name_packet *packet; + struct nbt_name *name = &request_packet->questions[0].name; + + packet = talloc_zero(nbtsock, struct nbt_name_packet); + if (packet == NULL) return; + + packet->name_trn_id = request_packet->name_trn_id; + packet->ancount = 1; + packet->operation = + NBT_FLAG_REPLY | + NBT_OPCODE_RELEASE | + NBT_FLAG_AUTHORITIVE | + rcode; + + packet->answers = talloc_array(packet, struct nbt_res_rec, 1); + if (packet->answers == NULL) goto failed; + + packet->answers[0].name = *name; + packet->answers[0].rr_type = NBT_QTYPE_NETBIOS; + packet->answers[0].rr_class = NBT_QCLASS_IP; + packet->answers[0].ttl = request_packet->additional[0].ttl; + packet->answers[0].rdata = request_packet->additional[0].rdata; + + DEBUG(7,("Sending %s name release reply for %s to %s:%d\n", + rcode==0?"positive":"negative", nbt_name_string(packet, name), src_address, src_port)); nbt_name_reply_send(nbtsock, src_address, src_port, packet); diff --git a/source4/nbt_server/winsdb.c b/source4/nbt_server/winsdb.c new file mode 100644 index 0000000000..bb7ce49b9b --- /dev/null +++ b/source4/nbt_server/winsdb.c @@ -0,0 +1,178 @@ +/* + Unix SMB/CIFS implementation. + + WINS database routines + + Copyright (C) Andrew Tridgell 2005 + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "nbt_server/nbt_server.h" +#include "nbt_server/winsdb.h" +#include "lib/ldb/include/ldb.h" +#include "db_wrap.h" + +/* + load a WINS entry from the database +*/ +struct winsdb_record *winsdb_load(struct wins_server *winssrv, + struct nbt_name *name, TALLOC_CTX *mem_ctx) +{ + struct ldb_message **res = NULL; + int ret; + struct winsdb_record *rec; + struct ldb_message_element *el; + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + const char *expr; + int i; + + expr = talloc_asprintf(tmp_ctx, "dn=%s", nbt_name_string(tmp_ctx, name)); + if (expr == NULL) goto failed; + + /* find the record in the WINS database */ + ret = ldb_search(winssrv->wins_db->ldb, NULL, LDB_SCOPE_ONELEVEL, expr, NULL, &res); + if (res != NULL) { + talloc_steal(tmp_ctx, res); + } + if (ret != 1) goto failed; + + rec = talloc(tmp_ctx, struct winsdb_record); + if (rec == NULL) goto failed; + + /* parse it into a more convenient winsdb_record structure */ + rec->name = name; + rec->state = ldb_msg_find_int(res[0], "active", WINS_REC_RELEASED); + rec->nb_flags = ldb_msg_find_int(res[0], "nbFlags", 0); + rec->expire_time = ldap_string_to_time(ldb_msg_find_string(res[0], "expires", NULL)); + rec->registered_by = ldb_msg_find_string(res[0], "registeredBy", NULL); + talloc_steal(rec, rec->registered_by); + + el = ldb_msg_find_element(res[0], "address"); + if (el == NULL) goto failed; + + rec->addresses = talloc_array(rec, const char *, el->num_values+1); + if (rec->addresses == NULL) goto failed; + + for (i=0;inum_values;i++) { + rec->addresses[i] = talloc_steal(rec->addresses, el->values[i].data); + } + rec->addresses[i] = NULL; + + talloc_steal(mem_ctx, rec); + talloc_free(tmp_ctx); + return rec; + +failed: + talloc_free(tmp_ctx); + return NULL; +} + + +/* + form a ldb_message from a winsdb_record +*/ +static struct ldb_message *winsdb_message(struct wins_server *winssrv, + struct winsdb_record *rec, TALLOC_CTX *mem_ctx) +{ + int i, ret=0; + struct ldb_context *ldb = winssrv->wins_db->ldb; + struct ldb_message *msg = ldb_msg_new(mem_ctx); + if (msg == NULL) goto failed; + + msg->dn = nbt_name_string(msg, rec->name); + if (msg->dn == NULL) goto failed; + ret |= ldb_msg_add_fmt(ldb, msg, "active", "%u", rec->state); + ret |= ldb_msg_add_fmt(ldb, msg, "nbFlags", "0x%04x", rec->nb_flags); + ret |= ldb_msg_add_string(ldb, msg, "registeredBy", rec->registered_by); + ret |= ldb_msg_add_string(ldb, msg, "expires", + ldap_timestring(msg, rec->expire_time)); + for (i=0;rec->addresses[i];i++) { + ret |= ldb_msg_add_string(ldb, msg, "address", rec->addresses[i]); + } + if (ret != 0) goto failed; + return msg; + +failed: + talloc_free(msg); + return NULL; +} + +/* + save a WINS record into the database +*/ +uint8_t winsdb_add(struct wins_server *winssrv, struct winsdb_record *rec) +{ + struct ldb_context *ldb = winssrv->wins_db->ldb; + struct ldb_message *msg; + TALLOC_CTX *tmp_ctx = talloc_new(winssrv); + int ret; + + msg = winsdb_message(winssrv, rec, tmp_ctx); + if (msg == NULL) goto failed; + ret = ldb_add(ldb, msg); + if (ret != 0) goto failed; + + talloc_free(tmp_ctx); + return NBT_RCODE_OK; + +failed: + talloc_free(tmp_ctx); + return NBT_RCODE_SVR; +} + + +/* + modify a WINS record in the database +*/ +uint8_t winsdb_modify(struct wins_server *winssrv, struct winsdb_record *rec) +{ + struct ldb_context *ldb = winssrv->wins_db->ldb; + struct ldb_message *msg; + TALLOC_CTX *tmp_ctx = talloc_new(winssrv); + int ret; + int i; + + msg = winsdb_message(winssrv, rec, tmp_ctx); + if (msg == NULL) goto failed; + + for (i=0;inum_elements;i++) { + msg->elements[i].flags = LDB_FLAG_MOD_REPLACE; + } + + ret = ldb_modify(ldb, msg); + if (ret != 0) goto failed; + + talloc_free(tmp_ctx); + return NBT_RCODE_OK; + +failed: + talloc_free(tmp_ctx); + return NBT_RCODE_SVR; +} + +/* + connect to the WINS database +*/ +NTSTATUS winsdb_init(struct wins_server *winssrv) +{ + winssrv->wins_db = ldb_wrap_connect(winssrv, lp_wins_url(), 0, NULL); + if (winssrv->wins_db == NULL) { + return NT_STATUS_INTERNAL_DB_ERROR; + } + + return NT_STATUS_OK; +} diff --git a/source4/nbt_server/winsdb.h b/source4/nbt_server/winsdb.h new file mode 100644 index 0000000000..72aeb8ef78 --- /dev/null +++ b/source4/nbt_server/winsdb.h @@ -0,0 +1,46 @@ +/* + Unix SMB/CIFS implementation. + + WINS server structures + + Copyright (C) Andrew Tridgell 2005 + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +enum wins_record_state { + WINS_REC_RELEASED=0, + WINS_REC_ACTIVE=1 +}; + +/* + each record in the database contains the following information +*/ +struct winsdb_record { + struct nbt_name *name; + uint16_t nb_flags; + enum wins_record_state state; + time_t expire_time; + const char *registered_by; + const char **addresses; +}; + +struct wins_server { + /* wins server database handle */ + struct ldb_wrap *wins_db; + + uint32_t min_ttl; + uint32_t max_ttl; +}; diff --git a/source4/nbt_server/winsserver.c b/source4/nbt_server/winsserver.c index 22cfee415c..b59a1c9fa3 100644 --- a/source4/nbt_server/winsserver.c +++ b/source4/nbt_server/winsserver.c @@ -22,28 +22,121 @@ #include "includes.h" #include "nbt_server/nbt_server.h" +#include "nbt_server/winsdb.h" +#include "system/time.h" +/* + register a new name with WINS +*/ +static uint8_t wins_register_new(struct nbt_name_socket *nbtsock, + struct nbt_name_packet *packet, + const char *src_address, int src_port) +{ + struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, + struct nbtd_interface); + struct wins_server *winssrv = iface->nbtsrv->winssrv; + struct nbt_name *name = &packet->questions[0].name; + uint32_t ttl = packet->additional[0].ttl; + struct winsdb_record rec; + + ttl = MIN(ttl, winssrv->max_ttl); + ttl = MAX(ttl, winssrv->min_ttl); + + rec.name = name; + rec.nb_flags = packet->additional[0].rdata.netbios.addresses[0].nb_flags; + rec.state = WINS_REC_ACTIVE; + rec.expire_time = time(NULL) + ttl; + rec.registered_by = src_address; + rec.addresses = str_list_make(packet, + packet->additional[0].rdata.netbios.addresses[0].ipaddr, + NULL); + + return winsdb_add(winssrv, &rec); +} +/* + register a name +*/ +static void nbtd_winsserver_register(struct nbt_name_socket *nbtsock, + struct nbt_name_packet *packet, + const char *src_address, int src_port) +{ + struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, + struct nbtd_interface); + struct wins_server *winssrv = iface->nbtsrv->winssrv; + struct nbt_name *name = &packet->questions[0].name; + struct winsdb_record *rec; + uint8_t rcode = 0; + + rec = winsdb_load(winssrv, name, packet); + if (rec == NULL) { + rcode = wins_register_new(nbtsock, packet, src_address, src_port); + } else if (rec->state != WINS_REC_ACTIVE) { + uint32_t ttl = packet->additional[0].ttl; + ttl = MIN(ttl, winssrv->max_ttl); + ttl = MAX(ttl, winssrv->min_ttl); + rec->nb_flags = packet->additional[0].rdata.netbios.addresses[0].nb_flags; + rec->state = WINS_REC_ACTIVE; + rec->expire_time = time(NULL) + ttl; + rec->registered_by = src_address; + rec->addresses = str_list_make(packet, + packet->additional[0].rdata.netbios.addresses[0].ipaddr, + NULL); + winsdb_modify(winssrv, rec); + } else { + rcode = NBT_RCODE_ACT; + } + + nbtd_name_registration_reply(nbtsock, packet, src_address, src_port, rcode); +} + + + +/* + query a name +*/ static void nbtd_winsserver_query(struct nbt_name_socket *nbtsock, struct nbt_name_packet *packet, const char *src_address, int src_port) { - nbtd_negative_name_query_reply(nbtsock, packet, src_address, src_port); -} + struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, + struct nbtd_interface); + struct wins_server *winssrv = iface->nbtsrv->winssrv; + struct nbt_name *name = &packet->questions[0].name; + struct winsdb_record *rec; + + rec = winsdb_load(winssrv, name, packet); + if (rec == NULL || rec->state != WINS_REC_ACTIVE) { + nbtd_negative_name_query_reply(nbtsock, packet, src_address, src_port); + return; + } -static void nbtd_winsserver_register(struct nbt_name_socket *nbtsock, - struct nbt_name_packet *packet, - const char *src_address, int src_port) -{ - nbtd_negative_name_registration_reply(nbtsock, packet, src_address, src_port); + nbtd_name_query_reply(nbtsock, packet, src_address, src_port, name, + 0, rec->nb_flags, rec->addresses); } - +/* + release a name +*/ static void nbtd_winsserver_release(struct nbt_name_socket *nbtsock, struct nbt_name_packet *packet, const char *src_address, int src_port) { + struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, + struct nbtd_interface); + struct wins_server *winssrv = iface->nbtsrv->winssrv; + struct nbt_name *name = &packet->questions[0].name; + struct winsdb_record *rec; + + rec = winsdb_load(winssrv, name, packet); + if (rec != NULL && rec->state == WINS_REC_ACTIVE) { + rec->state = WINS_REC_RELEASED; + winsdb_modify(winssrv, rec); + } + + /* we match w2k3 by always giving a positive reply to name releases. */ + nbtd_name_release_reply(nbtsock, packet, src_address, src_port, NBT_RCODE_OK); } @@ -54,7 +147,10 @@ void nbtd_winsserver_request(struct nbt_name_socket *nbtsock, struct nbt_name_packet *packet, const char *src_address, int src_port) { - if (packet->operation & NBT_FLAG_BROADCAST) { + struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, + struct nbtd_interface); + struct wins_server *winssrv = iface->nbtsrv->winssrv; + if ((packet->operation & NBT_FLAG_BROADCAST) || winssrv == NULL) { return; } @@ -83,14 +179,15 @@ void nbtd_winsserver_request(struct nbt_name_socket *nbtsock, NTSTATUS nbtd_winsserver_init(struct nbtd_server *nbtsrv) { if (!lp_wins_support()) { - nbtsrv->wins_db = NULL; + nbtsrv->winssrv = NULL; return NT_STATUS_OK; } - nbtsrv->wins_db = ldb_wrap_connect(nbtsrv, lp_wins_url(), 0, NULL); - if (nbtsrv->wins_db == NULL) { - return NT_STATUS_INTERNAL_DB_ERROR; - } + nbtsrv->winssrv = talloc(nbtsrv, struct wins_server); + NT_STATUS_HAVE_NO_MEMORY(nbtsrv->winssrv); + + nbtsrv->winssrv->max_ttl = lp_max_wins_ttl(); + nbtsrv->winssrv->min_ttl = lp_min_wins_ttl(); - return NT_STATUS_OK; + return winsdb_init(nbtsrv->winssrv); } -- cgit