/src/samba/libcli/nbt/nbtname.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | |
4 | | manipulate nbt name structures |
5 | | |
6 | | Copyright (C) Andrew Tridgell 2005 |
7 | | |
8 | | This program is free software; you can redistribute it and/or modify |
9 | | it under the terms of the GNU General Public License as published by |
10 | | the Free Software Foundation; either version 3 of the License, or |
11 | | (at your option) any later version. |
12 | | |
13 | | This program is distributed in the hope that it will be useful, |
14 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | GNU General Public License for more details. |
17 | | |
18 | | You should have received a copy of the GNU General Public License |
19 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
20 | | */ |
21 | | |
22 | | /* |
23 | | see rfc1002 for the detailed format of compressed names |
24 | | */ |
25 | | |
26 | | #include "includes.h" |
27 | | #include "librpc/gen_ndr/ndr_nbt.h" |
28 | | #include "librpc/gen_ndr/ndr_misc.h" |
29 | | #include "system/locale.h" |
30 | | #include "lib/util/util_net.h" |
31 | | #include "libcli/nbt/libnbt.h" |
32 | | |
33 | | /* |
34 | | decompress a 'compressed' name component |
35 | | */ |
36 | | static bool decompress_name(char *name, enum nbt_name_type *type) |
37 | 271k | { |
38 | 271k | int i; |
39 | 299k | for (i=0;name[2*i];i++) { |
40 | 28.6k | uint8_t c1 = name[2*i]; |
41 | 28.6k | uint8_t c2 = name[1+(2*i)]; |
42 | 28.6k | if (c1 < 'A' || c1 > 'P' || |
43 | 28.6k | c2 < 'A' || c2 > 'P') { |
44 | 89 | return false; |
45 | 89 | } |
46 | 28.5k | name[i] = ((c1-'A')<<4) | (c2-'A'); |
47 | 28.5k | } |
48 | 270k | name[i] = 0; |
49 | 270k | if (i == 16) { |
50 | 1.74k | *type = (enum nbt_name_type)(name[15]); |
51 | 1.74k | name[15] = 0; |
52 | 1.74k | i--; |
53 | 269k | } else { |
54 | 269k | *type = NBT_NAME_CLIENT; |
55 | 269k | } |
56 | | |
57 | | /* trim trailing spaces */ |
58 | 272k | for (;i>0 && name[i-1]==' ';i--) { |
59 | 1.43k | name[i-1] = 0; |
60 | 1.43k | } |
61 | | |
62 | 270k | return true; |
63 | 271k | } |
64 | | |
65 | | |
66 | | /* |
67 | | compress a name component |
68 | | */ |
69 | | static uint8_t *compress_name(TALLOC_CTX *mem_ctx, |
70 | | const uint8_t *name, enum nbt_name_type type) |
71 | 136k | { |
72 | 136k | uint8_t *cname; |
73 | 136k | int i; |
74 | 136k | uint8_t pad_char; |
75 | | |
76 | 136k | if (strlen((const char *)name) > 15) { |
77 | 0 | return NULL; |
78 | 0 | } |
79 | | |
80 | 136k | cname = talloc_array(mem_ctx, uint8_t, 33); |
81 | 136k | if (cname == NULL) return NULL; |
82 | | |
83 | 158k | for (i=0;name[i];i++) { |
84 | 21.2k | cname[2*i] = 'A' + (name[i]>>4); |
85 | 21.2k | cname[1+2*i] = 'A' + (name[i]&0xF); |
86 | 21.2k | } |
87 | 136k | if (strcmp((const char *)name, "*") == 0) { |
88 | 42 | pad_char = 0; |
89 | 136k | } else { |
90 | 136k | pad_char = ' '; |
91 | 136k | } |
92 | 2.16M | for (;i<15;i++) { |
93 | 2.03M | cname[2*i] = 'A' + (pad_char>>4); |
94 | 2.03M | cname[1+2*i] = 'A' + (pad_char&0xF); |
95 | 2.03M | } |
96 | | |
97 | 136k | pad_char = type; |
98 | 136k | cname[2*i] = 'A' + (pad_char>>4); |
99 | 136k | cname[1+2*i] = 'A' + (pad_char&0xF); |
100 | | |
101 | 136k | cname[32] = 0; |
102 | 136k | return cname; |
103 | 136k | } |
104 | | |
105 | | |
106 | | /** |
107 | | pull a nbt name from the wire |
108 | | */ |
109 | | _PUBLIC_ enum ndr_err_code ndr_pull_nbt_name(struct ndr_pull *ndr, ndr_flags_type ndr_flags, struct nbt_name *r) |
110 | 271k | { |
111 | 271k | char *scope; |
112 | 271k | char *cname; |
113 | 271k | const char *s; |
114 | 271k | bool ok; |
115 | | |
116 | 271k | if (!(ndr_flags & NDR_SCALARS)) { |
117 | 0 | return NDR_ERR_SUCCESS; |
118 | 0 | } |
119 | | |
120 | 271k | NDR_CHECK(ndr_pull_nbt_string(ndr, ndr_flags, &s)); |
121 | 271k | cname = discard_const_p(char, s); |
122 | | |
123 | 271k | scope = strchr(cname, '.'); |
124 | 271k | if (scope) { |
125 | 667 | *scope = 0; |
126 | 667 | r->scope = talloc_strdup(ndr->current_mem_ctx, &scope[1]); |
127 | 667 | NDR_ERR_HAVE_NO_MEMORY(r->scope); |
128 | 270k | } else { |
129 | 270k | r->scope = NULL; |
130 | 270k | } |
131 | | |
132 | | /* the first component is limited to 16 bytes in the DOS charset, |
133 | | which is 32 in the 'compressed' form */ |
134 | 271k | if (strlen(cname) > 32) { |
135 | 11 | return ndr_pull_error(ndr, NDR_ERR_STRING, |
136 | 11 | "NBT NAME cname > 32"); |
137 | 11 | } |
138 | | |
139 | | /* decompress the first component */ |
140 | 271k | ok = decompress_name(cname, &r->type); |
141 | 271k | if (!ok) { |
142 | 89 | return ndr_pull_error(ndr, NDR_ERR_STRING, |
143 | 89 | "NBT NAME failed to decompress"); |
144 | 89 | } |
145 | | |
146 | 270k | r->name = talloc_strdup(ndr->current_mem_ctx, cname); |
147 | 270k | NDR_ERR_HAVE_NO_MEMORY(r->name); |
148 | | |
149 | 270k | talloc_free(cname); |
150 | | |
151 | 270k | return NDR_ERR_SUCCESS; |
152 | 270k | } |
153 | | |
154 | | /** |
155 | | push a nbt name to the wire |
156 | | */ |
157 | | _PUBLIC_ enum ndr_err_code ndr_push_nbt_name(struct ndr_push *ndr, ndr_flags_type ndr_flags, const struct nbt_name *r) |
158 | 136k | { |
159 | 136k | uint8_t *cname, *fullname; |
160 | 136k | enum ndr_err_code ndr_err; |
161 | | |
162 | 136k | if (!(ndr_flags & NDR_SCALARS)) { |
163 | 0 | return NDR_ERR_SUCCESS; |
164 | 0 | } |
165 | | |
166 | 136k | if (strlen(r->name) > 15) { |
167 | 0 | return ndr_push_error(ndr, NDR_ERR_STRING, |
168 | 0 | "nbt_name longer as 15 chars: %s", |
169 | 0 | r->name); |
170 | 0 | } |
171 | | |
172 | 136k | cname = compress_name(ndr, (const uint8_t *)r->name, r->type); |
173 | 136k | NDR_ERR_HAVE_NO_MEMORY(cname); |
174 | | |
175 | 136k | if (r->scope) { |
176 | 473 | fullname = (uint8_t *)talloc_asprintf(ndr, "%s.%s", cname, r->scope); |
177 | 473 | NDR_ERR_HAVE_NO_MEMORY(fullname); |
178 | 473 | talloc_free(cname); |
179 | 136k | } else { |
180 | 136k | fullname = cname; |
181 | 136k | } |
182 | | |
183 | 136k | ndr_err = ndr_push_nbt_string(ndr, ndr_flags, (const char *)fullname); |
184 | | |
185 | 136k | return ndr_err; |
186 | 136k | } |
187 | | |
188 | | |
189 | | /** |
190 | | copy a nbt name structure |
191 | | */ |
192 | | _PUBLIC_ NTSTATUS nbt_name_dup(TALLOC_CTX *mem_ctx, |
193 | | const struct nbt_name *name, |
194 | | struct nbt_name *newname) |
195 | 0 | { |
196 | 0 | *newname = *name; |
197 | 0 | newname->name = talloc_strdup(mem_ctx, newname->name); |
198 | 0 | NT_STATUS_HAVE_NO_MEMORY(newname->name); |
199 | 0 | newname->scope = talloc_strdup(mem_ctx, newname->scope); |
200 | 0 | if (name->scope) { |
201 | 0 | NT_STATUS_HAVE_NO_MEMORY(newname->scope); |
202 | 0 | } |
203 | 0 | return NT_STATUS_OK; |
204 | 0 | } |
205 | | |
206 | | /** |
207 | | push a nbt name into a blob |
208 | | */ |
209 | | _PUBLIC_ NTSTATUS nbt_name_to_blob(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct nbt_name *name) |
210 | 0 | { |
211 | 0 | enum ndr_err_code ndr_err; |
212 | |
|
213 | 0 | ndr_err = ndr_push_struct_blob(blob, mem_ctx, name, (ndr_push_flags_fn_t)ndr_push_nbt_name); |
214 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
215 | 0 | return ndr_map_error2ntstatus(ndr_err); |
216 | 0 | } |
217 | | |
218 | 0 | return NT_STATUS_OK; |
219 | 0 | } |
220 | | |
221 | | /** |
222 | | pull a nbt name from a blob |
223 | | */ |
224 | | _PUBLIC_ NTSTATUS nbt_name_from_blob(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, struct nbt_name *name) |
225 | 0 | { |
226 | 0 | enum ndr_err_code ndr_err; |
227 | |
|
228 | 0 | ndr_err = ndr_pull_struct_blob(blob, mem_ctx, name, |
229 | 0 | (ndr_pull_flags_fn_t)ndr_pull_nbt_name); |
230 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
231 | 0 | return ndr_map_error2ntstatus(ndr_err); |
232 | 0 | } |
233 | | |
234 | 0 | return NT_STATUS_OK; |
235 | 0 | } |
236 | | |
237 | | |
238 | | /** |
239 | | choose a name to use when calling a server in a NBT session request. |
240 | | we use heuristics to see if the name we have been given is a IP |
241 | | address, or a too-long name. If it is then use *SMBSERVER, or a |
242 | | truncated name |
243 | | */ |
244 | | _PUBLIC_ void nbt_choose_called_name(TALLOC_CTX *mem_ctx, |
245 | | struct nbt_name *n, const char *name, int type) |
246 | 0 | { |
247 | 0 | n->scope = NULL; |
248 | 0 | n->type = type; |
249 | |
|
250 | 0 | if ((name == NULL) || is_ipaddress(name)) { |
251 | 0 | n->name = "*SMBSERVER"; |
252 | 0 | return; |
253 | 0 | } |
254 | 0 | if (strlen(name) > 15) { |
255 | 0 | const char *p = strchr(name, '.'); |
256 | 0 | char *s; |
257 | 0 | if (p - name > 15) { |
258 | 0 | n->name = "*SMBSERVER"; |
259 | 0 | return; |
260 | 0 | } |
261 | 0 | s = talloc_strndup(mem_ctx, name, PTR_DIFF(p, name)); |
262 | 0 | n->name = talloc_strdup_upper(mem_ctx, s); |
263 | 0 | return; |
264 | 0 | } |
265 | | |
266 | 0 | n->name = talloc_strdup_upper(mem_ctx, name); |
267 | 0 | } |
268 | | |
269 | | |
270 | | /* |
271 | | escape a string into a form containing only a small set of characters, |
272 | | the rest is hex encoded. This is similar to URL encoding |
273 | | */ |
274 | | static const char *nbt_hex_encode(TALLOC_CTX *mem_ctx, const char *s) |
275 | 9.93k | { |
276 | 9.93k | int i, len; |
277 | 9.93k | char *ret; |
278 | 9.93k | const char *valid_chars = "_-.$@ "; |
279 | 36.5k | #define NBT_CHAR_ALLOW(c) (isalnum((unsigned char)c) || strchr(valid_chars, c)) |
280 | | |
281 | 28.2k | for (len=i=0;s[i];i++,len++) { |
282 | 18.2k | if (!NBT_CHAR_ALLOW(s[i])) { |
283 | 15.9k | len += 2; |
284 | 15.9k | } |
285 | 18.2k | } |
286 | | |
287 | 9.93k | ret = talloc_array(mem_ctx, char, len+1); |
288 | 9.93k | if (ret == NULL) return NULL; |
289 | | |
290 | 28.2k | for (len=i=0;s[i];i++) { |
291 | 18.2k | if (NBT_CHAR_ALLOW(s[i])) { |
292 | 2.38k | ret[len++] = s[i]; |
293 | 15.9k | } else { |
294 | 15.9k | snprintf(&ret[len], 4, "%%%02x", (unsigned char)s[i]); |
295 | 15.9k | len += 3; |
296 | 15.9k | } |
297 | 18.2k | } |
298 | 9.93k | ret[len] = 0; |
299 | | |
300 | 9.93k | return ret; |
301 | 9.93k | } |
302 | | |
303 | | |
304 | | /** |
305 | | form a string for a NBT name |
306 | | */ |
307 | | _PUBLIC_ char *nbt_name_string(TALLOC_CTX *mem_ctx, const struct nbt_name *name) |
308 | 8.93k | { |
309 | 8.93k | TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); |
310 | 8.93k | char *ret; |
311 | 8.93k | if (name->scope) { |
312 | 1.00k | ret = talloc_asprintf(mem_ctx, "%s<%02x>-%s", |
313 | 1.00k | nbt_hex_encode(tmp_ctx, name->name), |
314 | 1.00k | name->type, |
315 | 1.00k | nbt_hex_encode(tmp_ctx, name->scope)); |
316 | 7.92k | } else { |
317 | 7.92k | ret = talloc_asprintf(mem_ctx, "%s<%02x>", |
318 | 7.92k | nbt_hex_encode(tmp_ctx, name->name), |
319 | 7.92k | name->type); |
320 | 7.92k | } |
321 | 8.93k | talloc_free(tmp_ctx); |
322 | 8.93k | return ret; |
323 | 8.93k | } |
324 | | |
325 | | /** |
326 | | pull a nbt name, WINS Replication uses another on wire format for nbt name |
327 | | */ |
328 | | _PUBLIC_ enum ndr_err_code ndr_pull_wrepl_nbt_name(struct ndr_pull *ndr, ndr_flags_type ndr_flags, struct nbt_name **_r) |
329 | 60.2k | { |
330 | 60.2k | struct nbt_name *r; |
331 | 60.2k | uint8_t *namebuf; |
332 | 60.2k | uint32_t namebuf_len; |
333 | | |
334 | 60.2k | if (!(ndr_flags & NDR_SCALARS)) { |
335 | 0 | return NDR_ERR_SUCCESS; |
336 | 0 | } |
337 | | |
338 | 60.2k | NDR_CHECK(ndr_pull_align(ndr, 4)); |
339 | 60.2k | NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &namebuf_len)); |
340 | 60.2k | if (namebuf_len < 1 || namebuf_len > 255) { |
341 | 59 | return ndr_pull_error(ndr, NDR_ERR_ALLOC, "value (%"PRIu32") out of range (1 - 255)", namebuf_len); |
342 | 59 | } |
343 | 60.1k | NDR_PULL_ALLOC_N(ndr, namebuf, namebuf_len); |
344 | 60.1k | NDR_CHECK(ndr_pull_array_uint8(ndr, NDR_SCALARS, namebuf, namebuf_len)); |
345 | | |
346 | 60.1k | if ((namebuf_len % 4) == 0) { |
347 | | /* |
348 | | * [MS-WINSRA] — v20091104 was wrong |
349 | | * regarding section "2.2.10.1 Name Record" |
350 | | * |
351 | | * If the name buffer is already 4 byte aligned |
352 | | * Windows (at least 2003 SP1 and 2008) add 4 extra |
353 | | * bytes. This can happen when the name has a scope. |
354 | | */ |
355 | 10.4k | uint32_t pad; |
356 | 10.4k | NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &pad)); |
357 | 10.4k | } |
358 | | |
359 | 60.1k | NDR_PULL_ALLOC(ndr, r); |
360 | | |
361 | | /* oh wow, what a nasty bug in windows ... */ |
362 | 60.1k | if (namebuf[0] == 0x1b && namebuf_len >= 16) { |
363 | 1.12k | namebuf[0] = namebuf[15]; |
364 | 1.12k | namebuf[15] = 0x1b; |
365 | 1.12k | } |
366 | | |
367 | 60.1k | if (namebuf_len < 17) { |
368 | 42.5k | r->type = 0x00; |
369 | | |
370 | 42.5k | r->name = talloc_strndup(r, (char *)namebuf, namebuf_len); |
371 | 42.5k | if (!r->name) return ndr_pull_error(ndr, NDR_ERR_ALLOC, "out of memory"); |
372 | | |
373 | 42.5k | r->scope= NULL; |
374 | | |
375 | 42.5k | talloc_free(namebuf); |
376 | 42.5k | *_r = r; |
377 | 42.5k | return NDR_ERR_SUCCESS; |
378 | 42.5k | } |
379 | | |
380 | 17.6k | r->type = namebuf[15]; |
381 | | |
382 | 17.6k | namebuf[15] = '\0'; |
383 | 17.6k | trim_string((char *)namebuf, NULL, " "); |
384 | 17.6k | r->name = talloc_strdup(r, (char *)namebuf); |
385 | 17.6k | if (!r->name) return ndr_pull_error(ndr, NDR_ERR_ALLOC, "out of memory"); |
386 | | |
387 | 17.6k | if (namebuf_len > 17) { |
388 | 17.0k | r->scope = talloc_strndup(r, (char *)(namebuf+16), namebuf_len-17); |
389 | 17.0k | if (!r->scope) return ndr_pull_error(ndr, NDR_ERR_ALLOC, "out of memory"); |
390 | 17.0k | } else { |
391 | 605 | r->scope = NULL; |
392 | 605 | } |
393 | | |
394 | 17.6k | talloc_free(namebuf); |
395 | 17.6k | *_r = r; |
396 | 17.6k | return NDR_ERR_SUCCESS; |
397 | 17.6k | } |
398 | | |
399 | | /** |
400 | | push a nbt name, WINS Replication uses another on wire format for nbt name |
401 | | */ |
402 | | _PUBLIC_ enum ndr_err_code ndr_push_wrepl_nbt_name(struct ndr_push *ndr, ndr_flags_type ndr_flags, const struct nbt_name *r) |
403 | 14.7k | { |
404 | 14.7k | uint8_t *namebuf; |
405 | 14.7k | uint32_t namebuf_len; |
406 | 14.7k | uint32_t _name_len; |
407 | 14.7k | uint32_t scope_len = 0; |
408 | | |
409 | 14.7k | if (r == NULL) { |
410 | 0 | return ndr_push_error(ndr, NDR_ERR_INVALID_POINTER, |
411 | 0 | "wrepl_nbt_name NULL pointer"); |
412 | 0 | } |
413 | | |
414 | 14.7k | if (!(ndr_flags & NDR_SCALARS)) { |
415 | 0 | return NDR_ERR_SUCCESS; |
416 | 0 | } |
417 | | |
418 | 14.7k | _name_len = strlen(r->name); |
419 | 14.7k | if (_name_len > 15) { |
420 | 4 | return ndr_push_error(ndr, NDR_ERR_STRING, |
421 | 4 | "wrepl_nbt_name longer as 15 chars: %s", |
422 | 4 | r->name); |
423 | 4 | } |
424 | | |
425 | 14.7k | if (r->scope) { |
426 | 1.58k | scope_len = strlen(r->scope); |
427 | 1.58k | } |
428 | 14.7k | if (scope_len > 238) { |
429 | 0 | return ndr_push_error(ndr, NDR_ERR_STRING, |
430 | 0 | "wrepl_nbt_name scope longer as 238 chars: %s", |
431 | 0 | r->scope); |
432 | 0 | } |
433 | | |
434 | 14.7k | namebuf = (uint8_t *)talloc_asprintf(ndr, "%-15s%c%s", |
435 | 14.7k | r->name, 'X', |
436 | 14.7k | (r->scope?r->scope:"")); |
437 | 14.7k | if (!namebuf) return ndr_push_error(ndr, NDR_ERR_ALLOC, "out of memory"); |
438 | | |
439 | 14.7k | namebuf_len = strlen((char *)namebuf) + 1; |
440 | | |
441 | | /* |
442 | | * we need to set the type here, and use a place-holder in the talloc_asprintf() |
443 | | * as the type can be 0x00, and then the namebuf_len = strlen(namebuf); would give wrong results |
444 | | */ |
445 | 14.7k | namebuf[15] = r->type; |
446 | | |
447 | | /* oh wow, what a nasty bug in windows ... */ |
448 | 14.7k | if (r->type == 0x1b) { |
449 | 929 | namebuf[15] = namebuf[0]; |
450 | 929 | namebuf[0] = 0x1b; |
451 | 929 | } |
452 | | |
453 | 14.7k | NDR_CHECK(ndr_push_align(ndr, 4)); |
454 | 14.7k | NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, namebuf_len)); |
455 | 14.7k | NDR_CHECK(ndr_push_array_uint8(ndr, NDR_SCALARS, namebuf, namebuf_len)); |
456 | | |
457 | 14.7k | if ((namebuf_len % 4) == 0) { |
458 | | /* |
459 | | * [MS-WINSRA] — v20091104 was wrong |
460 | | * regarding section "2.2.10.1 Name Record" |
461 | | * |
462 | | * If the name buffer is already 4 byte aligned |
463 | | * Windows (at least 2003 SP1 and 2008) add 4 extra |
464 | | * bytes. This can happen when the name has a scope. |
465 | | */ |
466 | 436 | NDR_CHECK(ndr_push_zero(ndr, 4)); |
467 | 436 | } |
468 | | |
469 | 14.7k | talloc_free(namebuf); |
470 | 14.7k | return NDR_ERR_SUCCESS; |
471 | 14.7k | } |
472 | | |
473 | | _PUBLIC_ void ndr_print_wrepl_nbt_name(struct ndr_print *ndr, const char *name, const struct nbt_name *r) |
474 | 8.93k | { |
475 | 8.93k | char *s = nbt_name_string(ndr, r); |
476 | 8.93k | ndr_print_string(ndr, name, s); |
477 | 8.93k | talloc_free(s); |
478 | 8.93k | } |
479 | | |
480 | | _PUBLIC_ enum ndr_err_code ndr_push_nbt_qtype(struct ndr_push *ndr, ndr_flags_type ndr_flags, enum nbt_qtype r) |
481 | 127k | { |
482 | | /* For WACK replies, we need to send NBT_QTYPE_NETBIOS on the wire. */ |
483 | 127k | NDR_CHECK(ndr_push_enum_uint16(ndr, NDR_SCALARS, (r == NBT_QTYPE_WACK) ? NBT_QTYPE_NETBIOS : r)); |
484 | 127k | return NDR_ERR_SUCCESS; |
485 | 127k | } |