]> git.sesse.net Git - nbtscanner/blob - nmb.c
Import nbtscanner 0.1.2.
[nbtscanner] / nmb.c
1 /*
2  * nbtscanner -- a tool for scanning large networks for SMB servers.
3  *
4  * nmb.c: Most functions directly related to the NMB protocol.
5  *
6  * Large amounts of code adapted from Samba (http://www.samba.org/)
7  * Copyright (C) Andrew Tridgell 1994-1998, and others.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  */
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27
28 #include "nmb.h"
29 #include "packet.h"
30 #include "byteorder.h"
31 #include "util.h"
32
33 void build_nbt_packet(struct nmb_packet *nmb, int name_trn_id)
34 {
35         nmb->header.name_trn_id = name_trn_id;
36         nmb->header.opcode = 0;
37         nmb->header.response = 0;
38         nmb->header.nm_flags.bcast = 0;
39         nmb->header.nm_flags.recursion_available = 0;
40         nmb->header.nm_flags.recursion_desired = 0;
41         nmb->header.nm_flags.trunc = 0;
42         nmb->header.nm_flags.authoritative = 0;
43         nmb->header.rcode = 0;
44         nmb->header.qdcount = 1;
45         nmb->header.ancount = 0;
46         nmb->header.nscount = 0;
47         nmb->header.arcount = 0;
48
49         make_nmb_name(&(nmb->question.question_name), "*", 0x0, "");
50
51         nmb->question.question_type = 0x21;
52         nmb->question.question_class = 0x1;
53 }
54
55 int build_nmb(char *buf, struct nmb_packet *nmb)
56 {
57         unsigned char *ubuf = (unsigned char *)buf;
58         int offset=0;
59
60         /* put in the header */
61         RSSVAL(ubuf,offset, nmb->header.name_trn_id);
62         ubuf[offset+2] = (nmb->header.opcode & 0xF) << 3;
63         if (nmb->header.response) ubuf[offset+2] |= (1<<7);
64         if (nmb->header.nm_flags.authoritative &&
65             nmb->header.response) ubuf[offset+2] |= 0x4;
66         if (nmb->header.nm_flags.trunc) ubuf[offset+2] |= 0x2;
67         if (nmb->header.nm_flags.recursion_desired) ubuf[offset+2] |= 0x1;
68         if (nmb->header.nm_flags.recursion_available &&
69             nmb->header.response) ubuf[offset+3] |= 0x80;
70         if (nmb->header.nm_flags.bcast) ubuf[offset+3] |= 0x10;
71         ubuf[offset+3] |= (nmb->header.rcode & 0xF);
72
73         RSSVAL(ubuf, offset+4, nmb->header.qdcount);
74         RSSVAL(ubuf, offset+6, nmb->header.ancount);
75         RSSVAL(ubuf, offset+8, nmb->header.nscount);
76         RSSVAL(ubuf, offset+10, nmb->header.arcount);
77
78         offset += 12;
79         if (nmb->header.qdcount) {
80                 /* XXXX this doesn't handle a qdcount of > 1 */
81                 offset += put_nmb_name((char *)ubuf, offset, &nmb->question.question_name);
82                 RSSVAL(ubuf,offset, nmb->question.question_type);
83                 RSSVAL(ubuf,offset+2, nmb->question.question_class);
84                 offset += 4;
85         }
86         return offset;
87 }
88
89 int parse_nmb(char *inbuf, int length, struct nmb_packet *nmb)
90 {
91         int nm_flags, offset;
92
93         memset((char *)nmb, 0, sizeof(*nmb));
94
95         if (length < 12) return(0);
96
97         /* parse the header */
98         nmb->header.name_trn_id = RSVAL(inbuf, 0);
99
100         nmb->header.opcode = (CVAL(inbuf, 2) >> 3) & 0xF;
101         nmb->header.response = ((CVAL(inbuf, 2) >> 7) & 1) ? 1 : 0;
102         nm_flags = ((CVAL(inbuf, 2) & 0x7) << 4) + (CVAL(inbuf, 3) >> 4);
103         nmb->header.nm_flags.bcast = (nm_flags & 1) ? 1 : 0;
104         nmb->header.nm_flags.recursion_available = (nm_flags & 8) ? 1 : 0;
105         nmb->header.nm_flags.recursion_desired = (nm_flags & 0x10) ? 1 : 0;
106         nmb->header.nm_flags.trunc = (nm_flags & 0x20) ? 1 : 0;
107         nmb->header.nm_flags.authoritative = (nm_flags & 0x40) ? 1 : 0;
108         nmb->header.rcode = CVAL(inbuf, 3) & 0xF;
109         nmb->header.qdcount = RSVAL(inbuf, 4);
110         nmb->header.ancount = RSVAL(inbuf, 6);
111         nmb->header.nscount = RSVAL(inbuf, 8);
112         nmb->header.arcount = RSVAL(inbuf, 10);
113
114         if (nmb->header.qdcount) {
115                 offset = parse_nmb_name(inbuf, 12, length, &nmb->question.question_name);
116                 if (!offset) return 0;
117
118                 if (length - (12+offset) < 4) return 0;
119                 nmb->question.question_type = RSVAL(inbuf, 12+offset);
120                 nmb->question.question_class = RSVAL(inbuf, 12+offset+2);
121
122                 offset += 12+4;
123         } else {
124                 offset = 12;
125         }
126
127         /* and any resource records */
128         if (nmb->header.ancount &&
129                 !parse_alloc_res_rec(inbuf, &offset, length, &nmb->answers,
130                                      nmb->header.ancount)) {
131                 return 0;
132         }
133
134         if (nmb->header.nscount &&
135                 !parse_alloc_res_rec(inbuf, &offset, length, &nmb->nsrecs,
136                                      nmb->header.nscount)) {
137                 return 0;
138         }
139
140         if (nmb->header.arcount &&
141                 !parse_alloc_res_rec(inbuf, &offset, length, &nmb->additional,
142                                      nmb->header.arcount)) {
143                 return 0;
144         }
145
146         return 1;
147 }
148
149 void make_nmb_name(struct nmb_name *n, const char *name, int type, const char *this_scope)
150 {
151         memset((char *)n, 0, sizeof(struct nmb_name));
152         strncpy(n->name, name, 15);
153         strupper(n->name);
154         n->name_type = (unsigned int)type & 0xFF;
155         strncpy(n->scope, this_scope, 63);
156         strupper(n->scope);
157 }
158
159 int parse_nmb_name(char *inbuf, int offset, int length, struct nmb_name *name)
160 {
161         int m, n = 0;
162         unsigned char *ubuf = (unsigned char *)inbuf;
163         int ret = 0;
164         int got_pointer = 0;
165         int loop_count = 0;
166
167         if (length - offset < 2)
168                 return 0;
169
170         /* handle initial name pointers */
171         if (!handle_name_ptrs(ubuf, &offset, length, &got_pointer, &ret))
172                 return 0;
173
174         m = ubuf[offset];
175
176         if (!m)
177                 return 0;
178         if ((m & 0xC0) || offset+m+2 > length)
179                 return 0;
180
181         memset((char *)name, 0, sizeof(*name));
182
183         /* the "compressed" part */
184         if (!got_pointer)
185                 ret += m + 2;
186         offset++;
187
188         while (m > 0) {
189                 unsigned char c1,c2;
190                 c1 = ubuf[offset++]-'A';
191                 c2 = ubuf[offset++]-'A';
192                 if ((c1 & 0xF0) || (c2 & 0xF0) || (n > sizeof(name->name)-1))
193                         return 0;
194                 name->name[n++] = (c1<<4) | c2;
195                 m -= 2;
196         }
197         name->name[n] = 0;
198
199         if (n==16) {
200                 /* parse out the name type,
201                    its always in the 16th byte of the name */
202                 name->name_type = ((unsigned char)name->name[15]) & 0xff;
203
204                 /* remove trailing spaces */
205                 name->name[15] = 0;
206                 n = 14;
207                 while (n && name->name[n]==' ')
208                         name->name[n--] = 0;
209         }
210
211         /* now the domain parts (if any) */
212         n = 0;
213         while (ubuf[offset]) {
214                 /* we can have pointers within the domain part as well */
215                 if (!handle_name_ptrs(ubuf,&offset,length,&got_pointer,&ret))
216                         return 0;
217
218                 m = ubuf[offset];
219                 /*
220                  * Don't allow null domain parts.
221                  */
222                 if (!m)
223                         return 0;
224                 if (!got_pointer)
225                         ret += m+1;
226                 if (n)
227                         name->scope[n++] = '.';
228                 if (m + 2 + offset > length || n + m + 1 > sizeof(name->scope))
229                         return 0;
230                 offset++;
231                 while (m--)
232                         name->scope[n++] = (char)ubuf[offset++];
233
234                 /*
235                  * Watch for malicious loops.
236                  */
237                 if (loop_count++ == 10)
238                         return 0;
239         }
240         name->scope[n++] = 0;
241
242         return ret;
243 }
244
245 int put_nmb_name(char *buf, int offset, struct nmb_name *name)
246 {
247         int ret,m;
248         char buf1[128];
249         char *p;
250
251         if (strcmp(name->name,"*") == 0) {
252                 /* special case for wildcard name */
253                 memset(buf1,'\0',20);
254                 buf1[0] = '*';
255                 buf1[15] = name->name_type;
256         } else {
257                 snprintf(buf1, sizeof(buf1) - 1,"%-15.15s%c",name->name,name->name_type);
258         }
259
260         buf[offset] = 0x20;
261         ret = 34;
262
263         for (m=0; m<16; m++) {
264                 buf[offset + 1 + 2*m] = 'A' + ((buf1[m] >> 4) & 0xF);
265                 buf[offset + 2 + 2*m] = 'A' + ( buf1[m]       & 0xF);
266         }
267         offset += 33;
268
269         buf[offset] = 0;
270
271         if (name->scope[0]) {
272                 /* XXXX this scope handling needs testing */
273                 ret += strlen(name->scope) + 1;
274                 strcpy(&buf[offset + 1], name->scope);
275
276                 p = &buf[offset + 1];
277                 while ((p = strchr(p, '.'))) {
278                         buf[offset] = PTR_DIFF(p, &buf[offset]);
279                         offset += buf[offset];
280                         p = &buf[offset+1];
281                 }
282                 buf[offset] = strlen(&buf[offset+1]);
283         }
284
285         return ret;
286 }
287
288 int handle_name_ptrs(unsigned char *ubuf, int *offset, int length,
289                              int *got_pointer, int *ret)
290 {
291         int loop_count=0;
292
293         while ((ubuf[*offset] & 0xC0) == 0xC0) {
294                 if (!*got_pointer) (*ret) += 2;
295                 (*got_pointer) = 1;
296                 (*offset) = ((ubuf[*offset] & ~0xC0) << 8) | ubuf[(*offset)+1];
297                 if (loop_count++ == 10 || (*offset) < 0 || (*offset) > (length-2)) {
298                         return 0;
299                 }
300         }
301         return 1;
302 }
303
304 int parse_alloc_res_rec(char *inbuf, int *offset, int length,
305                         struct res_rec **recs, int count)
306 {
307         int i;
308         *recs = (struct res_rec *)malloc(sizeof(**recs) * count);
309         if (!*recs) return 0;
310
311         memset((char *)*recs, 0, sizeof(**recs) * count);
312
313         for (i=0; i<count; i++) {
314                 int l = parse_nmb_name(inbuf, *offset, length, &(*recs)[i].rr_name);
315                 (*offset) += l;
316                 if (!l || (*offset)+10 > length) {
317                         free(*recs);
318                         *recs = NULL;
319                         return 0;
320                 }
321                 (*recs)[i].rr_type = RSVAL(inbuf, (*offset));
322                 (*recs)[i].rr_class = RSVAL(inbuf, (*offset)+2);
323                 (*recs)[i].ttl = RIVAL(inbuf, (*offset)+4);
324                 (*recs)[i].rdlength = RSVAL(inbuf, (*offset)+8);
325                 (*offset) += 10;
326                 if ((*recs)[i].rdlength > sizeof((*recs)[i].rdata) ||
327                     (*offset)+(*recs)[i].rdlength > length) {
328                         free(*recs);
329                         *recs = NULL;
330                         return 0;
331                 }
332                 memcpy((*recs)[i].rdata, inbuf+(*offset), (*recs)[i].rdlength);
333                 (*offset) += (*recs)[i].rdlength;
334         }
335         return 0;
336 }
337
338 void free_nmb_packet(struct nmb_packet *nmb)
339 {
340         if (nmb->answers) {
341                 free(nmb->answers);
342                 nmb->answers = NULL;
343         }
344         if (nmb->nsrecs) {
345                 free(nmb->nsrecs);
346                 nmb->nsrecs = NULL;
347         }
348         if (nmb->additional) {
349                 free(nmb->additional);
350                 nmb->additional = NULL;
351         }
352         free(nmb);
353 }
354