Fix denial of service - memory corruption.
[samba.git] / source3 / nmbd / nmbd_packets.c
index cb13febe466830eadb4750f572be98c841363148..1c570ea617dbeedfa2d86bda4f4edd1833373d9f 100644 (file)
@@ -28,9 +28,7 @@ extern int global_nmb_port;
 
 extern int num_response_packets;
 
-static void queue_packet(struct packet_struct *packet);
-
-BOOL rescan_listen_set = False;
+bool rescan_listen_set = False;
 
 
 /*******************************************************************
@@ -50,7 +48,7 @@ static int find_subnet_fd_for_address( struct in_addr local_ip )
        struct subnet_record *subrec;
 
        for( subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
-               if(ip_equal(local_ip, subrec->myip))
+               if(ip_equal_v4(local_ip, subrec->myip))
                        return subrec->nmb_sock;
 
        return ClientNMB;
@@ -65,7 +63,7 @@ static int find_subnet_mailslot_fd_for_address( struct in_addr local_ip )
        struct subnet_record *subrec;
 
        for( subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
-               if(ip_equal(local_ip, subrec->myip))
+               if(ip_equal_v4(local_ip, subrec->myip))
                        return subrec->dgram_sock;
 
        return ClientDGRAM;
@@ -141,9 +139,9 @@ static uint16 generate_name_trn_id(void)
  Either loops back or sends out a completed NetBIOS packet.
 **************************************************************************/
 
