From 805f5d861fe84ff9bc53f974d667434b7d4c60d8 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 30 Dec 2005 12:13:46 +0000 Subject: r12606: - fix multihomed registrations always remove the addresses which are no longer valid - use the wins_challenge_send/recv calls which are also used by the replication challenge metze (This used to be commit 037b5d9f3661fd7a121d1db0a50dc7743d62a5e1) --- source4/nbt_server/config.mk | 1 + source4/nbt_server/wins/winsserver.c | 226 ++++++++++++++++++++++++++++++++++- source4/nbt_server/wins/winswack.c | 212 +------------------------------- source4/nbt_server/wins/winswack.h | 37 ++++++ 4 files changed, 265 insertions(+), 211 deletions(-) create mode 100644 source4/nbt_server/wins/winswack.h diff --git a/source4/nbt_server/config.mk b/source4/nbt_server/config.mk index 61d2a7a3d6..ac342840ac 100644 --- a/source4/nbt_server/config.mk +++ b/source4/nbt_server/config.mk @@ -17,6 +17,7 @@ OBJ_FILES = \ wins/winsserver.o \ wins/winsclient.o \ wins/winswack.o +PRIVATE_PROTO_HEADER = wins/winswack_proto.h REQUIRED_SUBSYSTEMS = \ LIBCLI_NBT WINSDB # End SUBSYSTEM NBTD_WINS diff --git a/source4/nbt_server/wins/winsserver.c b/source4/nbt_server/wins/winsserver.c index 02b2048f73..a456ed06d5 100644 --- a/source4/nbt_server/wins/winsserver.c +++ b/source4/nbt_server/wins/winsserver.c @@ -24,7 +24,9 @@ #include "includes.h" #include "nbt_server/nbt_server.h" #include "nbt_server/wins/winsdb.h" +#include "nbt_server/wins/winswack.h" #include "system/time.h" +#include "libcli/composite/composite.h" #include "smbd/service_task.h" /* @@ -163,6 +165,226 @@ static uint8_t wins_sgroup_merge(struct nbt_name_socket *nbtsock, return winsdb_modify(winssrv->wins_db, rec, WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP); } +struct wack_state { + struct wins_server *winssrv; + struct nbt_name_socket *nbtsock; + struct nbt_name_packet *request_packet; + struct winsdb_record *rec; + struct nbt_peer_socket src; + const char *reg_address; + enum wrepl_name_type new_type; + struct wins_challenge_io io; + NTSTATUS status; +}; + +/* + deny a registration request +*/ +static void wins_wack_deny(struct wack_state *s) +{ + nbtd_name_registration_reply(s->nbtsock, s->request_packet, + &s->src, NBT_RCODE_ACT); + DEBUG(4,("WINS: denied name registration request for %s from %s:%d\n", + nbt_name_string(s, s->rec->name), s->src.addr, s->src.port)); + talloc_free(s); +} + +/* + allow a registration request +*/ +static void wins_wack_allow(struct wack_state *s) +{ + NTSTATUS status; + uint32_t ttl = wins_server_ttl(s->winssrv, s->request_packet->additional[0].ttl); + struct winsdb_record *rec = s->rec, *rec2; + uint32_t i,j; + + status = winsdb_lookup(s->winssrv->wins_db, rec->name, s, &rec2); + if (!NT_STATUS_IS_OK(status) || + rec2->version != rec->version || + strcmp(rec2->wins_owner, rec->wins_owner) != 0) { + DEBUG(1,("WINS: record %s changed during WACK - failing registration\n", + nbt_name_string(s, rec->name))); + wins_wack_deny(s); + return; + } + + /* + * if the old name owner doesn't hold the name anymore + * handle the request as new registration for the new name owner + */ + if (!NT_STATUS_IS_OK(s->status)) { + uint8_t rcode; + + winsdb_delete(s->winssrv->wins_db, rec); + rcode = wins_register_new(s->nbtsock, s->request_packet, &s->src, s->new_type); + if (rcode != NBT_RCODE_OK) { + DEBUG(1,("WINS: record %s failed to register as new during WACK\n", + nbt_name_string(s, rec->name))); + wins_wack_deny(s); + return; + } + goto done; + } + + rec->expire_time = time(NULL) + ttl; + rec->registered_by = s->src.addr; + + /* + * now remove all addresses that're the client doesn't hold anymore + * and update the time stamp and owner for the ownes that are still there + */ + for (i=0; rec->addresses[i]; i++) { + BOOL found = False; + for (j=0; j < s->io.out.num_addresses; j++) { + if (strcmp(rec->addresses[i]->address, s->io.out.addresses[j]) != 0) continue; + + found = True; + break; + } + if (found) { + rec->addresses[i]->wins_owner = WINSDB_OWNER_LOCAL; + rec->addresses[i]->expire_time = rec->expire_time; + continue; + } + + winsdb_addr_list_remove(rec->addresses, rec->addresses[i]->address); + } + + rec->addresses = winsdb_addr_list_add(rec->addresses, + s->reg_address, + WINSDB_OWNER_LOCAL, + rec->expire_time); + if (rec->addresses == NULL) goto failed; + + /* if we have more than one address, this becomes implicit a MHOMED record */ + if (winsdb_addr_list_length(rec->addresses) > 1) { + rec->type = WREPL_TYPE_MHOMED; + } + + winsdb_modify(s->winssrv->wins_db, rec, WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP); + + DEBUG(4,("WINS: accepted registration of %s with address %s\n", + nbt_name_string(s, rec->name), s->reg_address)); + +done: + nbtd_name_registration_reply(s->nbtsock, s->request_packet, + &s->src, NBT_RCODE_OK); +failed: + talloc_free(s); +} + +/* + called when a name query to a current owner completes +*/ +static void wack_wins_challenge_handler(struct composite_context *c_req) +{ + struct wack_state *s = talloc_get_type(c_req->async.private_data, + struct wack_state); + BOOL found; + uint32_t i; + + s->status = wins_challenge_recv(c_req, s, &s->io); + + /* + * if the owner denies it holds the name, then allow + * the registration + */ + if (!NT_STATUS_IS_OK(s->status)) { + wins_wack_allow(s); + return; + } + + if (s->new_type == WREPL_TYPE_GROUP || s->new_type == WREPL_TYPE_SGROUP) { + DEBUG(1,("WINS: record %s failed to register as group type(%u) during WACK, it's still type(%u)\n", + nbt_name_string(s, s->rec->name), s->new_type, s->rec->type)); + wins_wack_deny(s); + return; + } + + /* + * if the owner still wants the name and doesn't reply + * with the address trying to be registered, then deny + * the registration + */ + found = False; + for (i=0; i < s->io.out.num_addresses; i++) { + if (strcmp(s->reg_address, s->io.out.addresses[i]) != 0) continue; + + found = True; + break; + } + if (!found) { + wins_wack_deny(s); + return; + } + + wins_wack_allow(s); + return; +} + + +/* + a client has asked to register a unique name that someone else owns. We + need to ask each of the current owners if they still want it. If they do + then reject the registration, otherwise allow it +*/ +static void wins_register_wack(struct nbt_name_socket *nbtsock, + struct nbt_name_packet *packet, + struct winsdb_record *rec, + const struct nbt_peer_socket *src, + enum wrepl_name_type new_type) +{ + struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, + struct nbtd_interface); + struct wins_server *winssrv = iface->nbtsrv->winssrv; + struct wack_state *s; + struct composite_context *c_req; + uint32_t ttl; + + s = talloc_zero(nbtsock, struct wack_state); + if (s == NULL) goto failed; + + /* package up the state variables for this wack request */ + s->winssrv = winssrv; + s->nbtsock = nbtsock; + s->request_packet = talloc_steal(s, packet); + s->rec = talloc_steal(s, rec); + s->reg_address = packet->additional[0].rdata.netbios.addresses[0].ipaddr; + s->new_type = new_type; + s->src.port = src->port; + s->src.addr = talloc_strdup(s, src->addr); + if (s->src.addr == NULL) goto failed; + + s->io.in.nbtd_server = iface->nbtsrv; + s->io.in.event_ctx = iface->nbtsrv->task->event_ctx; + s->io.in.name = rec->name; + s->io.in.num_addresses = winsdb_addr_list_length(rec->addresses); + s->io.in.addresses = winsdb_addr_string_list(s, rec->addresses); + if (s->io.in.addresses == NULL) goto failed; + + /* + * send a WACK to the client, specifying the maximum time it could + * take to check with the owner, plus some slack + */ + ttl = 5 + 4 * winsdb_addr_list_length(rec->addresses); + nbtd_wack_reply(nbtsock, packet, src, ttl); + + /* + * send the challenge to the old addresses + */ + c_req = wins_challenge_send(s, &s->io); + if (c_req == NULL) goto failed; + + c_req->async.fn = wack_wins_challenge_handler; + c_req->async.private_data = s; + return; + +failed: + talloc_free(s); + nbtd_name_registration_reply(nbtsock, packet, src, NBT_RCODE_SVR); +} + /* register a name */ @@ -263,7 +485,7 @@ static void nbtd_winsserver_register(struct nbt_name_socket *nbtsock, * TODO: is this correct? */ if (new_type == WREPL_TYPE_GROUP || new_type == WREPL_TYPE_GROUP) { - wins_register_wack(nbtsock, packet, rec, src); + wins_register_wack(nbtsock, packet, rec, src, new_type); return; } @@ -282,7 +504,7 @@ static void nbtd_winsserver_register(struct nbt_name_socket *nbtsock, * we have to do a WACK to see if the current owner is willing * to give up its claim */ - wins_register_wack(nbtsock, packet, rec, src); + wins_register_wack(nbtsock, packet, rec, src, new_type); return; case WREPL_TYPE_GROUP: diff --git a/source4/nbt_server/wins/winswack.c b/source4/nbt_server/wins/winswack.c index cedd66603f..9e41d14e45 100644 --- a/source4/nbt_server/wins/winswack.c +++ b/source4/nbt_server/wins/winswack.c @@ -24,23 +24,10 @@ #include "includes.h" #include "nbt_server/nbt_server.h" #include "nbt_server/wins/winsdb.h" +#include "nbt_server/wins/winswack.h" #include "system/time.h" #include "libcli/composite/composite.h" -struct wins_challenge_io { - struct { - struct nbtd_server *nbtd_server; - struct event_context *event_ctx; - struct nbt_name *name; - uint32_t num_addresses; - const char **addresses; - } in; - struct { - uint32_t num_addresses; - const char **addresses; - } out; -}; - struct wins_challenge_state { struct wins_challenge_io *io; uint32_t current_address; @@ -78,7 +65,7 @@ static void wins_challenge_handler(struct nbt_name_request *req) composite_done(ctx); } -static NTSTATUS wins_challenge_recv(struct composite_context *ctx, TALLOC_CTX *mem_ctx, struct wins_challenge_io *io) +NTSTATUS wins_challenge_recv(struct composite_context *ctx, TALLOC_CTX *mem_ctx, struct wins_challenge_io *io) { NTSTATUS status = ctx->status; struct wins_challenge_state *state = talloc_get_type(ctx->private_data, struct wins_challenge_state); @@ -95,7 +82,7 @@ static NTSTATUS wins_challenge_recv(struct composite_context *ctx, TALLOC_CTX *m return status; } -static struct composite_context *wins_challenge_send(TALLOC_CTX *mem_ctx, struct wins_challenge_io *io) +struct composite_context *wins_challenge_send(TALLOC_CTX *mem_ctx, struct wins_challenge_io *io) { struct composite_context *result; struct wins_challenge_state *state; @@ -256,199 +243,6 @@ failed: return NULL; } -struct wack_state { - struct wins_server *winssrv; - struct nbt_name_socket *nbtsock; - struct nbt_name_packet *request_packet; - struct winsdb_record *rec; - struct nbt_peer_socket src; - const char **owner_addresses; - const char *reg_address; - struct nbt_name_query query; -}; - - -/* - deny a registration request -*/ -static void wins_wack_deny(struct wack_state *state) -{ - nbtd_name_registration_reply(state->nbtsock, state->request_packet, - &state->src, NBT_RCODE_ACT); - DEBUG(4,("WINS: denied name registration request for %s from %s:%d\n", - nbt_name_string(state, state->rec->name), state->src.addr, state->src.port)); - talloc_free(state); -} - -/* - allow a registration request -*/ -static void wins_wack_allow(struct wack_state *state) -{ - NTSTATUS status; - uint32_t ttl = wins_server_ttl(state->winssrv, state->request_packet->additional[0].ttl); - struct winsdb_record *rec = state->rec, *rec2; - - status = winsdb_lookup(state->winssrv->wins_db, rec->name, state, &rec2); - if (!NT_STATUS_IS_OK(status) - || rec2->version != rec->version - || strcmp(rec2->wins_owner, rec->wins_owner) != 0) { - DEBUG(1,("WINS: record %s changed during WACK - failing registration\n", - nbt_name_string(state, rec->name))); - wins_wack_deny(state); - return; - } - - nbtd_name_registration_reply(state->nbtsock, state->request_packet, - &state->src, NBT_RCODE_OK); - - rec->expire_time = time(NULL) + ttl; - rec->registered_by = state->src.addr; - - /* TODO: is it correct to only add this address? */ - rec->addresses = winsdb_addr_list_add(rec->addresses, - state->reg_address, - WINSDB_OWNER_LOCAL, - rec->expire_time); - if (rec->addresses == NULL) goto failed; - - /* if we have more than one address, this becomes implicit a MHOMED record */ - if (winsdb_addr_list_length(rec->addresses) > 1) { - rec->type = WREPL_TYPE_MHOMED; - } - - winsdb_modify(state->winssrv->wins_db, rec, WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP); - - DEBUG(4,("WINS: accepted registration of %s with address %s\n", - nbt_name_string(state, rec->name), state->reg_address)); - -failed: - talloc_free(state); -} - -/* - called when a name query to a current owner completes -*/ -static void wins_wack_handler(struct nbt_name_request *req) -{ - struct wack_state *state = talloc_get_type(req->async.private, struct wack_state); - NTSTATUS status; - int i; - struct winsdb_record *rec = state->rec; - - status = nbt_name_query_recv(req, state, &state->query); - - /* if we timed out then try the next owner address, if any */ - if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { - state->owner_addresses++; - if (state->owner_addresses[0] == NULL) { - wins_wack_allow(state); - return; - } - state->query.in.dest_addr = state->owner_addresses[0]; - - req = nbt_name_query_send(state->nbtsock, &state->query); - if (req == NULL) goto failed; - - req->async.fn = wins_wack_handler; - req->async.private = state; - return; - } - - /* if the owner denies it holds the name, then allow - the registration */ - if (!NT_STATUS_IS_OK(status)) { - wins_wack_allow(state); - return; - } - - /* if the owner still wants the name and doesn't reply - with the address trying to be registered, then deny - the registration */ - if (!str_list_check(state->query.out.reply_addrs, state->reg_address)) { - wins_wack_deny(state); - return; - } - - /* we are going to allow the registration, but first remove any addresses - from the record that aren't in the reply from the client */ - for (i=0; state->query.out.reply_addrs[i]; i++) { - if (!winsdb_addr_list_check(rec->addresses, state->query.out.reply_addrs[i])) { - winsdb_addr_list_remove(rec->addresses, state->query.out.reply_addrs[i]); - } - } - - wins_wack_allow(state); - return; - -failed: - talloc_free(state); -} - - -/* - a client has asked to register a unique name that someone else owns. We - need to ask each of the current owners if they still want it. If they do - then reject the registration, otherwise allow it -*/ -void wins_register_wack(struct nbt_name_socket *nbtsock, - struct nbt_name_packet *packet, - struct winsdb_record *rec, - const struct nbt_peer_socket *src) -{ - struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, - struct nbtd_interface); - struct wins_server *winssrv = iface->nbtsrv->winssrv; - struct wack_state *state; - struct nbt_name_request *req; - uint32_t ttl; - - state = talloc(nbtsock, struct wack_state); - if (state == NULL) goto failed; - - /* package up the state variables for this wack request */ - state->winssrv = winssrv; - state->nbtsock = nbtsock; - state->request_packet = talloc_steal(state, packet); - state->rec = talloc_steal(state, rec); - state->owner_addresses = winsdb_addr_string_list(state, rec->addresses); - if (state->owner_addresses == NULL) goto failed; - state->reg_address = packet->additional[0].rdata.netbios.addresses[0].ipaddr; - state->src.port = src->port; - state->src.addr = talloc_strdup(state, src->addr); - if (state->src.addr == NULL) goto failed; - - /* setup a name query to the first address */ - state->query.in.name = *rec->name; - state->query.in.dest_addr = state->owner_addresses[0]; - state->query.in.broadcast = False; - state->query.in.wins_lookup = True; - state->query.in.timeout = 1; - state->query.in.retries = 2; - - /* the LOGON type is a nasty hack */ - if (rec->name->type == NBT_NAME_LOGON) { - wins_wack_allow(state); - return; - } - - /* send a WACK to the client, specifying the maximum time it could - take to check with the owner, plus some slack */ - ttl = 5 + 4 * winsdb_addr_list_length(rec->addresses); - nbtd_wack_reply(nbtsock, packet, src, ttl); - - req = nbt_name_query_send(nbtsock, &state->query); - if (req == NULL) goto failed; - - req->async.fn = wins_wack_handler; - req->async.private = state; - return; - -failed: - talloc_free(state); - nbtd_name_registration_reply(nbtsock, packet, src, NBT_RCODE_SVR); -} - /* wrepl_server needs to be able to do a name query request, but some windows servers always send the reply to port 137, regardless of the request diff --git a/source4/nbt_server/wins/winswack.h b/source4/nbt_server/wins/winswack.h new file mode 100644 index 0000000000..42105f08ff --- /dev/null +++ b/source4/nbt_server/wins/winswack.h @@ -0,0 +1,37 @@ +/* + Unix SMB/CIFS implementation. + + wins server WACK processing + + Copyright (C) Stefan Metzmacher 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. +*/ + +struct wins_challenge_io { + struct { + struct nbtd_server *nbtd_server; + struct event_context *event_ctx; + struct nbt_name *name; + uint32_t num_addresses; + const char **addresses; + } in; + struct { + uint32_t num_addresses; + const char **addresses; + } out; +}; + +#include "nbt_server/wins/winswack_proto.h" -- cgit