1 /* $OpenBSD: smbutil.c,v 1.6 2007/10/07 16:41:05 deraadt Exp $ */ 2 3 /* 4 Copyright (C) Andrew Tridgell 1995-1999 5 6 This software may be distributed either under the terms of the 7 BSD-style license that accompanies tcpdump or the GNU GPL version 2 8 or later */ 9 10 #ifdef HAVE_CONFIG_H 11 #include "config.h" 12 #endif 13 14 #ifndef lint 15 static const char rcsid[] = 16 "@(#) $Id: smbutil.c,v 1.6 2007/10/07 16:41:05 deraadt Exp $"; 17 #endif 18 19 #include <sys/param.h> 20 #include <sys/time.h> 21 #include <sys/types.h> 22 #include <sys/socket.h> 23 24 25 #include <netinet/in.h> 26 27 #include <ctype.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <time.h> 32 33 #include "interface.h" 34 #include "smb.h" 35 36 extern const uchar *startbuf; 37 38 /******************************************************************* 39 interpret a 32 bit dos packed date/time to some parameters 40 ********************************************************************/ 41 static void interpret_dos_date(uint32 date,int *year,int *month,int *day,int *hour,int *minute,int *second) 42 { 43 uint32 p0,p1,p2,p3; 44 45 p0=date&0xFF; p1=((date&0xFF00)>>8)&0xFF; 46 p2=((date&0xFF0000)>>16)&0xFF; p3=((date&0xFF000000)>>24)&0xFF; 47 48 *second = 2*(p0 & 0x1F); 49 *minute = ((p0>>5)&0xFF) + ((p1&0x7)<<3); 50 *hour = (p1>>3)&0xFF; 51 *day = (p2&0x1F); 52 *month = ((p2>>5)&0xFF) + ((p3&0x1)<<3) - 1; 53 *year = ((p3>>1)&0xFF) + 80; 54 } 55 56 /******************************************************************* 57 create a unix date from a dos date 58 ********************************************************************/ 59 static time_t make_unix_date(const void *date_ptr) 60 { 61 uint32 dos_date=0; 62 struct tm t; 63 64 dos_date = IVAL(date_ptr,0); 65 66 if (dos_date == 0) return(0); 67 68 interpret_dos_date(dos_date,&t.tm_year,&t.tm_mon, 69 &t.tm_mday,&t.tm_hour,&t.tm_min,&t.tm_sec); 70 t.tm_wday = 1; 71 t.tm_yday = 1; 72 t.tm_isdst = 0; 73 74 return (mktime(&t)); 75 } 76 77 /******************************************************************* 78 create a unix date from a dos date 79 ********************************************************************/ 80 static time_t make_unix_date2(const void *date_ptr) 81 { 82 uint32 x,x2; 83 84 x = IVAL(date_ptr,0); 85 x2 = ((x&0xFFFF)<<16) | ((x&0xFFFF0000)>>16); 86 SIVAL(&x,0,x2); 87 88 return(make_unix_date((void *)&x)); 89 } 90 91 /**************************************************************************** 92 interpret an 8 byte "filetime" structure to a time_t 93 It's originally in "100ns units since jan 1st 1601" 94 ****************************************************************************/ 95 static time_t interpret_long_date(const char *p) 96 { 97 double d; 98 time_t ret; 99 100 /* this gives us seconds since jan 1st 1601 (approx) */ 101 d = (IVAL(p,4)*256.0 + CVAL(p,3)) * (1.0e-7 * (1<<24)); 102 103 /* now adjust by 369 years to make the secs since 1970 */ 104 d -= 369.0*365.25*24*60*60; 105 106 /* and a fudge factor as we got it wrong by a few days */ 107 d += (3*24*60*60 + 6*60*60 + 2); 108 109 if (d<0) 110 return(0); 111 112 ret = (time_t)d; 113 114 return(ret); 115 } 116 117 118 /**************************************************************************** 119 interpret the weird netbios "name". Return the name type, or -1 if 120 we run past the end of the buffer 121 ****************************************************************************/ 122 static int name_interpret(const uchar *in,const uchar *maxbuf,char *out) 123 { 124 char *ob = out; 125 int ret; 126 int len; 127 128 if (in >= maxbuf) 129 return(-1); /* name goes past the end of the buffer */ 130 TCHECK2(*in, 1); 131 len = (*in++) / 2; 132 133 *out=0; 134 135 if (len > 30 || len<1) return(0); 136 137 while (len--) 138 { 139 if (in + 1 >= maxbuf) 140 return(-1); /* name goes past the end of the buffer */ 141 TCHECK2(*in, 2); 142 if (in[0] < 'A' || in[0] > 'P' || in[1] < 'A' || in[1] > 'P') { 143 *out++ = 0; 144 break; 145 } 146 *out = ((in[0]-'A')<<4) + (in[1]-'A'); 147 in += 2; 148 out++; 149 } 150 ret = out[-1]; 151 out--; 152 while (out[-1] == ' ') 153 out--; 154 *out = '\0'; 155 for (; *ob; ob++) 156 if (!isprint(*ob)) 157 *ob = 'X'; 158 159 return(ret); 160 161 trunc: 162 return(-1); 163 } 164 165 /**************************************************************************** 166 find a pointer to a netbios name 167 ****************************************************************************/ 168 static const uchar *name_ptr(const uchar *buf,int ofs,const uchar *maxbuf) 169 { 170 const uchar *p; 171 uchar c; 172 173 p = buf+ofs; 174 if (p >= maxbuf) 175 return(NULL); /* name goes past the end of the buffer */ 176 TCHECK2(*p, 1); 177 178 c = *p; 179 180 /* XXX - this should use the same code that the DNS dissector does */ 181 if ((c & 0xC0) == 0xC0) 182 { 183 uint16 l = RSVAL(buf, ofs) & 0x3FFF; 184 if (l == 0) 185 { 186 /* We have a pointer that points to itself. */ 187 return(NULL); 188 } 189 p = buf + l; 190 if (p >= maxbuf) 191 return(NULL); /* name goes past the end of the buffer */ 192 TCHECK2(*p, 1); 193 return(buf + l); 194 } 195 else 196 return(buf+ofs); 197 198 trunc: 199 return(NULL); /* name goes past the end of the buffer */ 200 } 201 202 /**************************************************************************** 203 extract a netbios name from a buf 204 ****************************************************************************/ 205 static int name_extract(const uchar *buf,int ofs,const uchar *maxbuf,char *name) 206 { 207 const uchar *p = name_ptr(buf,ofs,maxbuf); 208 if (p == NULL) 209 return(-1); /* error (probably name going past end of buffer) */ 210 *name = '\0'; 211 return(name_interpret(p,maxbuf,name)); 212 } 213 214 215 /**************************************************************************** 216 return the total storage length of a mangled name 217 ****************************************************************************/ 218 static int name_len(const unsigned char *s, const unsigned char *maxbuf) 219 { 220 const unsigned char *s0 = s; 221 unsigned char c; 222 223 if (s >= maxbuf) 224 return(-1); /* name goes past the end of the buffer */ 225 TCHECK2(*s, 1); 226 c = *s; 227 if ((c & 0xC0) == 0xC0) 228 return(2); 229 while (*s) 230 { 231 if (s >= maxbuf) 232 return(-1); /* name goes past the end of the buffer */ 233 TCHECK2(*s, 1); 234 s += (*s)+1; 235 } 236 return(PTR_DIFF(s,s0)+1); 237 238 trunc: 239 return(-1); /* name goes past the end of the buffer */ 240 } 241 242 static char *name_type_str(int name_type) 243 { 244 static char *f = NULL; 245 switch (name_type) { 246 case 0: f = "Workstation"; break; 247 case 0x03: f = "Client?"; break; 248 case 0x20: f = "Server"; break; 249 case 0x1d: f = "Master Browser"; break; 250 case 0x1b: f = "Domain Controller"; break; 251 case 0x1e: f = "Browser Server"; break; 252 default: f = "Unknown"; break; 253 } 254 return(f); 255 } 256 257 static void write_bits(unsigned int val,char *fmt) 258 { 259 char *p = fmt; 260 int i=0; 261 262 while ((p=strchr(fmt,'|'))) { 263 int l = PTR_DIFF(p,fmt); 264 if (l && (val & (1<<i))) 265 printf("%.*s ",l,fmt); 266 fmt = p+1; 267 i++; 268 } 269 } 270 271 /* convert a unicode string */ 272 static const char *unistr(const char *s, int *len) 273 { 274 static char buf[1000]; 275 int l=0; 276 static int use_unicode = -1; 277 278 if (use_unicode == -1) { 279 char *p = getenv("USE_UNICODE"); 280 if (p && (atoi(p) == 1)) 281 use_unicode = 1; 282 else 283 use_unicode = 0; 284 } 285 286 /* maybe it isn't unicode - a cheap trick */ 287 if (!use_unicode || (s[0] && s[1])) { 288 *len = strlen(s)+1; 289 return s; 290 } 291 292 *len = 0; 293 294 if (s[0] == 0 && s[1] != 0) { 295 s++; 296 *len = 1; 297 } 298 299 while (l < (sizeof(buf)-1) && s[0] && s[1] == 0) { 300 buf[l] = s[0]; 301 s += 2; l++; 302 *len += 2; 303 } 304 buf[l] = 0; 305 *len += 2; 306 return buf; 307 } 308 309 static const uchar *fdata1(const uchar *buf, const char *fmt, const uchar *maxbuf) 310 { 311 int reverse=0; 312 char *attrib_fmt = "READONLY|HIDDEN|SYSTEM|VOLUME|DIR|ARCHIVE|"; 313 int len; 314 315 while (*fmt && buf<maxbuf) { 316 switch (*fmt) { 317 case 'a': 318 write_bits(CVAL(buf,0),attrib_fmt); 319 buf++; fmt++; 320 break; 321 322 case 'A': 323 write_bits(SVAL(buf,0),attrib_fmt); 324 buf+=2; fmt++; 325 break; 326 327 case '{': 328 { 329 char bitfmt[128]; 330 char *p = strchr(++fmt,'}'); 331 int l = PTR_DIFF(p,fmt); 332 strlcpy(bitfmt,fmt,sizeof(bitfmt)); 333 fmt = p+1; 334 write_bits(CVAL(buf,0),bitfmt); 335 buf++; 336 break; 337 } 338 339 case 'P': 340 { 341 int l = atoi(fmt+1); 342 buf += l; 343 fmt++; 344 while (isdigit(*fmt)) fmt++; 345 break; 346 } 347 case 'r': 348 reverse = !reverse; 349 fmt++; 350 break; 351 case 'D': 352 { 353 unsigned int x = reverse?RIVAL(buf,0):IVAL(buf,0); 354 printf("%d (0x%x)",x, x); 355 buf += 4; 356 fmt++; 357 break; 358 } 359 case 'L': 360 { 361 unsigned int x1 = reverse?RIVAL(buf,0):IVAL(buf,0); 362 unsigned int x2 = reverse?RIVAL(buf,4):IVAL(buf,4); 363 if (x2) { 364 printf("0x%08x:%08x",x2, x1); 365 } else { 366 printf("%d (0x%08x%08x)",x1, x2, x1); 367 } 368 buf += 8; 369 fmt++; 370 break; 371 } 372 case 'd': 373 { 374 unsigned int x = reverse?RSVAL(buf,0):SVAL(buf,0); 375 printf("%d",x); 376 buf += 2; 377 fmt++; 378 break; 379 } 380 case 'W': 381 { 382 unsigned int x = reverse?RIVAL(buf,0):IVAL(buf,0); 383 printf("0x%X",x); 384 buf += 4; 385 fmt++; 386 break; 387 } 388 case 'w': 389 { 390 unsigned int x = reverse?RSVAL(buf,0):SVAL(buf,0); 391 printf("0x%X",x); 392 buf += 2; 393 fmt++; 394 break; 395 } 396 case 'B': 397 { 398 unsigned int x = CVAL(buf,0); 399 printf("0x%X",x); 400 buf += 1; 401 fmt++; 402 break; 403 } 404 case 'b': 405 { 406 unsigned int x = CVAL(buf,0); 407 printf("%d",x); /* EMF - jesus, use B if you want hex */ 408 buf += 1; 409 fmt++; 410 break; 411 } 412 case 'S': 413 { 414 printf("%.*s",(int)PTR_DIFF(maxbuf,buf),unistr(buf, &len)); 415 buf += len; 416 fmt++; 417 break; 418 } 419 case 'Z': 420 { 421 if (*buf != 4 && *buf != 2) 422 printf("Error! ASCIIZ buffer of type %d (safety=%d) ", 423 *buf,(int)PTR_DIFF(maxbuf,buf)); 424 printf("%.*s",(int)PTR_DIFF(maxbuf,buf+1),unistr(buf+1, &len)); 425 buf += len+1; 426 fmt++; 427 break; 428 } 429 case 's': 430 { 431 int l = atoi(fmt+1); 432 printf("%-*.*s",l,l,buf); 433 buf += l; 434 fmt++; while (isdigit(*fmt)) fmt++; 435 break; 436 } 437 case 'h': 438 { 439 int l = atoi(fmt+1); 440 while (l--) printf("%02x",*buf++); 441 fmt++; while (isdigit(*fmt)) fmt++; 442 break; 443 } 444 case 'n': 445 { 446 int t = atoi(fmt+1); 447 char nbuf[255]; 448 int name_type; 449 int len; 450 switch (t) { 451 case 1: 452 name_type = name_extract(startbuf,PTR_DIFF(buf,startbuf),maxbuf, 453 nbuf); 454 if (name_type < 0) 455 goto trunc; 456 len = name_len(buf,maxbuf); 457 if (len < 0) 458 goto trunc; 459 buf += len; 460 printf("%.15s type 0x%02X (%s)", 461 nbuf,name_type,name_type_str(name_type)); 462 break; 463 case 2: 464 name_type = buf[15]; 465 printf("%.15s type 0x%02X (%s)", 466 buf,name_type,name_type_str(name_type)); 467 buf += 16; 468 break; 469 } 470 fmt++; while (isdigit(*fmt)) fmt++; 471 break; 472 } 473 case 'T': 474 { 475 time_t t; 476 int x = IVAL(buf,0); 477 switch (atoi(fmt+1)) { 478 case 1: 479 if (x==0 || x==-1 || x==0xFFFFFFFF) 480 t = 0; 481 else 482 t = make_unix_date(buf); 483 buf+=4; 484 break; 485 case 2: 486 if (x==0 || x==-1 || x==0xFFFFFFFF) 487 t = 0; 488 else 489 t = make_unix_date2(buf); 490 buf+=4; 491 break; 492 case 3: 493 t = interpret_long_date(buf); 494 buf+=8; 495 break; 496 default: 497 error("fdata1: invalid fmt: %s", fmt); 498 } 499 printf("%s",t?asctime(localtime(&t)):"NULL "); 500 fmt++; while (isdigit(*fmt)) fmt++; 501 break; 502 } 503 default: 504 putchar(*fmt); 505 fmt++; 506 break; 507 } 508 } 509 510 if (buf>=maxbuf && *fmt) 511 printf("END OF BUFFER "); 512 513 return(buf); 514 515 trunc: 516 printf("WARNING: Short packet. Try increasing the snap length "); 517 return(NULL); 518 } 519 520 const uchar *fdata(const uchar *buf, const char *fmt, const uchar *maxbuf) 521 { 522 static int depth=0; 523 char s[128]; 524 char *p; 525 526 while (*fmt) { 527 switch (*fmt) { 528 case '*': 529 fmt++; 530 while (buf < maxbuf) { 531 const uchar *buf2; 532 depth++; 533 buf2 = fdata(buf,fmt,maxbuf); 534 depth--; 535 if (buf2 == buf) return(buf); 536 buf = buf2; 537 } 538 break; 539 540 case '|': 541 fmt++; 542 if (buf>=maxbuf) return(buf); 543 break; 544 545 case '%': 546 fmt++; 547 buf=maxbuf; 548 break; 549 550 case '#': 551 fmt++; 552 return(buf); 553 break; 554 555 case '[': 556 fmt++; 557 if (buf>=maxbuf) return(buf); 558 memset(s, 0, sizeof(s)); 559 p = strchr(fmt,']'); 560 strncpy(s,fmt,p-fmt); /* XXX? */ 561 fmt = p+1; 562 buf = fdata1(buf,s,maxbuf); 563 if (buf == NULL) 564 return(NULL); 565 break; 566 567 default: 568 putchar(*fmt); fmt++; 569 fflush(stdout); 570 break; 571 } 572 } 573 if (!depth && buf<maxbuf) { 574 int len = PTR_DIFF(maxbuf,buf); 575 printf("(%d data bytes)",len); 576 /* EMF - use -X flag if you want this verbosity 577 * print_data(buf,len); 578 */ 579 return(buf+len); 580 } 581 return(buf); 582 } 583 584 typedef struct 585 { 586 char *name; 587 int code; 588 char *message; 589 } err_code_struct; 590 591 /* Dos Error Messages */ 592 static err_code_struct dos_msgs[] = { 593 {"ERRbadfunc",1,"Invalid function."}, 594 {"ERRbadfile",2,"File not found."}, 595 {"ERRbadpath",3,"Directory invalid."}, 596 {"ERRnofids",4,"No file descriptors available"}, 597 {"ERRnoaccess",5,"Access denied."}, 598 {"ERRbadfid",6,"Invalid file handle."}, 599 {"ERRbadmcb",7,"Memory control blocks destroyed."}, 600 {"ERRnomem",8,"Insufficient server memory to perform the requested function."}, 601 {"ERRbadmem",9,"Invalid memory block address."}, 602 {"ERRbadenv",10,"Invalid environment."}, 603 {"ERRbadformat",11,"Invalid format."}, 604 {"ERRbadaccess",12,"Invalid open mode."}, 605 {"ERRbaddata",13,"Invalid data."}, 606 {"ERR",14,"reserved."}, 607 {"ERRbaddrive",15,"Invalid drive specified."}, 608 {"ERRremcd",16,"A Delete Directory request attempted to remove the server's current directory."}, 609 {"ERRdiffdevice",17,"Not same device."}, 610 {"ERRnofiles",18,"A File Search command can find no more files matching the specified criteria."}, 611 {"ERRbadshare",32,"The sharing mode specified for an Open conflicts with existing FIDs on the file."}, 612 {"ERRlock",33,"A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."}, 613 {"ERRfilexists",80,"The file named in a Create Directory, Make New File or Link request already exists."}, 614 {"ERRbadpipe",230,"Pipe invalid."}, 615 {"ERRpipebusy",231,"All instances of the requested pipe are busy."}, 616 {"ERRpipeclosing",232,"Pipe close in progress."}, 617 {"ERRnotconnected",233,"No process on other end of pipe."}, 618 {"ERRmoredata",234,"There is more data to be returned."}, 619 {NULL,-1,NULL}}; 620 621 /* Server Error Messages */ 622 err_code_struct server_msgs[] = { 623 {"ERRerror",1,"Non-specific error code."}, 624 {"ERRbadpw",2,"Bad password - name/password pair in a Tree Connect or Session Setup are invalid."}, 625 {"ERRbadtype",3,"reserved."}, 626 {"ERRaccess",4,"The requester does not have the necessary access rights within the specified context for the requested function. The context is defined by the TID or the UID."}, 627 {"ERRinvnid",5,"The tree ID (TID) specified in a command was invalid."}, 628 {"ERRinvnetname",6,"Invalid network name in tree connect."}, 629 {"ERRinvdevice",7,"Invalid device - printer request made to non-printer connection or non-printer request made to printer connection."}, 630 {"ERRqfull",49,"Print queue full (files) -- returned by open print file."}, 631 {"ERRqtoobig",50,"Print queue full -- no space."}, 632 {"ERRqeof",51,"EOF on print queue dump."}, 633 {"ERRinvpfid",52,"Invalid print file FID."}, 634 {"ERRsmbcmd",64,"The server did not recognize the command received."}, 635 {"ERRsrverror",65,"The server encountered an internal error, e.g., system file unavailable."}, 636 {"ERRfilespecs",67,"The file handle (FID) and pathname parameters contained an invalid combination of values."}, 637 {"ERRreserved",68,"reserved."}, 638 {"ERRbadpermits",69,"The access permissions specified for a file or directory are not a valid combination. The server cannot set the requested attribute."}, 639 {"ERRreserved",70,"reserved."}, 640 {"ERRsetattrmode",71,"The attribute mode in the Set File Attribute request is invalid."}, 641 {"ERRpaused",81,"Server is paused."}, 642 {"ERRmsgoff",82,"Not receiving messages."}, 643 {"ERRnoroom",83,"No room to buffer message."}, 644 {"ERRrmuns",87,"Too many remote user names."}, 645 {"ERRtimeout",88,"Operation timed out."}, 646 {"ERRnoresource",89,"No resources currently available for request."}, 647 {"ERRtoomanyuids",90,"Too many UIDs active on this session."}, 648 {"ERRbaduid",91,"The UID is not known as a valid ID on this session."}, 649 {"ERRusempx",250,"Temp unable to support Raw, use MPX mode."}, 650 {"ERRusestd",251,"Temp unable to support Raw, use standard read/write."}, 651 {"ERRcontmpx",252,"Continue in MPX mode."}, 652 {"ERRreserved",253,"reserved."}, 653 {"ERRreserved",254,"reserved."}, 654 {"ERRnosupport",0xFFFF,"Function not supported."}, 655 {NULL,-1,NULL}}; 656 657 /* Hard Error Messages */ 658 err_code_struct hard_msgs[] = { 659 {"ERRnowrite",19,"Attempt to write on write-protected diskette."}, 660 {"ERRbadunit",20,"Unknown unit."}, 661 {"ERRnotready",21,"Drive not ready."}, 662 {"ERRbadcmd",22,"Unknown command."}, 663 {"ERRdata",23,"Data error (CRC)."}, 664 {"ERRbadreq",24,"Bad request structure length."}, 665 {"ERRseek",25 ,"Seek error."}, 666 {"ERRbadmedia",26,"Unknown media type."}, 667 {"ERRbadsector",27,"Sector not found."}, 668 {"ERRnopaper",28,"Printer out of paper."}, 669 {"ERRwrite",29,"Write fault."}, 670 {"ERRread",30,"Read fault."}, 671 {"ERRgeneral",31,"General failure."}, 672 {"ERRbadshare",32,"A open conflicts with an existing open."}, 673 {"ERRlock",33,"A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."}, 674 {"ERRwrongdisk",34,"The wrong disk was found in a drive."}, 675 {"ERRFCBUnavail",35,"No FCBs are available to process request."}, 676 {"ERRsharebufexc",36,"A sharing buffer has been exceeded."}, 677 {NULL,-1,NULL}}; 678 679 680 static struct 681 { 682 int code; 683 char *class; 684 err_code_struct *err_msgs; 685 } err_classes[] = { 686 {0,"SUCCESS",NULL}, 687 {0x01,"ERRDOS",dos_msgs}, 688 {0x02,"ERRSRV",server_msgs}, 689 {0x03,"ERRHRD",hard_msgs}, 690 {0x04,"ERRXOS",NULL}, 691 {0xE1,"ERRRMX1",NULL}, 692 {0xE2,"ERRRMX2",NULL}, 693 {0xE3,"ERRRMX3",NULL}, 694 {0xFF,"ERRCMD",NULL}, 695 {-1,NULL,NULL}}; 696 697 698 /**************************************************************************** 699 return a SMB error string from a SMB buffer 700 ****************************************************************************/ 701 char *smb_errstr(int class,int num) 702 { 703 static char ret[128]; 704 int i,j; 705 706 ret[0]=0; 707 708 for (i=0;err_classes[i].class;i++) 709 if (err_classes[i].code == class) 710 { 711 if (err_classes[i].err_msgs) 712 { 713 err_code_struct *err = err_classes[i].err_msgs; 714 for (j=0;err[j].name;j++) 715 if (num == err[j].code) 716 { 717 snprintf(ret, sizeof(ret), "%s - %s (%s)", 718 err_classes[i].class, 719 err[j].name,err[j].message); 720 return ret; 721 } 722 } 723 724 snprintf(ret,sizeof(ret),"%s - %d",err_classes[i].class,num); 725 return ret; 726 } 727 728 snprintf(ret,sizeof(ret),"ERROR: Unknown error (%d,%d)",class,num); 729 return(ret); 730 } 731