-static BOOL send_netbios_packet(struct packet_struct *p)
+static bool send_netbios_packet(struct packet_struct *p)
 {
-       BOOL loopback_this_packet = False;
+       bool loopback_this_packet = False;
 
        /* Check if we are sending to or from ourselves as a WINS server. */
        if(ismyip_v4(p->ip) && (p->port == global_nmb_port))
@@ -174,7 +172,7 @@ static BOOL send_netbios_packet(struct packet_struct *p)
 **************************************************************************/
 
 static struct packet_struct *create_and_init_netbios_packet(struct nmb_name *nmbname,
-                                                            BOOL bcast, BOOL rec_des,
+                                                            bool bcast, bool rec_des,
                                                             struct in_addr to_ip)
 {
        struct packet_struct *packet = NULL;
@@ -221,7 +219,7 @@ static struct packet_struct *create_and_init_netbios_packet(struct nmb_name *nmb
  Sets up the common elements of register, refresh or release packet.
 **************************************************************************/
 
-static BOOL create_and_init_additional_record(struct packet_struct *packet,
+static bool create_and_init_additional_record(struct packet_struct *packet,
                                                      uint16 nb_flags,
                                                      const struct in_addr *register_ip)
 {
@@ -269,7 +267,7 @@ static BOOL create_and_init_additional_record(struct packet_struct *packet,
  Sends out a name query.
 **************************************************************************/
 
-static BOOL initiate_name_query_packet( struct packet_struct *packet)
+static bool initiate_name_query_packet( struct packet_struct *packet)
 {
        struct nmb_packet *nmb = NULL;
 
@@ -291,7 +289,7 @@ static BOOL initiate_name_query_packet( struct packet_struct *packet)
  Sends out a name query - from a WINS server. 
 **************************************************************************/
 
-static BOOL initiate_name_query_packet_from_wins_server( struct packet_struct *packet)
+static bool initiate_name_query_packet_from_wins_server( struct packet_struct *packet)
 {   
        struct nmb_packet *nmb = NULL;
   
@@ -313,7 +311,7 @@ static BOOL initiate_name_query_packet_from_wins_server( struct packet_struct *p
  Sends out a name register.
 **************************************************************************/
 
-static BOOL initiate_name_register_packet( struct packet_struct *packet,
+static bool initiate_name_register_packet( struct packet_struct *packet,
                                     uint16 nb_flags, const struct in_addr *register_ip)
 {
        struct nmb_packet *nmb = &packet->packet.nmb;
@@ -337,7 +335,7 @@ static BOOL initiate_name_register_packet( struct packet_struct *packet,
  Sends out a multihomed name register.
 **************************************************************************/
 
-static BOOL initiate_multihomed_name_register_packet(struct packet_struct *packet,
+static bool initiate_multihomed_name_register_packet(struct packet_struct *packet,
                                                     uint16 nb_flags, struct in_addr *register_ip)
 {
        struct nmb_packet *nmb = &packet->packet.nmb;
@@ -365,7 +363,7 @@ for name %s IP %s (bcast=%s) to IP %s\n",
  Sends out a name refresh.
 **************************************************************************/
 
-static BOOL initiate_name_refresh_packet( struct packet_struct *packet,
+static bool initiate_name_refresh_packet( struct packet_struct *packet,
                                    uint16 nb_flags, struct in_addr *refresh_ip)
 {
        struct nmb_packet *nmb = &packet->packet.nmb;
@@ -389,7 +387,7 @@ static BOOL initiate_name_refresh_packet( struct packet_struct *packet,
  Sends out a name release.
 **************************************************************************/
 
-static BOOL initiate_name_release_packet( struct packet_struct *packet,
+static bool initiate_name_release_packet( struct packet_struct *packet,
                                    uint16 nb_flags, struct in_addr *release_ip)
 {
        struct nmb_packet *nmb = &packet->packet.nmb;
@@ -413,7 +411,7 @@ static BOOL initiate_name_release_packet( struct packet_struct *packet,
  Sends out a node status.
 **************************************************************************/
 
-static BOOL initiate_node_status_packet( struct packet_struct *packet )
+static bool initiate_node_status_packet( struct packet_struct *packet )
 {
        struct nmb_packet *nmb = &packet->packet.nmb;
 
@@ -442,7 +440,7 @@ static BOOL initiate_node_status_packet( struct packet_struct *packet )
  broadcast subnet.
 ****************************************************************************/
 
-static BOOL assert_check_subnet(struct subnet_record *subrec)
+static bool assert_check_subnet(struct subnet_record *subrec)
 {
        if( subrec == remote_broadcast_subnet) {
                DEBUG(0,("assert_check_subnet: Attempt to send packet on remote broadcast subnet. \
@@ -478,7 +476,7 @@ struct response_record *queue_register_name( struct subnet_record *subrec,
                return NULL;
 
        in_addr_to_sockaddr_storage(&ss, subrec->bcast_ip);
-       pss = iface_ip(&ss);
+       pss = iface_ip((struct sockaddr *)&ss);
        if (!pss || pss->ss_family != AF_INET) {
                p->locked = False;
                free_packet(p);
@@ -587,7 +585,7 @@ struct response_record *queue_register_multihomed_name( struct subnet_record *su
 {
        struct packet_struct *p;
        struct response_record *rrec;
-       BOOL ret;
+       bool ret;
        
        /* Sanity check. */
        if(subrec != unicast_subnet) {
@@ -872,7 +870,7 @@ void reply_netbios_packet(struct packet_struct *orig_packet,
        struct nmb_packet *nmb = NULL;
        struct res_rec answers;
        struct nmb_packet *orig_nmb = &orig_packet->packet.nmb;
-       BOOL loopback_this_packet = False;
+       bool loopback_this_packet = False;
        int rr_type = RR_TYPE_NB;
        const char *packet_type = "unknown";
 
@@ -970,6 +968,12 @@ for id %hu\n", packet_type, nmb_namestr(&orig_nmb->question.question_name),
        nmb->answers->ttl      = ttl;
 
        if (data && len) {
+               if (len < 0 || len > sizeof(nmb->answers->rdata)) {
+                       DEBUG(5,("reply_netbios_packet: "
+                               "invalid packet len (%d)\n",
+                               len ));
+                       return;
+               }
                nmb->answers->rdlength = len;
                memcpy(nmb->answers->rdata, data, len);
        }
@@ -998,7 +1002,7 @@ for id %hu\n", packet_type, nmb_namestr(&orig_nmb->question.question_name),
   Queue a packet into a packet queue
 ******************************************************************/
 
-static void queue_packet(struct packet_struct *packet)
+void queue_packet(struct packet_struct *packet)
 {
        struct packet_struct *p;
 
@@ -1179,7 +1183,7 @@ command code %d from %s IP %s to %s\n", subrec->subnet_name, command, nmb_namest
   stage as subsequent processing is expensive. 
 ****************************************************************************/
 
-static BOOL listening(struct packet_struct *p,struct nmb_name *nbname)
+static bool listening(struct packet_struct *p,struct nmb_name *nbname)
 {
        struct subnet_record *subrec = NULL;
 
@@ -1307,9 +1311,9 @@ packet sent to name %s from IP %s\n",
   Validate a response nmb packet.
 ****************************************************************************/
 
-static BOOL validate_nmb_response_packet( struct nmb_packet *nmb )
+static bool validate_nmb_response_packet( struct nmb_packet *nmb )
 {
-       BOOL ignore = False;
+       bool ignore = False;
 
        switch (nmb->header.opcode) {
                case NMB_NAME_REG_OPCODE:
@@ -1358,9 +1362,9 @@ static BOOL validate_nmb_response_packet( struct nmb_packet *nmb )
   Validate a request nmb packet.
 ****************************************************************************/
 
-static BOOL validate_nmb_packet( struct nmb_packet *nmb )
+static bool validate_nmb_packet( struct nmb_packet *nmb )
 {
-       BOOL ignore = False;
+       bool ignore = False;
 
        switch (nmb->header.opcode) {
                case NMB_NAME_REG_OPCODE:
@@ -1607,6 +1611,8 @@ void retransmit_or_expire_response_records(time_t t)
        for (subrec = FIRST_SUBNET; subrec; subrec = get_next_subnet_maybe_unicast_or_wins_server(subrec)) {
                struct response_record *rrec, *nextrrec;
 
+  restart:
+
                for (rrec = subrec->responselist; rrec; rrec = nextrrec) {
                        nextrrec = rrec->next;
 
@@ -1645,6 +1651,9 @@ on subnet %s\n", rrec->response_id, inet_ntoa(rrec->packet->ip), subrec->subnet_
                                                                        no timeout function. */
                                                        remove_response_record(subrec, rrec);
                                                }
+                                               /* We have changed subrec->responselist,
+                                                * restart from the beginning of this list. */
+                                               goto restart;
                                        } /* !rrec->in_expitation_processing */
                                } /* rrec->repeat_count > 0 */
                        } /* rrec->repeat_time <= t */
@@ -1657,7 +1666,7 @@ on subnet %s\n", rrec->response_id, inet_ntoa(rrec->packet->ip), subrec->subnet_
   plus the broadcast sockets.
 ***************************************************************************/
 
-static BOOL create_listen_fdset(fd_set **ppset, int **psock_array, int *listen_number, int *maxfd)
+static bool create_listen_fdset(fd_set **ppset, int **psock_array, int *listen_number, int *maxfd)
 {
        int *sock_array = NULL;
        struct subnet_record *subrec = NULL;
@@ -1674,7 +1683,7 @@ static BOOL create_listen_fdset(fd_set **ppset, int **psock_array, int *listen_n
        for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
                count++;
 
-       if((count*2) + 2 > FD_SETSIZE) {
+       if((count*2) + 2 >= FD_SETSIZE) {
                DEBUG(0,("create_listen_fdset: Too many file descriptors needed (%d). We can \
 only use %d.\n", (count*2) + 2, FD_SETSIZE));
                SAFE_FREE(pset);
@@ -1690,24 +1699,44 @@ only use %d.\n", (count*2) + 2, FD_SETSIZE));
        FD_ZERO(pset);
 
        /* Add in the broadcast socket on 137. */
+       if (ClientNMB < 0 || ClientNMB >= FD_SETSIZE) {
+               errno = EBADF;
+               SAFE_FREE(pset);
+               return True;
+       }
+
        FD_SET(ClientNMB,pset);
        sock_array[num++] = ClientNMB;
        *maxfd = MAX( *maxfd, ClientNMB);
 
        /* Add in the 137 sockets on all the interfaces. */
        for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+               if (subrec->nmb_sock < 0 || subrec->nmb_sock >= FD_SETSIZE) {
+                       /* We have to ignore sockets outside FD_SETSIZE. */
+                       continue;
+               }
                FD_SET(subrec->nmb_sock,pset);
                sock_array[num++] = subrec->nmb_sock;
                *maxfd = MAX( *maxfd, subrec->nmb_sock);
        }
 
        /* Add in the broadcast socket on 138. */
+       if (ClientDGRAM < 0 || ClientDGRAM >= FD_SETSIZE) {
+               errno = EBADF;
+               SAFE_FREE(pset);
+               return True;
+       }
+
        FD_SET(ClientDGRAM,pset);
        sock_array[num++] = ClientDGRAM;
        *maxfd = MAX( *maxfd, ClientDGRAM);
 
        /* Add in the 138 sockets on all the interfaces. */
        for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+               if (subrec->dgram_sock < 0 || subrec->dgram_sock >= FD_SETSIZE) {
+                       /* We have to ignore sockets outside FD_SETSIZE. */
+                       continue;
+               }
                FD_SET(subrec->dgram_sock,pset);
                sock_array[num++] = subrec->dgram_sock;
                *maxfd = MAX( *maxfd, subrec->dgram_sock);
@@ -1729,7 +1758,7 @@ only use %d.\n", (count*2) + 2, FD_SETSIZE));
   return True if the socket is dead
 ***************************************************************************/
 
-BOOL listen_for_packets(BOOL run_election)
+bool listen_for_packets(bool run_election)
 {
        static fd_set *listen_set = NULL;
        static int listen_number = 0;
@@ -1758,12 +1787,17 @@ BOOL listen_for_packets(BOOL run_election)
 
 #ifndef SYNC_DNS
        dns_fd = asyncdns_fd();
-       if (dns_fd != -1) {
+       if (dns_fd >= 0 && dns_fd < FD_SETSIZE) {
                FD_SET(dns_fd, &r_fds);
                maxfd = MAX( maxfd, dns_fd);
        }
 #endif
 
+       /* Process a signal and timer events now... */
+       if (run_events(nmbd_event_context(), 0, NULL, NULL)) {
+               return False;
+       }
+
        /*
         * During elections and when expecting a netbios response packet we
         * need to send election packets at tighter intervals.
@@ -1780,28 +1814,13 @@ BOOL listen_for_packets(BOOL run_election)
                                         &r_fds, &w_fds, &timeout, &maxfd);
        }
 
-       if (timeval_is_zero(&timeout)) {
-               /* Process a timed event now... */
-               if (run_events(nmbd_event_context(), 0, NULL, NULL)) {
-                       return False;
-               }
-       }
-
-       /* Prepare for the select - allow certain signals. */
-
-       BlockSignals(False, SIGTERM);
-
        selrtn = sys_select(maxfd+1,&r_fds,&w_fds,NULL,&timeout);
 
-       /* We can only take signals when we are in the select - block them again here. */
-
-       BlockSignals(True, SIGTERM);
-
-       if(selrtn == -1) {
+       if (run_events(nmbd_event_context(), selrtn, &r_fds, &w_fds)) {
                return False;
        }
 
-       if (run_events(nmbd_event_context(), selrtn, &r_fds, &w_fds)) {
+       if (selrtn == -1) {
                return False;
        }
 
@@ -1874,13 +1893,13 @@ BOOL listen_for_packets(BOOL run_election)
   Construct and send a netbios DGRAM.
 **************************************************************************/
 
-BOOL send_mailslot(BOOL unique, const char *mailslot,char *buf, size_t len,
+bool send_mailslot(bool unique, const char *mailslot,char *buf, size_t len,
                    const char *srcname, int src_type,
                    const char *dstname, int dest_type,
                    struct in_addr dest_ip,struct in_addr src_ip,
                   int dest_port)
 {
-       BOOL loopback_this_packet = False;
+       bool loopback_this_packet = False;
        struct packet_struct p;
        struct dgram_packet *dgram = &p.packet.dgram;
        char *ptr,*p2;
@@ -1912,7 +1931,13 @@ BOOL send_mailslot(BOOL unique, const char *mailslot,char *buf, size_t len,
        /* Setup the smb part. */
        ptr -= 4; /* XXX Ugliness because of handling of tcp SMB length. */
        memcpy(tmp,ptr,4);
-       set_message(ptr,17,strlen(mailslot) + 1 + len,True);
+
+       if (smb_size + 17*2 + strlen(mailslot) + 1 + len > MAX_DGRAM_SIZE) {
+               DEBUG(0, ("send_mailslot: Cannot write beyond end of packet\n"));
+               return false;
+       }
+
+       cli_set_message(ptr,17,strlen(mailslot) + 1 + len,True);
        memcpy(ptr,tmp,4);
 
        SCVAL(ptr,smb_com,SMBtrans);