1 #define UNICODE 2 #define Unknown win_Unknown 3 #include <windows.h> 4 #include <winbase.h> 5 #undef Unknown 6 #undef Sleep 7 #include "dat.h" 8 #include "fns.h" 9 #include "error.h" 10 #include <lm.h> 11 12 /* TODO: try using / in place of \ in path names */ 13 14 enum 15 { 16 MAX_SID = sizeof(SID) + SID_MAX_SUB_AUTHORITIES*sizeof(DWORD), 17 ACL_ROCK = sizeof(ACL) + 20*(sizeof(ACCESS_ALLOWED_ACE)+MAX_SID), 18 SD_ROCK = SECURITY_DESCRIPTOR_MIN_LENGTH + MAX_SID + ACL_ROCK, 19 MAXCOMP = 128, 20 }; 21 22 typedef struct User User; 23 typedef struct Gmem Gmem; 24 typedef struct Stat Stat; 25 typedef struct Fsinfo Fsinfo; 26 typedef WIN32_FIND_DATA Fsdir; 27 28 #ifndef INVALID_SET_FILE_POINTER 29 #define INVALID_SET_FILE_POINTER ((DWORD)-1) 30 #endif 31 32 struct Fsinfo 33 { 34 int uid; 35 int gid; 36 int mode; 37 int fd; 38 vlong offset; 39 QLock oq; 40 char* spec; 41 Rune* srv; 42 Cname* name; /* Windows' idea of the file name */ 43 ushort usesec; 44 ushort checksec; 45 Fsdir* de; /* non-nil for saved entry from last dirread at offset */ 46 }; 47 #define FS(c) ((Fsinfo*)(c)->aux) 48 49 /* 50 * info about a user or group 51 * there are two ways to specify a user: 52 * by sid, a unique identifier 53 * by user and domain names 54 * this structure is used to convert between the two, 55 * as well as figure out which groups a users belongs to. 56 * the user information never gets thrown away, 57 * but the group information gets refreshed with each setid. 58 */ 59 struct User 60 { 61 QLock lk; /* locks the gotgroup and group fields */ 62 SID *sid; 63 Rune *name; 64 Rune *dom; 65 int type; /* the type of sid, ie SidTypeUser, SidTypeAlias, ... */ 66 int gotgroup; /* tried to add group */ 67 Gmem *group; /* global and local groups to which this user or group belongs. */ 68 User *next; 69 }; 70 71 struct Gmem 72 { 73 User *user; 74 Gmem *next; 75 }; 76 77 /* 78 * intermediate stat information 79 */ 80 struct Stat 81 { 82 User *owner; 83 User *group; 84 ulong mode; 85 }; 86 87 /* 88 * some "well-known" sids 89 */ 90 static SID *creatorowner; 91 static SID *creatorgroup; 92 static SID *everyone; 93 static SID *ntignore; 94 static SID *ntroot; /* user who is supposed to run emu as a server */ 95 96 /* 97 * all users we ever see end up in this table 98 * users are never deleted, but we should update 99 * group information for users sometime 100 */ 101 static struct 102 { 103 QLock lk; 104 User *u; 105 }users; 106 107 /* 108 * conversion from inferno permission modes to nt access masks 109 * is this good enough? this is what nt sets, except for NOMODE 110 */ 111 #define NOMODE (READ_CONTROL|FILE_READ_EA|FILE_READ_ATTRIBUTES) 112 #define RMODE (READ_CONTROL|SYNCHRONIZE\ 113 |FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES) 114 #define XMODE (READ_CONTROL|SYNCHRONIZE\ 115 |FILE_EXECUTE|FILE_READ_ATTRIBUTES) 116 #define WMODE (DELETE|READ_CONTROL|SYNCHRONIZE|WRITE_DAC|WRITE_OWNER\ 117 |FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA\ 118 |FILE_DELETE_CHILD|FILE_WRITE_ATTRIBUTES) 119 120 static int 121 modetomask[] = 122 { 123 NOMODE, 124 XMODE, 125 WMODE, 126 WMODE|XMODE, 127 RMODE, 128 RMODE|XMODE, 129 RMODE|WMODE, 130 RMODE|WMODE|XMODE, 131 }; 132 133 extern DWORD PlatformId; 134 char rootdir[MAXROOT] = "\\inferno"; 135 Rune rootname[] = L"inferno-server"; 136 static Qid rootqid; 137 static User *fsnone; 138 static User *fsuser; 139 static Rune *ntsrv; 140 static int usesec; 141 static int checksec; 142 static int isserver; 143 static int file_share_delete; 144 static uchar isntfrog[256]; 145 146 static void fsremove(Chan*); 147 148 wchar_t *widen(char *s); 149 char *narrowen(wchar_t *ws); 150 int widebytes(wchar_t *ws); 151 152 static char Etoolong[] = "file name too long"; 153 154 /* 155 * these lan manager functions are not supplied 156 * on windows95, so we have to load the dll by hand 157 */ 158 static struct { 159 NET_API_STATUS (NET_API_FUNCTION *UserGetLocalGroups)( 160 LPWSTR servername, 161 LPWSTR username, 162 DWORD level, 163 DWORD flags, 164 LPBYTE *bufptr, 165 DWORD prefmaxlen, 166 LPDWORD entriesread, 167 LPDWORD totalentries); 168 NET_API_STATUS (NET_API_FUNCTION *UserGetGroups)( 169 LPWSTR servername, 170 LPWSTR username, 171 DWORD level, 172 LPBYTE *bufptr, 173 DWORD prefmaxlen, 174 LPDWORD entriesread, 175 LPDWORD totalentries); 176 NET_API_STATUS (NET_API_FUNCTION *GetAnyDCName)( 177 LPCWSTR ServerName, 178 LPCWSTR DomainName, 179 LPBYTE *Buffer); 180 NET_API_STATUS (NET_API_FUNCTION *ApiBufferFree)(LPVOID Buffer); 181 } net; 182 183 extern int nth2fd(HANDLE); 184 extern HANDLE ntfd2h(int); 185 static int cnisroot(Cname*); 186 static int fsisroot(Chan*); 187 static int okelem(char*, int); 188 static int fsexist(char*, Qid*); 189 static char* fspath(Cname*, char*, char*, char*); 190 static Cname* fswalkpath(Cname*, char*, int); 191 static char* fslastelem(Cname*); 192 static long fsdirread(Chan*, uchar*, int, vlong); 193 static ulong fsqidpath(char*); 194 static int fsomode(int); 195 static int fsdirset(char*, int, WIN32_FIND_DATA*, char*, Chan*, int isdir); 196 static int fsdirsize(WIN32_FIND_DATA*, char*, Chan*); 197 static void fssettime(char*, long, long); 198 static long unixtime(FILETIME); 199 static FILETIME wintime(ulong); 200 static void secinit(void); 201 static int secstat(Dir*, char*, Rune*); 202 static int secsize(char*, Rune*); 203 static void seccheck(char*, ulong, Rune*); 204 static int sechasperm(char*, ulong, Rune*); 205 static SECURITY_DESCRIPTOR* secsd(char*, char[SD_ROCK]); 206 static int secsdhasperm(SECURITY_DESCRIPTOR*, ulong, Rune*); 207 static int secsdstat(SECURITY_DESCRIPTOR*, Stat*, Rune*); 208 static SECURITY_DESCRIPTOR* secmksd(char[SD_ROCK], Stat*, ACL*, int); 209 static SID *dupsid(SID*); 210 static int ismembersid(Rune*, User*, SID*); 211 static int ismember(User*, User*); 212 static User *sidtouser(Rune*, SID*); 213 static User *domnametouser(Rune*, Rune*, Rune*); 214 static User *nametouser(Rune*, Rune*); 215 static User *unametouser(Rune*, char*); 216 static void addgroups(User*, int); 217 static User *mkuser(SID*, int, Rune*, Rune*); 218 static Rune *domsrv(Rune *, Rune[MAX_PATH]); 219 static Rune *filesrv(char*); 220 static int fsacls(char*); 221 static User *secuser(void); 222 223 int runeslen(Rune*); 224 Rune* runesdup(Rune*); 225 Rune* utftorunes(Rune*, char*, int); 226 char* runestoutf(char*, Rune*, int); 227 int runescmp(Rune*, Rune*); 228 229 230 int 231 winfilematch(char *path, WIN32_FIND_DATA *data) 232 { 233 char *p; 234 wchar_t *wpath; 235 int r; 236 237 p = path+strlen(path); 238 while(p > path && p[-1] != '\\') 239 --p; 240 wpath = widen(p); 241 r = (data->cFileName[0] == '.' && runeslen(data->cFileName) == 1) 242 || runescmp(data->cFileName, wpath) == 0; 243 free(wpath); 244 return r; 245 } 246 247 int 248 winfileclash(char *path) 249 { 250 HANDLE h; 251 WIN32_FIND_DATA data; 252 wchar_t *wpath; 253 254 wpath = widen(path); 255 h = FindFirstFile(wpath, &data); 256 free(wpath); 257 if (h != INVALID_HANDLE_VALUE) { 258 FindClose(h); 259 return !winfilematch(path, &data); 260 } 261 return 0; 262 } 263 264 265 /* 266 * this gets called to set up the environment when we switch users 267 */ 268 void 269 setid(char *name, int owner) 270 { 271 User *u; 272 273 if(owner && !iseve()) 274 return; 275 276 kstrdup(&up->env->user, name); 277 278 if(!usesec) 279 return; 280 281 u = unametouser(ntsrv, up->env->user); 282 if(u == nil) 283 u = fsnone; 284 else { 285 qlock(&u->lk); 286 addgroups(u, 1); 287 qunlock(&u->lk); 288 } 289 if(u == nil) 290 panic("setid: user nil\n"); 291 292 up->env->ui = u; 293 } 294 295 static void 296 fsfree(Chan *c) 297 { 298 cnameclose(FS(c)->name); 299 if(FS(c)->de != nil) 300 free(FS(c)->de); 301 free(FS(c)); 302 } 303 304 void 305 fsinit(void) 306 { 307 int n, isvol; 308 ulong attr; 309 char *p, tmp[MAXROOT]; 310 wchar_t *wp, *wpath, *last; 311 wchar_t wrootdir[MAXROOT]; 312 313 isntfrog['/'] = 1; 314 isntfrog['\\'] = 1; 315 isntfrog[':'] = 1; 316 isntfrog['*'] = 1; 317 isntfrog['?'] = 1; 318 isntfrog['"'] = 1; 319 isntfrog['<'] = 1; 320 isntfrog['>'] = 1; 321 322 /* 323 * vet the root 324 */ 325 strcpy(tmp, rootdir); 326 for(p = tmp; *p; p++) 327 if(*p == '/') 328 *p = '\\'; 329 if(tmp[0] != 0 && tmp[1] == ':') { 330 if(tmp[2] == 0) { 331 tmp[2] = '\\'; 332 tmp[3] = 0; 333 } 334 else if(tmp[2] != '\\') { 335 /* don't allow c:foo - only c:\foo */ 336 panic("illegal root pathX"); 337 } 338 } 339 wrootdir[0] = '\0'; 340 wpath = widen(tmp); 341 for(wp = wpath; *wp; wp++) { 342 if(*wp < 32 || (*wp < 256 && isntfrog[*wp] && *wp != '\\' && *wp != ':')) 343 panic("illegal root path"); 344 } 345 n = GetFullPathName(wpath, MAXROOT, wrootdir, &last); 346 free(wpath); 347 runestoutf(rootdir, wrootdir, MAXROOT); 348 if(n >= MAXROOT || n == 0) 349 panic("illegal root path"); 350 351 /* get rid of trailing \ */ 352 while(rootdir[n-1] == '\\') { 353 if(n <= 2) { 354 panic("illegal root path"); 355 } 356 rootdir[--n] = '\0'; 357 } 358 359 isvol = 0; 360 if(rootdir[1] == ':' && rootdir[2] == '\0') 361 isvol = 1; 362 else if(rootdir[0] == '\\' && rootdir[1] == '\\') { 363 p = strchr(&rootdir[2], '\\'); 364 if(p == nil) 365 panic("inferno root can't be a server"); 366 isvol = strchr(p+1, '\\') == nil; 367 } 368 369 if(strchr(rootdir, '\\') == nil) 370 strcat(rootdir, "\\."); 371 attr = GetFileAttributes(wrootdir); 372 if(attr == 0xFFFFFFFF) 373 panic("root path '%s' does not exist", narrowen(wrootdir)); 374 rootqid.path = fsqidpath(rootdir); 375 if(attr & FILE_ATTRIBUTE_DIRECTORY) 376 rootqid.type |= QTDIR; 377 rootdir[n] = '\0'; 378 379 rootqid.vers = time(0); 380 381 /* 382 * set up for nt file security checking 383 */ 384 ntsrv = filesrv(rootdir); 385 usesec = PlatformId == VER_PLATFORM_WIN32_NT; /* true for NT and 2000 */ 386 if(usesec){ 387 file_share_delete = FILE_SHARE_DELETE; /* sensible handling of shared files by delete and rename */ 388 secinit(); 389 if(!fsacls(rootdir)) 390 usesec = 0; 391 } 392 checksec = usesec && isserver; 393 } 394 395 Chan* 396 fsattach(char *spec) 397 { 398 Chan *c; 399 static int devno; 400 static Lock l; 401 char *drive = (char *)spec; 402 403 if (!emptystr(drive) && (drive[1] != ':' || drive[2] != '\0')) 404 error(Ebadspec); 405 406 c = devattach('U', spec); 407 lock(&l); 408 c->dev = devno++; 409 unlock(&l); 410 c->qid = rootqid; 411 c->aux = smalloc(sizeof(Fsinfo)); 412 FS(c)->srv = ntsrv; 413 if(!emptystr(spec)) { 414 char *s = smalloc(strlen(spec)+1); 415 strcpy(s, spec); 416 FS(c)->spec = s; 417 FS(c)->srv = filesrv(spec); 418 FS(c)->usesec = fsacls(spec); 419 FS(c)->checksec = FS(c)->usesec && isserver; 420 c->qid.path = fsqidpath(spec); 421 c->qid.type = QTDIR; 422 c->qid.vers = 0; 423 }else{ 424 FS(c)->usesec = usesec; 425 FS(c)->checksec = checksec; 426 } 427 FS(c)->name = newcname("/"); 428 return c; 429 } 430 431 Walkqid* 432 fswalk(Chan *c, Chan *nc, char **name, int nname) 433 { 434 int j, alloc; 435 Walkqid *wq; 436 char path[MAX_PATH], *p; 437 Cname *ph; 438 Cname *current, *next; 439 440 if(nname > 0) 441 isdir(c); 442 443 alloc = 0; 444 current = nil; 445 wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); 446 if(waserror()){ 447 if(alloc && wq->clone != nil) 448 cclose(wq->clone); 449 cnameclose(current); 450 free(wq); 451 return nil; 452 } 453 if(nc == nil){ 454 nc = devclone(c); 455 nc->type = 0; 456 alloc = 1; 457 } 458 wq->clone = nc; 459 current = FS(c)->name; 460 if(current != nil) 461 incref(¤t->r); 462 for(j = 0; j < nname; j++){ 463 if(!(nc->qid.type&QTDIR)){ 464 if(j==0) 465 error(Enotdir); 466 break; 467 } 468 if(!okelem(name[j], 0)){ 469 if(j == 0) 470 error(Efilename); 471 break; 472 } 473 p = fspath(current, name[j], path, FS(c)->spec); 474 if(FS(c)->checksec) { 475 *p = '\0'; 476 if(!sechasperm(path, XMODE, FS(c)->srv)){ 477 if(j == 0) 478 error(Eperm); 479 break; 480 } 481 *p = '\\'; 482 } 483 484 if(strcmp(name[j], "..") == 0) { 485 if(fsisroot(c)) 486 nc->qid = rootqid; 487 else{ 488 ph = fswalkpath(current, "..", 1); 489 if(cnisroot(ph)){ 490 nc->qid = rootqid; 491 current = ph; 492 if(current != nil) 493 incref(¤t->r); 494 } 495 else { 496 fspath(ph, 0, path, FS(c)->spec); 497 if(!fsexist(path, &nc->qid)){ 498 cnameclose(ph); 499 if(j == 0) 500 error(Enonexist); 501 break; 502 } 503 } 504 next = fswalkpath(current, name[j], 1); 505 cnameclose(current); 506 current = next; 507 cnameclose(ph); 508 } 509 } 510 else{ 511 if(!fsexist(path, &nc->qid)){ 512 if(j == 0) 513 error(Enonexist); 514 break; 515 } 516 next = fswalkpath(current, name[j], 1); 517 cnameclose(current); 518 current = next; 519 } 520 wq->qid[wq->nqid++] = nc->qid; 521 } 522 poperror(); 523 if(wq->nqid < nname){ 524 cnameclose(current); 525 if(alloc) 526 cclose(wq->clone); 527 wq->clone = nil; 528 }else if(wq->clone){ 529 nc->aux = smalloc(sizeof(Fsinfo)); 530 nc->type = c->type; 531 FS(nc)->spec = FS(c)->spec; 532 FS(nc)->srv = FS(c)->srv; 533 FS(nc)->name = current; 534 FS(nc)->usesec = FS(c)->usesec; 535 FS(nc)->checksec = FS(c)->checksec; 536 } 537 return wq; 538 } 539 540 Chan* 541 fsopen(Chan *c, int mode) 542 { 543 HANDLE h; 544 int m, isdir, aflag, cflag; 545 char path[MAX_PATH]; 546 wchar_t *wpath; 547 548 isdir = c->qid.type & QTDIR; 549 if(isdir && mode != OREAD) 550 error(Eperm); 551 fspath(FS(c)->name, 0, path, FS(c)->spec); 552 553 if(FS(c)->checksec) { 554 switch(mode & (OTRUNC|3)) { 555 case OREAD: 556 seccheck(path, RMODE, FS(c)->srv); 557 break; 558 case OWRITE: 559 case OWRITE|OTRUNC: 560 seccheck(path, WMODE, FS(c)->srv); 561 break; 562 case ORDWR: 563 case ORDWR|OTRUNC: 564 case OREAD|OTRUNC: 565 seccheck(path, RMODE|WMODE, FS(c)->srv); 566 break; 567 case OEXEC: 568 seccheck(path, XMODE, FS(c)->srv); 569 break; 570 default: 571 error(Ebadarg); 572 } 573 } 574 575 c->mode = openmode(mode); 576 if(isdir) 577 FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE); 578 else { 579 m = fsomode(mode & 3); 580 cflag = OPEN_EXISTING; 581 if(mode & OTRUNC) 582 cflag = TRUNCATE_EXISTING; 583 aflag = FILE_FLAG_RANDOM_ACCESS; 584 if(mode & ORCLOSE) 585 aflag |= FILE_FLAG_DELETE_ON_CLOSE; 586 if (winfileclash(path)) 587 error(Eexist); 588 wpath = widen(path); 589 h = CreateFile(wpath, m, FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, 0, cflag, aflag, 0); 590 free(wpath); 591 if(h == INVALID_HANDLE_VALUE) 592 oserror(); 593 FS(c)->fd = nth2fd(h); 594 } 595 596 c->offset = 0; 597 FS(c)->offset = 0; 598 c->flag |= COPEN; 599 return c; 600 } 601 602 void 603 fscreate(Chan *c, char *name, int mode, ulong perm) 604 { 605 Stat st; 606 HANDLE h; 607 int m, aflag; 608 SECURITY_ATTRIBUTES sa; 609 SECURITY_DESCRIPTOR *sd; 610 BY_HANDLE_FILE_INFORMATION hi; 611 char *p, path[MAX_PATH], sdrock[SD_ROCK]; 612 wchar_t *wpath; 613 ACL *acl; 614 615 if(!okelem(name, 1)) 616 error(Efilename); 617 618 m = fsomode(mode & 3); 619 p = fspath(FS(c)->name, name, path, FS(c)->spec); 620 acl = (ACL*)smalloc(ACL_ROCK); 621 sd = nil; 622 if(FS(c)->usesec) { 623 *p = '\0'; 624 sd = secsd(path, sdrock); 625 *p = '\\'; 626 if(sd == nil){ 627 free(acl); 628 oserror(); 629 } 630 if(FS(c)->checksec && !secsdhasperm(sd, WMODE, FS(c)->srv) 631 || !secsdstat(sd, &st, FS(c)->srv)){ 632 if(sd != (void*)sdrock) 633 free(sd); 634 free(acl); 635 error(Eperm); 636 } 637 if(sd != (void*)sdrock) 638 free(sd); 639 if(perm & DMDIR) 640 st.mode = (perm & ~0777) | (st.mode & perm & 0777); 641 else 642 st.mode = (perm & ~0666) | (st.mode & perm & 0666); 643 st.owner = up->env->ui; 644 if(!isserver) 645 st.owner = fsuser; 646 sd = secmksd(sdrock, &st, acl, perm & DMDIR); 647 if(sd == nil){ 648 free(acl); 649 oserror(); 650 } 651 } 652 sa.nLength = sizeof(sa); 653 sa.lpSecurityDescriptor = sd; 654 sa.bInheritHandle = 0; 655 656 if(perm & DMDIR) { 657 if(mode != OREAD) { 658 free(acl); 659 error(Eisdir); 660 } 661 wpath = widen(path); 662 if(!CreateDirectory(wpath, &sa) || !fsexist(path, &c->qid)) { 663 free(wpath); 664 free(acl); 665 oserror(); 666 } 667 free(wpath); 668 FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE); 669 } 670 else { 671 aflag = 0; 672 if(mode & ORCLOSE) 673 aflag = FILE_FLAG_DELETE_ON_CLOSE; 674 if (winfileclash(path)) 675 error(Eexist); 676 wpath = widen(path); 677 h = CreateFile(wpath, m, FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, &sa, CREATE_ALWAYS, aflag, 0); 678 free(wpath); 679 if(h == INVALID_HANDLE_VALUE) { 680 free(acl); 681 oserror(); 682 } 683 FS(c)->fd = nth2fd(h); 684 c->qid.path = fsqidpath(path); 685 c->qid.type = 0; 686 c->qid.vers = 0; 687 if(GetFileInformationByHandle(h, &hi)) 688 c->qid.vers = unixtime(hi.ftLastWriteTime); 689 } 690 691 c->mode = openmode(mode); 692 c->offset = 0; 693 FS(c)->offset = 0; 694 c->flag |= COPEN; 695 FS(c)->name = fswalkpath(FS(c)->name, name, 0); 696 free(acl); 697 } 698 699 void 700 fsclose(Chan *c) 701 { 702 HANDLE h; 703 704 if(c->flag & COPEN){ 705 h = ntfd2h(FS(c)->fd); 706 if(h != INVALID_HANDLE_VALUE){ 707 if(c->qid.type & QTDIR) 708 FindClose(h); 709 else 710 CloseHandle(h); 711 } 712 } 713 if(c->flag & CRCLOSE){ 714 if(!waserror()){ 715 fsremove(c); 716 poperror(); 717 } 718 return; 719 } 720 fsfree(c); 721 } 722 723 /* 724 * 64-bit seeks, using SetFilePointer because SetFilePointerEx 725 * is not supported by NT 726 */ 727 static void 728 fslseek(HANDLE h, vlong offset) 729 { 730 LONG hi; 731 732 if(1 || offset <= 0x7ffffff){ /* TO DO: remove 1 || */ 733 if(SetFilePointer(h, (LONG)offset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) 734 oserror(); 735 }else{ 736 hi = offset>>32; 737 if(SetFilePointer(h, (LONG)offset, &hi, FILE_BEGIN) == INVALID_SET_FILE_POINTER && 738 GetLastError() != NO_ERROR) 739 oserror(); 740 } 741 } 742 743 long 744 fsread(Chan *c, void *va, long n, vlong offset) 745 { 746 DWORD n2; 747 HANDLE h; 748 749 qlock(&FS(c)->oq); 750 if(waserror()){ 751 qunlock(&FS(c)->oq); 752 nexterror(); 753 } 754 if(c->qid.type & QTDIR) { 755 n2 = fsdirread(c, va, n, offset); 756 } 757 else { 758 h = ntfd2h(FS(c)->fd); 759 if(FS(c)->offset != offset){ 760 fslseek(h, offset); 761 FS(c)->offset = offset; 762 } 763 if(!ReadFile(h, va, n, &n2, NULL)) 764 oserror(); 765 FS(c)->offset += n2; 766 } 767 qunlock(&FS(c)->oq); 768 poperror(); 769 return n2; 770 } 771 772 long 773 fswrite(Chan *c, void *va, long n, vlong offset) 774 { 775 DWORD n2; 776 HANDLE h; 777 778 qlock(&FS(c)->oq); 779 if(waserror()){ 780 qunlock(&FS(c)->oq); 781 nexterror(); 782 } 783 h = ntfd2h(FS(c)->fd); 784 if(FS(c)->offset != offset){ 785 fslseek(h, offset); 786 FS(c)->offset = offset; 787 } 788 if(!WriteFile(h, va, n, &n2, NULL)) 789 oserror(); 790 FS(c)->offset += n2; 791 qunlock(&FS(c)->oq); 792 poperror(); 793 return n2; 794 } 795 796 int 797 fsstat(Chan *c, uchar *buf, int n) 798 { 799 WIN32_FIND_DATA data; 800 char path[MAX_PATH]; 801 wchar_t *wpath; 802 803 /* 804 * have to fake up a data for volumes like 805 * c: and \\server\share since you can't FindFirstFile them 806 */ 807 if(fsisroot(c)){ 808 strcpy(path, rootdir); 809 if(strchr(path, '\\') == nil) 810 strcat(path, "\\."); 811 wpath = widen(path); 812 data.dwFileAttributes = GetFileAttributes(wpath); 813 free(wpath); 814 if(data.dwFileAttributes == 0xffffffff) 815 oserror(); 816 data.ftCreationTime = 817 data.ftLastAccessTime = 818 data.ftLastWriteTime = wintime(time(0)); 819 data.nFileSizeHigh = 0; 820 data.nFileSizeLow = 0; 821 utftorunes(data.cFileName, ".", MAX_PATH); 822 } else { 823 HANDLE h = INVALID_HANDLE_VALUE; 824 825 fspath(FS(c)->name, 0, path, FS(c)->spec); 826 if (c->flag & COPEN) 827 h = ntfd2h(FS(c)->fd); 828 829 if (h != INVALID_HANDLE_VALUE) { 830 BY_HANDLE_FILE_INFORMATION fi; 831 if (c->mode & OWRITE) 832 FlushFileBuffers(h); 833 if (!GetFileInformationByHandle(h, &fi)) 834 oserror(); 835 data.dwFileAttributes = fi.dwFileAttributes; 836 data.ftCreationTime = fi.ftCreationTime; 837 data.ftLastAccessTime = fi.ftLastAccessTime; 838 data.ftLastWriteTime = fi.ftLastWriteTime;; 839 data.nFileSizeHigh = fi.nFileSizeHigh; 840 data.nFileSizeLow = fi.nFileSizeLow; 841 } else { 842 wpath = widen(path); 843 h = FindFirstFile(wpath, &data); 844 free(wpath); 845 if(h == INVALID_HANDLE_VALUE) 846 oserror(); 847 if (!winfilematch(path, &data)) { 848 FindClose(h); 849 error(Enonexist); 850 } 851 FindClose(h); 852 } 853 utftorunes(data.cFileName, fslastelem(FS(c)->name), MAX_PATH); 854 } 855 856 return fsdirset(buf, n, &data, path, c, 0); 857 } 858 859 int 860 fswstat(Chan *c, uchar *buf, int n) 861 { 862 int wsd; 863 Dir dir; 864 Stat st; 865 Cname * volatile ph; 866 HANDLE h; 867 ulong attr; 868 User *ou, *gu; 869 WIN32_FIND_DATA data; 870 SECURITY_DESCRIPTOR *sd; 871 char *last, sdrock[SD_ROCK], path[MAX_PATH], newpath[MAX_PATH], strs[4*256]; 872 wchar_t wspath[MAX_PATH], wsnewpath[MAX_PATH]; 873 wchar_t *wpath; 874 int nmatch; 875 876 n = convM2D(buf, n, &dir, strs); 877 if(n == 0) 878 error(Eshortstat); 879 880 last = fspath(FS(c)->name, 0, path, FS(c)->spec); 881 utftorunes(wspath, path, MAX_PATH); 882 883 if(fsisroot(c)){ 884 if(dir.atime != ~0) 885 data.ftLastAccessTime = wintime(dir.atime); 886 if(dir.mtime != ~0) 887 data.ftLastWriteTime = wintime(dir.mtime); 888 utftorunes(data.cFileName, ".", MAX_PATH); 889 }else{ 890 h = FindFirstFile(wspath, &data); 891 if(h == INVALID_HANDLE_VALUE) 892 oserror(); 893 if (!winfilematch(path, &data)) { 894 FindClose(h); 895 error(Enonexist); 896 } 897 FindClose(h); 898 } 899 900 wsd = 0; 901 ou = nil; 902 gu = nil; 903 if(FS(c)->usesec) { 904 if(FS(c)->checksec && up->env->ui == fsnone) 905 error(Eperm); 906 907 /* 908 * find new owner and group 909 */ 910 if(!emptystr(dir.uid)){ 911 ou = unametouser(FS(c)->srv, dir.uid); 912 if(ou == nil) 913 oserror(); 914 } 915 if(!emptystr(dir.gid)){ 916 gu = unametouser(FS(c)->srv, dir.gid); 917 if(gu == nil){ 918 if(strcmp(dir.gid, "unknown") != 0 919 && strcmp(dir.gid, "deleted") != 0) 920 oserror(); 921 gu = ou; 922 } 923 } 924 925 /* 926 * find old stat info 927 */ 928 sd = secsd(path, sdrock); 929 if(sd == nil || !secsdstat(sd, &st, FS(c)->srv)){ 930 if(sd != nil && sd != (void*)sdrock) 931 free(sd); 932 oserror(); 933 } 934 if(sd != (void*)sdrock) 935 free(sd); 936 937 /* 938 * permission rules: 939 * if none, can't do anything 940 * chown => no way 941 * chgrp => current owner or group, and in new group 942 * mode/time => owner or in either group 943 * rename => write in parent 944 */ 945 if(ou == nil) 946 ou = st.owner; 947 if(FS(c)->checksec && st.owner != ou) 948 error(Eperm); 949 950 if(gu == nil) 951 gu = st.group; 952 if(st.group != gu){ 953 if(FS(c)->checksec 954 &&(!ismember(up->env->ui, ou) && !ismember(up->env->ui, gu) 955 || !ismember(up->env->ui, st.group))) 956 error(Eperm); 957 wsd = 1; 958 } 959 960 if(dir.atime != ~0 && unixtime(data.ftLastAccessTime) != dir.atime 961 || dir.mtime != ~0 && unixtime(data.ftLastWriteTime) != dir.mtime 962 || dir.mode != ~0 && st.mode != dir.mode){ 963 if(FS(c)->checksec 964 && !ismember(up->env->ui, ou) 965 && !ismember(up->env->ui, gu) 966 && !ismember(up->env->ui, st.group)) 967 error(Eperm); 968 if(dir.mode != ~0 && st.mode != dir.mode) 969 wsd = 1; 970 } 971 } 972 wpath = widen(dir.name); 973 nmatch = runescmp(wpath, data.cFileName); 974 free(wpath); 975 if(!emptystr(dir.name) && nmatch != 0){ 976 if(!okelem(dir.name, 1)) 977 error(Efilename); 978 ph = fswalkpath(FS(c)->name, "..", 1); 979 if(waserror()){ 980 cnameclose(ph); 981 nexterror(); 982 } 983 ph = fswalkpath(ph, dir.name, 0); 984 fspath(ph, 0, newpath, FS(c)->spec); 985 utftorunes(wsnewpath, newpath, MAX_PATH); 986 if(GetFileAttributes(wpath) != 0xffffffff && !winfileclash(newpath)) 987 error("file already exists"); 988 if(fsisroot(c)) 989 error(Eperm); 990 if(FS(c)->checksec){ 991 *last = '\0'; 992 seccheck(path, WMODE, FS(c)->srv); 993 *last = '\\'; 994 } 995 poperror(); 996 cnameclose(ph); 997 } 998 999 if(dir.atime != ~0 && unixtime(data.ftLastAccessTime) != dir.atime 1000 || dir.mtime != ~0 && unixtime(data.ftLastWriteTime) != dir.mtime) 1001 fssettime(path, dir.atime, dir.mtime); 1002 1003 attr = data.dwFileAttributes; 1004 if(dir.mode & 0222) 1005 attr &= ~FILE_ATTRIBUTE_READONLY; 1006 else 1007 attr |= FILE_ATTRIBUTE_READONLY; 1008 if(!fsisroot(c) 1009 && attr != data.dwFileAttributes 1010 && (attr & FILE_ATTRIBUTE_READONLY)) 1011 SetFileAttributes(wspath, attr); 1012 if(FS(c)->usesec && wsd){ 1013 ACL *acl = (ACL *) smalloc(ACL_ROCK); 1014 st.owner = ou; 1015 st.group = gu; 1016 if(dir.mode != ~0) 1017 st.mode = dir.mode; 1018 sd = secmksd(sdrock, &st, acl, data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); 1019 if(sd == nil || !SetFileSecurity(wspath, DACL_SECURITY_INFORMATION, sd)){ 1020 free(acl); 1021 oserror(); 1022 } 1023 free(acl); 1024 } 1025 1026 if(!fsisroot(c) 1027 && attr != data.dwFileAttributes 1028 && !(attr & FILE_ATTRIBUTE_READONLY)) 1029 SetFileAttributes(wspath, attr); 1030 1031 /* do last so path is valid throughout */ 1032 wpath = widen(dir.name); 1033 nmatch = runescmp(wpath, data.cFileName); 1034 free(wpath); 1035 if(!emptystr(dir.name) && nmatch != 0) { 1036 ph = fswalkpath(FS(c)->name, "..", 1); 1037 if(waserror()){ 1038 cnameclose(ph); 1039 nexterror(); 1040 } 1041 ph = fswalkpath(ph, dir.name, 0); 1042 fspath(ph, 0, newpath, FS(c)->spec); 1043 utftorunes(wsnewpath, newpath, MAX_PATH); 1044 /* 1045 * can't rename if it is open: if this process has it open, close it temporarily. 1046 */ 1047 if(!file_share_delete && c->flag & COPEN){ 1048 h = ntfd2h(FS(c)->fd); 1049 if(h != INVALID_HANDLE_VALUE) 1050 CloseHandle(h); /* woe betide it if ORCLOSE */ 1051 FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE); 1052 } 1053 if(!MoveFile(wspath, wsnewpath)) { 1054 oserror(); 1055 } else if(!file_share_delete && c->flag & COPEN) { 1056 int aflag; 1057 SECURITY_ATTRIBUTES sa; 1058 1059 /* The move succeeded, so open new file to maintain handle */ 1060 sa.nLength = sizeof(sa); 1061 sa.lpSecurityDescriptor = sd; 1062 sa.bInheritHandle = 0; 1063 if(c->flag & CRCLOSE) 1064 aflag = FILE_FLAG_DELETE_ON_CLOSE; 1065 h = CreateFile(wsnewpath, fsomode(c->mode & 0x3), FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, &sa, OPEN_EXISTING, aflag, 0); 1066 if(h == INVALID_HANDLE_VALUE) 1067 oserror(); 1068 FS(c)->fd = nth2fd(h); 1069 } 1070 cnameclose(FS(c)->name); 1071 poperror(); 1072 FS(c)->name = ph; 1073 } 1074 return n; 1075 } 1076 1077 static void 1078 fsremove(Chan *c) 1079 { 1080 int n; 1081 char *p, path[MAX_PATH]; 1082 wchar_t wspath[MAX_PATH]; 1083 1084 if(waserror()){ 1085 fsfree(c); 1086 nexterror(); 1087 } 1088 if(fsisroot(c)) 1089 error(Eperm); 1090 p = fspath(FS(c)->name, 0, path, FS(c)->spec); 1091 utftorunes(wspath, path, MAX_PATH); 1092 if(FS(c)->checksec){ 1093 *p = '\0'; 1094 seccheck(path, WMODE, FS(c)->srv); 1095 *p = '\\'; 1096 } 1097 if(c->qid.type & QTDIR) 1098 n = RemoveDirectory(wspath); 1099 else 1100 n = DeleteFile(wspath); 1101 if (!n) { 1102 ulong attr, mode; 1103 SECURITY_DESCRIPTOR *sd = nil; 1104 char sdrock[SD_ROCK]; 1105 Stat st; 1106 int secok; 1107 attr = GetFileAttributes(wspath); 1108 if(attr != 0xFFFFFFFF) { 1109 if (FS(c)->usesec) { 1110 sd = secsd(path, sdrock); 1111 secok = (sd != nil) && secsdstat(sd, &st, FS(c)->srv); 1112 if (secok) { 1113 ACL *acl = (ACL *) smalloc(ACL_ROCK); 1114 mode = st.mode; 1115 st.mode |= 0660; 1116 sd = secmksd(sdrock, &st, acl, attr & FILE_ATTRIBUTE_DIRECTORY); 1117 if(sd != nil) { 1118 SetFileSecurity(wspath, DACL_SECURITY_INFORMATION, sd); 1119 } 1120 free(acl); 1121 if(sd != nil && sd != (void*)sdrock) 1122 free(sd); 1123 sd = nil; 1124 } 1125 } 1126 SetFileAttributes(wspath, FILE_ATTRIBUTE_NORMAL); 1127 if(c->qid.type & QTDIR) 1128 n = RemoveDirectory(wspath); 1129 else 1130 n = DeleteFile(wspath); 1131 if (!n) { 1132 if (FS(c)->usesec && secok) { 1133 ACL *acl = (ACL *) smalloc(ACL_ROCK); 1134 st.mode = mode; 1135 sd = secmksd(sdrock, &st, acl, attr & FILE_ATTRIBUTE_DIRECTORY); 1136 if(sd != nil) { 1137 SetFileSecurity(wspath, DACL_SECURITY_INFORMATION, sd); 1138 } 1139 free(acl); 1140 } 1141 SetFileAttributes(wspath, attr); 1142 if(sd != nil && sd != (void*)sdrock) 1143 free(sd); 1144 } 1145 } 1146 } 1147 if(!n) 1148 oserror(); 1149 poperror(); 1150 fsfree(c); 1151 } 1152 1153 /* 1154 * check elem for illegal characters /\:*?"<> 1155 * ... and relatives are also disallowed, 1156 * since they specify grandparents, which we 1157 * are not prepared to handle 1158 */ 1159 static int 1160 okelem(char *elem, int nodots) 1161 { 1162 int c, dots; 1163 1164 dots = 0; 1165 while((c = *(uchar*)elem) != 0){ 1166 if(isntfrog[c]) 1167 return 0; 1168 if(c == '.' && dots >= 0) 1169 dots++; 1170 else 1171 dots = -1; 1172 elem++; 1173 } 1174 if(nodots) 1175 return dots <= 0; 1176 return dots <= 2; 1177 } 1178 1179 static int 1180 cnisroot(Cname *c) 1181 { 1182 return strcmp(c->s, "/") == 0; 1183 } 1184 1185 static int 1186 fsisroot(Chan *c) 1187 { 1188 return strcmp(FS(c)->name->s, "/") == 0; 1189 } 1190 1191 static char* 1192 fspath(Cname *c, char *ext, char *path, char *spec) 1193 { 1194 char *p, *last, *rootd; 1195 int extlen = 0; 1196 1197 rootd = spec != nil ? spec : rootdir; 1198 if(ext) 1199 extlen = strlen(ext) + 1; 1200 if(strlen(rootd) + extlen >= MAX_PATH) 1201 error(Etoolong); 1202 strcpy(path, rootd); 1203 if(cnisroot(c)){ 1204 if(ext) { 1205 strcat(path, "\\"); 1206 strcat(path, ext); 1207 } 1208 }else{ 1209 if(*c->s != '/') { 1210 if(strlen(path) + 1 >= MAX_PATH) 1211 error(Etoolong); 1212 strcat(path, "\\"); 1213 } 1214 if(strlen(path) + strlen(c->s) + extlen >= MAX_PATH) 1215 error(Etoolong); 1216 strcat(path, c->s); 1217 if(ext){ 1218 strcat(path, "\\"); 1219 strcat(path, ext); 1220 } 1221 } 1222 last = path; 1223 for(p = path; *p != '\0'; p++){ 1224 if(*p == '/' || *p == '\\'){ 1225 *p = '\\'; 1226 last = p; 1227 } 1228 } 1229 return last; 1230 } 1231 1232 extern void cleancname(Cname*); 1233 1234 static Cname * 1235 fswalkpath(Cname *c, char *name, int dup) 1236 { 1237 if(dup) 1238 c = newcname(c->s); 1239 c = addelem(c, name); 1240 if(isdotdot(name)) 1241 cleancname(c); 1242 return c; 1243 } 1244 1245 static char * 1246 fslastelem(Cname *c) 1247 { 1248 char *p; 1249 1250 p = c->s + c->len; 1251 while(p > c->s && p[-1] != '/') 1252 p--; 1253 return p; 1254 } 1255 1256 static int 1257 fsdirbadentry(WIN32_FIND_DATA *data) 1258 { 1259 wchar_t *s; 1260 1261 s = data->cFileName; 1262 if(s[0] == 0) 1263 return 1; 1264 if(s[0] == '.' && (s[1] == 0 || s[1] == '.' && s[2] == 0)) 1265 return 1; 1266 1267 return 0; 1268 } 1269 1270 static Fsdir* 1271 fsdirent(Chan *c, char *path, Fsdir *data) 1272 { 1273 wchar_t *wpath; 1274 HANDLE h; 1275 1276 h = ntfd2h(FS(c)->fd); 1277 if(data == nil) 1278 data = smalloc(sizeof(*data)); 1279 if(FS(c)->offset == 0){ 1280 if(h != INVALID_HANDLE_VALUE) 1281 FindClose(h); 1282 wpath = widen(path); 1283 h = FindFirstFile(wpath, data); 1284 free(wpath); 1285 FS(c)->fd = nth2fd(h); 1286 if(h == INVALID_HANDLE_VALUE){ 1287 free(data); 1288 return nil; 1289 } 1290 if(!fsdirbadentry(data)) 1291 return data; 1292 } 1293 do{ 1294 if(!FindNextFile(h, data)){ 1295 free(data); 1296 return nil; 1297 } 1298 }while(fsdirbadentry(data)); 1299 return data; 1300 } 1301 1302 static long 1303 fsdirread(Chan *c, uchar *va, int count, vlong offset) 1304 { 1305 int i, r; 1306 char path[MAX_PATH], *p; 1307 Fsdir *de; 1308 vlong o; 1309 1310 if(count == 0 || offset < 0) 1311 return 0; 1312 p = fspath(FS(c)->name, "*.*", path, FS(c)->spec); 1313 p++; 1314 de = nil; 1315 if(FS(c)->offset != offset){ 1316 de = FS(c)->de; 1317 if(FS(c)->de != nil){ 1318 free(FS(c)->de); 1319 FS(c)->de = nil; 1320 } 1321 FS(c)->offset = 0; 1322 for(o = 0; o < offset;){ 1323 de = fsdirent(c, path, de); 1324 if(de == nil){ 1325 FS(c)->offset = o; 1326 return 0; 1327 } 1328 runestoutf(p, de->cFileName, &path[MAX_PATH]-p); 1329 path[MAX_PATH-1] = '\0'; 1330 o += fsdirsize(de, path, c); 1331 } 1332 FS(c)->offset = offset; 1333 } 1334 for(i = 0; i < count;){ 1335 if(FS(c)->de != nil){ /* left over from previous read at offset */ 1336 de = FS(c)->de; 1337 FS(c)->de = nil; 1338 }else{ 1339 de = fsdirent(c, path, de); 1340 if(de == nil) 1341 break; 1342 } 1343 runestoutf(p, de->cFileName, &path[MAX_PATH]-p); 1344 path[MAX_PATH-1] = '\0'; 1345 r = fsdirset(va+i, count-i, de, path, c, 1); 1346 if(r <= 0){ 1347 /* won't fit; save for next read at this offset */ 1348 FS(c)->de = de; 1349 break; 1350 } 1351 i += r; 1352 FS(c)->offset += r; 1353 } 1354 return i; 1355 } 1356 1357 static ulong 1358 fsqidpath(char *p) 1359 { 1360 ulong h; 1361 int c; 1362 1363 h = 0; 1364 while(*p != '\0'){ 1365 /* force case insensitive file names */ 1366 c = *p++; 1367 if(c >= 'A' && c <= 'Z') 1368 c += 'a'-'A'; 1369 h = h * 19 ^ c; 1370 } 1371 return h; 1372 } 1373 1374 /* TO DO: substitute fixed, made-up (unlikely) names for these */ 1375 static char* devf[] = { "aux", "com1", "com2", "lpt1", "nul", "prn", nil }; 1376 1377 static int 1378 devfile(char *p) 1379 { 1380 char *s, *t, *u, **ss; 1381 1382 if((u = strrchr(p, '\\')) != nil) 1383 u++; 1384 else if((u = strrchr(p, '/')) != nil) 1385 u++; 1386 else 1387 u = p; 1388 for(ss = devf; *ss != nil; ss++){ 1389 for(s = *ss, t = u; *s != '\0' && *t != '\0' && *t != '.'; s++, t++) 1390 if(*s != *t && *s != *t+'a'-'A') 1391 break; 1392 if(*s == '\0' && (*t == '\0' || *t == '.')) 1393 return 1; 1394 } 1395 return 0; 1396 } 1397 1398 /* 1399 * there are other ways to figure out 1400 * the attributes and times for a file. 1401 * perhaps they are faster 1402 */ 1403 static int 1404 fsexist(char *p, Qid *q) 1405 { 1406 HANDLE h; 1407 WIN32_FIND_DATA data; 1408 wchar_t *wpath; 1409 1410 if(devfile(p)) 1411 return 0; 1412 wpath = widen(p); 1413 h = FindFirstFile(wpath, &data); 1414 free(wpath); 1415 if(h == INVALID_HANDLE_VALUE) 1416 return 0; 1417 if (!winfilematch(p, &data)) { 1418 FindClose(h); 1419 return 0; 1420 } 1421 FindClose(h); 1422 1423 q->path = fsqidpath(p); 1424 q->type = 0; 1425 1426 if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 1427 q->type |= QTDIR; 1428 1429 q->vers = unixtime(data.ftLastWriteTime); 1430 1431 return 1; 1432 } 1433 1434 static int 1435 fsdirset(char *edir, int n, WIN32_FIND_DATA *data, char *path, Chan *c, int isdir) 1436 { 1437 Dir dir; 1438 static char neveryone[] = "Everyone"; 1439 1440 dir.name = narrowen(data->cFileName); 1441 dir.muid = nil; 1442 dir.qid.path = fsqidpath(path); 1443 dir.qid.vers = 0; 1444 dir.qid.type = 0; 1445 dir.mode = 0; 1446 dir.atime = unixtime(data->ftLastAccessTime); 1447 dir.mtime = unixtime(data->ftLastWriteTime); 1448 dir.qid.vers = dir.mtime; 1449 dir.length = ((uvlong)data->nFileSizeHigh<<32) | ((uvlong)data->nFileSizeLow & ~((uvlong)0xFFFFFFFF<<32)); 1450 dir.type = 'U'; 1451 dir.dev = c->dev; 1452 1453 if(!FS(c)->usesec){ 1454 /* no NT security so make something up */ 1455 dir.uid = neveryone; 1456 dir.gid = neveryone; 1457 dir.mode = 0777; 1458 }else if(!secstat(&dir, path, FS(c)->srv)) 1459 oserror(); 1460 1461 if(data->dwFileAttributes & FILE_ATTRIBUTE_READONLY) 1462 dir.mode &= ~0222; 1463 if(data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){ 1464 dir.qid.type |= QTDIR; 1465 dir.mode |= DMDIR; 1466 dir.length = 0; 1467 } 1468 1469 if(isdir && sizeD2M(&dir) > n) 1470 n = -1; 1471 else 1472 n = convD2M(&dir, edir, n); 1473 if(dir.uid != neveryone) 1474 free(dir.uid); 1475 if(dir.gid != neveryone) 1476 free(dir.gid); 1477 free(dir.name); 1478 return n; 1479 } 1480 1481 static int 1482 fsdirsize(WIN32_FIND_DATA *data, char *path, Chan *c) 1483 { 1484 int i, n; 1485 1486 n = widebytes(data->cFileName); 1487 if(!FS(c)->usesec) 1488 n += 8+8; 1489 else{ 1490 i = secsize(path, FS(c)->srv); 1491 if(i < 0) 1492 oserror(); 1493 n += i; 1494 } 1495 return STATFIXLEN+n; 1496 } 1497 1498 static void 1499 fssettime(char *path, long at, long mt) 1500 { 1501 HANDLE h; 1502 FILETIME atime, mtime; 1503 wchar_t *wpath; 1504 1505 wpath = widen(path); 1506 h = CreateFile(wpath, GENERIC_WRITE, 1507 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 1508 free(wpath); 1509 if(h == INVALID_HANDLE_VALUE) 1510 return; 1511 mtime = wintime(mt); 1512 atime = wintime(at); 1513 if(!SetFileTime(h, 0, &atime, &mtime)){ 1514 CloseHandle(h); 1515 oserror(); 1516 } 1517 CloseHandle(h); 1518 } 1519 1520 static int 1521 fsomode(int m) 1522 { 1523 switch(m & 0x3) { 1524 case OREAD: 1525 case OEXEC: 1526 return GENERIC_READ; 1527 case OWRITE: 1528 return GENERIC_WRITE; 1529 case ORDWR: 1530 return GENERIC_READ|GENERIC_WRITE; 1531 } 1532 error(Ebadarg); 1533 return 0; 1534 } 1535 1536 static long 1537 unixtime(FILETIME ft) 1538 { 1539 vlong t; 1540 1541 t = (vlong)ft.dwLowDateTime + ((vlong)ft.dwHighDateTime<<32); 1542 t -= (vlong)10000000*134774*24*60*60; 1543 1544 return (long)(t/10000000); 1545 } 1546 1547 static FILETIME 1548 wintime(ulong t) 1549 { 1550 FILETIME ft; 1551 vlong vt; 1552 1553 vt = (vlong)t*10000000+(vlong)10000000*134774*24*60*60; 1554 1555 ft.dwLowDateTime = vt; 1556 ft.dwHighDateTime = vt>>32; 1557 1558 return ft; 1559 } 1560 1561 /* 1562 * the sec routines manage file permissions for nt. 1563 * nt files have an associated security descriptor, 1564 * which has in it an owner, a group, 1565 * and a discretionary acces control list, or acl, 1566 * which specifies the permissions for the file. 1567 * 1568 * the strategy for mapping between inferno owner, 1569 * group, other, and mode and nt file security is: 1570 * 1571 * inferno owner == nt file owner 1572 * inferno other == nt Everyone 1573 * inferno group == first non-owner, 1574 * non-Everyone user given in the acl, 1575 * or the owner if there is no such user. 1576 * we examine the entire acl when check for permissions, 1577 * but only report a subset. 1578 * 1579 * when we write an acl, we also give all permissions to 1580 * the special user rootname, who is supposed to run emu in server mode. 1581 */ 1582 static void 1583 secinit(void) 1584 { 1585 HMODULE lib; 1586 HANDLE token; 1587 TOKEN_PRIVILEGES *priv; 1588 char privrock[sizeof(TOKEN_PRIVILEGES) + 1*sizeof(LUID_AND_ATTRIBUTES)]; 1589 SID_IDENTIFIER_AUTHORITY id = SECURITY_CREATOR_SID_AUTHORITY; 1590 SID_IDENTIFIER_AUTHORITY wid = SECURITY_WORLD_SID_AUTHORITY; 1591 SID_IDENTIFIER_AUTHORITY ntid = SECURITY_NT_AUTHORITY; 1592 1593 lib = LoadLibraryA("netapi32"); 1594 if(lib == 0) { 1595 usesec = 0; 1596 return; 1597 } 1598 1599 net.UserGetGroups = (void*)GetProcAddress(lib, "NetUserGetGroups"); 1600 if(net.UserGetGroups == 0) 1601 panic("bad netapi32 library"); 1602 net.UserGetLocalGroups = (void*)GetProcAddress(lib, "NetUserGetLocalGroups"); 1603 if(net.UserGetLocalGroups == 0) 1604 panic("bad netapi32 library"); 1605 net.GetAnyDCName = (void*)GetProcAddress(lib, "NetGetAnyDCName"); 1606 if(net.GetAnyDCName == 0) 1607 panic("bad netapi32 library"); 1608 net.ApiBufferFree = (void*)GetProcAddress(lib, "NetApiBufferFree"); 1609 if(net.ApiBufferFree == 0) 1610 panic("bad netapi32 library"); 1611 1612 if(!AllocateAndInitializeSid(&id, 1, 1613 SECURITY_CREATOR_OWNER_RID, 1614 1, 2, 3, 4, 5, 6, 7, &creatorowner) 1615 || !AllocateAndInitializeSid(&id, 1, 1616 SECURITY_CREATOR_GROUP_RID, 1617 1, 2, 3, 4, 5, 6, 7, &creatorgroup) 1618 || !AllocateAndInitializeSid(&wid, 1, 1619 SECURITY_WORLD_RID, 1620 1, 2, 3, 4, 5, 6, 7, &everyone) 1621 || !AllocateAndInitializeSid(&ntid, 1, 1622 0, 1623 1, 2, 3, 4, 5, 6, 7, &ntignore)) 1624 panic("can't initialize well-known sids"); 1625 1626 fsnone = sidtouser(ntsrv, everyone); 1627 if(fsnone == nil) 1628 panic("can't make none user"); 1629 1630 /* 1631 * see if we are running as the emu server user 1632 * if so, set up SE_RESTORE_NAME privilege, 1633 * which allows setting the owner field in a security descriptor. 1634 * other interesting privileges are SE_TAKE_OWNERSHIP_NAME, 1635 * which enables changing the ownership of a file to yourself 1636 * regardless of the permissions on the file, SE_BACKUP_NAME, 1637 * which enables reading any files regardless of permission, 1638 * and SE_CHANGE_NOTIFY_NAME, which enables walking through 1639 * directories without X permission. 1640 * SE_RESTORE_NAME and SE_BACKUP_NAME together allow writing 1641 * and reading any file data, regardless of permission, 1642 * if the file is opened with FILE_BACKUP_SEMANTICS. 1643 */ 1644 isserver = 0; 1645 fsuser = secuser(); 1646 if(fsuser == nil) 1647 fsuser = fsnone; 1648 else if(runescmp(fsuser->name, rootname) == 0 1649 && OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token)){ 1650 priv = (TOKEN_PRIVILEGES*)privrock; 1651 priv->PrivilegeCount = 1; 1652 priv->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 1653 if(LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &priv->Privileges[0].Luid) 1654 && AdjustTokenPrivileges(token, 0, priv, 0, NULL, NULL)) 1655 isserver = 1; 1656 CloseHandle(token); 1657 } 1658 } 1659 1660 /* 1661 * get the User for the executing process 1662 */ 1663 static User* 1664 secuser(void) 1665 { 1666 DWORD need; 1667 HANDLE token; 1668 TOKEN_USER *tu; 1669 char turock[sizeof(TOKEN_USER) + MAX_SID]; 1670 1671 if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) 1672 return nil; 1673 1674 tu = (TOKEN_USER*)turock; 1675 if(!GetTokenInformation(token, TokenUser, tu, sizeof(turock), &need)){ 1676 CloseHandle(token); 1677 return nil; 1678 } 1679 CloseHandle(token); 1680 return sidtouser(nil, tu->User.Sid); 1681 } 1682 1683 static int 1684 secstat(Dir *dir, char *file, Rune *srv) 1685 { 1686 int ok, n; 1687 Stat st; 1688 char sdrock[SD_ROCK]; 1689 SECURITY_DESCRIPTOR *sd; 1690 1691 sd = secsd(file, sdrock); 1692 if(sd == nil){ 1693 int e = GetLastError(); 1694 if(e == ERROR_ACCESS_DENIED || e == ERROR_SHARING_VIOLATION){ 1695 dir->uid = strdup("unknown"); 1696 dir->gid = strdup("unknown"); 1697 if(dir->uid == nil || dir->gid == nil){ 1698 free(dir->uid); 1699 error(Enomem); /* will change to use kstrdup */ 1700 } 1701 dir->mode = 0; 1702 return 1; 1703 } 1704 return 0; 1705 } 1706 ok = secsdstat(sd, &st, srv); 1707 if(sd != (void*)sdrock) 1708 free(sd); 1709 if(ok){ 1710 dir->mode = st.mode; 1711 n = runenlen(st.owner->name, runeslen(st.owner->name)); 1712 dir->uid = smalloc(n+1); 1713 runestoutf(dir->uid, st.owner->name, n+1); 1714 n = runenlen(st.group->name, runeslen(st.group->name)); 1715 dir->gid = smalloc(n+1); 1716 runestoutf(dir->gid, st.group->name, n+1); 1717 } 1718 return ok; 1719 } 1720 1721 static int 1722 secsize(char *file, Rune *srv) 1723 { 1724 int ok; 1725 Stat st; 1726 char sdrock[SD_ROCK]; 1727 SECURITY_DESCRIPTOR *sd; 1728 1729 sd = secsd(file, sdrock); 1730 if(sd == nil){ 1731 int e = GetLastError(); 1732 if(e == ERROR_ACCESS_DENIED || e == ERROR_SHARING_VIOLATION) 1733 return 7+7; 1734 return -1; 1735 } 1736 ok = secsdstat(sd, &st, srv); 1737 if(sd != (void*)sdrock) 1738 free(sd); 1739 if(ok) 1740 return runenlen(st.owner->name, runeslen(st.owner->name))+runenlen(st.group->name, runeslen(st.group->name)); 1741 return -1; 1742 } 1743 1744 /* 1745 * verify that u had access to file 1746 */ 1747 static void 1748 seccheck(char *file, ulong access, Rune *srv) 1749 { 1750 if(!sechasperm(file, access, srv)) 1751 error(Eperm); 1752 } 1753 1754 static int 1755 sechasperm(char *file, ulong access, Rune *srv) 1756 { 1757 int ok; 1758 char sdrock[SD_ROCK]; 1759 SECURITY_DESCRIPTOR *sd; 1760 1761 /* 1762 * only really needs dacl info 1763 */ 1764 sd = secsd(file, sdrock); 1765 if(sd == nil) 1766 return 0; 1767 ok = secsdhasperm(sd, access, srv); 1768 if(sd != (void*)sdrock) 1769 free(sd); 1770 return ok; 1771 } 1772 1773 static SECURITY_DESCRIPTOR* 1774 secsd(char *file, char sdrock[SD_ROCK]) 1775 { 1776 DWORD need; 1777 SECURITY_DESCRIPTOR *sd; 1778 char *path, pathrock[6]; 1779 wchar_t *wpath; 1780 1781 path = file; 1782 if(path[0] != '\0' && path[1] == ':' && path[2] == '\0'){ 1783 path = pathrock; 1784 strcpy(path, "?:\\."); 1785 path[0] = file[0]; 1786 } 1787 sd = (SECURITY_DESCRIPTOR*)sdrock; 1788 need = 0; 1789 wpath = widen(path); 1790 if(GetFileSecurity(wpath, OWNER_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION, sd, SD_ROCK, &need)) { 1791 free(wpath); 1792 return sd; 1793 } 1794 if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) { 1795 free(wpath); 1796 return nil; 1797 } 1798 sd = malloc(need); 1799 if(sd == nil) { 1800 free(wpath); 1801 error(Enomem); 1802 } 1803 if(GetFileSecurity(wpath, OWNER_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION, sd, need, &need)) { 1804 free(wpath); 1805 return sd; 1806 } 1807 free(wpath); 1808 free(sd); 1809 return nil; 1810 } 1811 1812 static int 1813 secsdstat(SECURITY_DESCRIPTOR *sd, Stat *st, Rune *srv) 1814 { 1815 ACL *acl; 1816 BOOL hasacl, b; 1817 ACE_HEADER *aceh; 1818 User *owner, *group; 1819 SID *sid, *osid, *gsid; 1820 ACCESS_ALLOWED_ACE *ace; 1821 int i, allow, deny, *p, m; 1822 ACL_SIZE_INFORMATION size; 1823 1824 st->mode = 0; 1825 1826 osid = nil; 1827 gsid = nil; 1828 if(!GetSecurityDescriptorOwner(sd, &osid, &b) 1829 || !GetSecurityDescriptorDacl(sd, &hasacl, &acl, &b)) 1830 return 0; 1831 1832 if(acl == 0) 1833 size.AceCount = 0; 1834 else if(!GetAclInformation(acl, &size, sizeof(size), AclSizeInformation)) 1835 return 0; 1836 1837 /* 1838 * first pass through acl finds group 1839 */ 1840 for(i = 0; i < size.AceCount; i++){ 1841 if(!GetAce(acl, i, &aceh)) 1842 continue; 1843 if(aceh->AceFlags & INHERIT_ONLY_ACE) 1844 continue; 1845 1846 if(aceh->AceType != ACCESS_ALLOWED_ACE_TYPE 1847 && aceh->AceType != ACCESS_DENIED_ACE_TYPE) 1848 continue; 1849 1850 ace = (ACCESS_ALLOWED_ACE*)aceh; 1851 sid = (SID*)&ace->SidStart; 1852 if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup)) 1853 continue; 1854 1855 if(EqualSid(sid, everyone)) 1856 ; 1857 else if(EqualSid(sid, osid)) 1858 ; 1859 else if(EqualPrefixSid(sid, ntignore)) 1860 continue; /* boring nt accounts */ 1861 else{ 1862 gsid = sid; 1863 break; 1864 } 1865 } 1866 if(gsid == nil) 1867 gsid = osid; 1868 1869 owner = sidtouser(srv, osid); 1870 group = sidtouser(srv, gsid); 1871 if(owner == 0 || group == 0) 1872 return 0; 1873 1874 /* no acl means full access */ 1875 allow = 0; 1876 if(acl == 0) 1877 allow = 0777; 1878 deny = 0; 1879 for(i = 0; i < size.AceCount; i++){ 1880 if(!GetAce(acl, i, &aceh)) 1881 continue; 1882 if(aceh->AceFlags & INHERIT_ONLY_ACE) 1883 continue; 1884 1885 if(aceh->AceType == ACCESS_ALLOWED_ACE_TYPE) 1886 p = &allow; 1887 else if(aceh->AceType == ACCESS_DENIED_ACE_TYPE) 1888 p = &deny; 1889 else 1890 continue; 1891 1892 ace = (ACCESS_ALLOWED_ACE*)aceh; 1893 sid = (SID*)&ace->SidStart; 1894 if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup)) 1895 continue; 1896 1897 m = 0; 1898 if(ace->Mask & FILE_EXECUTE) 1899 m |= 1; 1900 if(ace->Mask & FILE_WRITE_DATA) 1901 m |= 2; 1902 if(ace->Mask & FILE_READ_DATA) 1903 m |= 4; 1904 1905 if(ismembersid(srv, owner, sid)) 1906 *p |= (m << 6) & ~(allow|deny) & 0700; 1907 if(ismembersid(srv, group, sid)) 1908 *p |= (m << 3) & ~(allow|deny) & 0070; 1909 if(EqualSid(everyone, sid)) 1910 *p |= m & ~(allow|deny) & 0007; 1911 } 1912 1913 st->mode = allow & ~deny; 1914 st->owner = owner; 1915 st->group = group; 1916 return 1; 1917 } 1918 1919 static int 1920 secsdhasperm(SECURITY_DESCRIPTOR *sd, ulong access, Rune *srv) 1921 { 1922 User *u; 1923 ACL *acl; 1924 BOOL hasacl, b; 1925 ACE_HEADER *aceh; 1926 SID *sid, *osid, *gsid; 1927 int i, allow, deny, *p, m; 1928 ACCESS_ALLOWED_ACE *ace; 1929 ACL_SIZE_INFORMATION size; 1930 1931 u = up->env->ui; 1932 allow = 0; 1933 deny = 0; 1934 osid = nil; 1935 gsid = nil; 1936 if(!GetSecurityDescriptorDacl(sd, &hasacl, &acl, &b)) 1937 return 0; 1938 1939 /* no acl means full access */ 1940 if(acl == 0) 1941 return 1; 1942 if(!GetAclInformation(acl, &size, sizeof(size), AclSizeInformation)) 1943 return 0; 1944 for(i = 0; i < size.AceCount; i++){ 1945 if(!GetAce(acl, i, &aceh)) 1946 continue; 1947 if(aceh->AceFlags & INHERIT_ONLY_ACE) 1948 continue; 1949 1950 if(aceh->AceType == ACCESS_ALLOWED_ACE_TYPE) 1951 p = &allow; 1952 else if(aceh->AceType == ACCESS_DENIED_ACE_TYPE) 1953 p = &deny; 1954 else 1955 continue; 1956 1957 ace = (ACCESS_ALLOWED_ACE*)aceh; 1958 sid = (SID*)&ace->SidStart; 1959 if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup)) 1960 continue; 1961 1962 m = ace->Mask; 1963 1964 if(ismembersid(srv, u, sid)) 1965 *p |= m & ~(allow|deny); 1966 } 1967 1968 allow &= ~deny; 1969 1970 return (allow & access) == access; 1971 } 1972 1973 static SECURITY_DESCRIPTOR* 1974 secmksd(char *sdrock, Stat *st, ACL *dacl, int isdir) 1975 { 1976 int m; 1977 1978 ulong mode; 1979 ACE_HEADER *aceh; 1980 SECURITY_DESCRIPTOR *sd; 1981 1982 sd = (SECURITY_DESCRIPTOR*)sdrock; 1983 if(!InitializeAcl(dacl, ACL_ROCK, ACL_REVISION)) 1984 return nil; 1985 1986 mode = st->mode; 1987 if(st->owner == st->group){ 1988 mode |= (mode >> 3) & 0070; 1989 mode |= (mode << 3) & 0700; 1990 } 1991 1992 1993 m = modetomask[(mode>>6) & 7]; 1994 if(!AddAccessAllowedAce(dacl, ACL_REVISION, m, st->owner->sid)) 1995 return nil; 1996 1997 if(isdir && !AddAccessAllowedAce(dacl, ACL_REVISION, m, creatorowner)) 1998 return nil; 1999 2000 m = modetomask[(mode>>3) & 7]; 2001 if(!AddAccessAllowedAce(dacl, ACL_REVISION, m, st->group->sid)) 2002 return nil; 2003 2004 m = modetomask[(mode>>0) & 7]; 2005 if(!AddAccessAllowedAce(dacl, ACL_REVISION, m, everyone)) 2006 return nil; 2007 2008 if(isdir){ 2009 /* hack to add inherit flags */ 2010 if(!GetAce(dacl, 1, &aceh)) 2011 return nil; 2012 aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE; 2013 if(!GetAce(dacl, 2, &aceh)) 2014 return nil; 2015 aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE; 2016 if(!GetAce(dacl, 3, &aceh)) 2017 return nil; 2018 aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE; 2019 } 2020 2021 /* 2022 * allow server user to access any file 2023 */ 2024 if(isserver){ 2025 if(!AddAccessAllowedAce(dacl, ACL_REVISION, RMODE|WMODE|XMODE, fsuser->sid)) 2026 return nil; 2027 if(isdir){ 2028 if(!GetAce(dacl, 4, &aceh)) 2029 return nil; 2030 aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE; 2031 } 2032 } 2033 2034 if(!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION)) 2035 return nil; 2036 if(!SetSecurityDescriptorDacl(sd, 1, dacl, 0)) 2037 return nil; 2038 // if(isserver && !SetSecurityDescriptorOwner(sd, st->owner->sid, 0)) 2039 // return nil; 2040 return sd; 2041 } 2042 2043 /* 2044 * the user manipulation routines 2045 * just make it easier to deal with user identities 2046 */ 2047 static User* 2048 sidtouser(Rune *srv, SID *s) 2049 { 2050 SID_NAME_USE type; 2051 Rune aname[100], dname[100]; 2052 DWORD naname, ndname; 2053 User *u; 2054 2055 qlock(&users.lk); 2056 for(u = users.u; u != 0; u = u->next) 2057 if(EqualSid(s, u->sid)) 2058 break; 2059 qunlock(&users.lk); 2060 2061 if(u != 0) 2062 return u; 2063 2064 naname = sizeof(aname); 2065 ndname = sizeof(dname); 2066 2067 if(!LookupAccountSidW(srv, s, aname, &naname, dname, &ndname, &type)) 2068 return nil; 2069 return mkuser(s, type, aname, dname); 2070 } 2071 2072 static User* 2073 domnametouser(Rune *srv, Rune *name, Rune *dom) 2074 { 2075 User *u; 2076 2077 qlock(&users.lk); 2078 for(u = users.u; u != 0; u = u->next) 2079 if(runescmp(name, u->name) == 0 && runescmp(dom, u->dom) == 0) 2080 break; 2081 qunlock(&users.lk); 2082 if(u == 0) 2083 u = nametouser(srv, name); 2084 return u; 2085 } 2086 2087 static User* 2088 nametouser(Rune *srv, Rune *name) 2089 { 2090 char sidrock[MAX_SID]; 2091 SID *sid; 2092 SID_NAME_USE type; 2093 Rune dom[MAX_PATH]; 2094 DWORD nsid, ndom; 2095 2096 sid = (SID*)sidrock; 2097 nsid = sizeof(sidrock); 2098 ndom = sizeof(dom); 2099 if(!LookupAccountNameW(srv, name, sid, &nsid, dom, &ndom, &type)) 2100 return nil; 2101 2102 return mkuser(sid, type, name, dom); 2103 } 2104 2105 /* 2106 * this mapping could be cached 2107 */ 2108 static User* 2109 unametouser(Rune *srv, char *name) 2110 { 2111 Rune rname[MAX_PATH]; 2112 2113 utftorunes(rname, name, MAX_PATH); 2114 return nametouser(srv, rname); 2115 } 2116 2117 /* 2118 * make a user structure and add it to the global cache. 2119 */ 2120 static User* 2121 mkuser(SID *sid, int type, Rune *name, Rune *dom) 2122 { 2123 User *u; 2124 2125 qlock(&users.lk); 2126 for(u = users.u; u != 0; u = u->next){ 2127 if(EqualSid(sid, u->sid)){ 2128 qunlock(&users.lk); 2129 return u; 2130 } 2131 } 2132 2133 switch(type) { 2134 default: 2135 break; 2136 case SidTypeDeletedAccount: 2137 name = L"deleted"; 2138 break; 2139 case SidTypeInvalid: 2140 name = L"invalid"; 2141 break; 2142 case SidTypeUnknown: 2143 name = L"unknown"; 2144 break; 2145 } 2146 2147 u = malloc(sizeof(User)); 2148 if(u == nil){ 2149 qunlock(&users.lk); 2150 return 0; 2151 } 2152 u->next = nil; 2153 u->group = nil; 2154 u->sid = dupsid(sid); 2155 u->type = type; 2156 u->name = nil; 2157 if(name != nil) 2158 u->name = runesdup(name); 2159 u->dom = nil; 2160 if(dom != nil) 2161 u->dom = runesdup(dom); 2162 2163 u->next = users.u; 2164 users.u = u; 2165 2166 qunlock(&users.lk); 2167 return u; 2168 } 2169 2170 /* 2171 * check if u is a member of gsid, 2172 * which might be a group. 2173 */ 2174 static int 2175 ismembersid(Rune *srv, User *u, SID *gsid) 2176 { 2177 User *g; 2178 2179 if(EqualSid(u->sid, gsid)) 2180 return 1; 2181 2182 g = sidtouser(srv, gsid); 2183 if(g == 0) 2184 return 0; 2185 return ismember(u, g); 2186 } 2187 2188 static int 2189 ismember(User *u, User *g) 2190 { 2191 Gmem *grps; 2192 2193 if(EqualSid(u->sid, g->sid)) 2194 return 1; 2195 2196 if(EqualSid(g->sid, everyone)) 2197 return 1; 2198 2199 qlock(&u->lk); 2200 addgroups(u, 0); 2201 for(grps = u->group; grps != 0; grps = grps->next){ 2202 if(EqualSid(grps->user->sid, g->sid)){ 2203 qunlock(&u->lk); 2204 return 1; 2205 } 2206 } 2207 qunlock(&u->lk); 2208 return 0; 2209 } 2210 2211 /* 2212 * find out what groups a user belongs to. 2213 * if force, throw out the old info and do it again. 2214 * 2215 * note that a global group is also know as a group, 2216 * and a local group is also know as an alias. 2217 * global groups can only contain users. 2218 * local groups can contain global groups or users. 2219 * this code finds all global groups to which a user belongs, 2220 * and all the local groups to which the user or a global group 2221 * containing the user belongs. 2222 */ 2223 static void 2224 addgroups(User *u, int force) 2225 { 2226 LOCALGROUP_USERS_INFO_0 *loc; 2227 GROUP_USERS_INFO_0 *grp; 2228 DWORD i, n, rem; 2229 User *gu; 2230 Gmem *g, *next; 2231 Rune *srv, srvrock[MAX_PATH]; 2232 2233 if(force){ 2234 u->gotgroup = 0; 2235 for(g = u->group; g != nil; g = next){ 2236 next = g->next; 2237 free(g); 2238 } 2239 u->group = nil; 2240 } 2241 if(u->gotgroup) 2242 return; 2243 u->gotgroup = 1; 2244 2245 rem = 1; 2246 n = 0; 2247 srv = domsrv(u->dom, srvrock); 2248 while(rem != n){ 2249 i = net.UserGetGroups(srv, u->name, 0, 2250 (BYTE**)&grp, 1024, &n, &rem); 2251 if(i != NERR_Success && i != ERROR_MORE_DATA) 2252 break; 2253 for(i = 0; i < n; i++){ 2254 gu = domnametouser(srv, grp[i].grui0_name, u->dom); 2255 if(gu == 0) 2256 continue; 2257 g = malloc(sizeof(Gmem)); 2258 if(g == nil) 2259 error(Enomem); 2260 g->user = gu; 2261 g->next = u->group; 2262 u->group = g; 2263 } 2264 net.ApiBufferFree(grp); 2265 } 2266 2267 rem = 1; 2268 n = 0; 2269 while(rem != n){ 2270 i = net.UserGetLocalGroups(srv, u->name, 0, LG_INCLUDE_INDIRECT, 2271 (BYTE**)&loc, 1024, &n, &rem); 2272 if(i != NERR_Success && i != ERROR_MORE_DATA) 2273 break; 2274 for(i = 0; i < n; i++){ 2275 gu = domnametouser(srv, loc[i].lgrui0_name, u->dom); 2276 if(gu == NULL) 2277 continue; 2278 g = malloc(sizeof(Gmem)); 2279 if(g == nil) 2280 error(Enomem); 2281 g->user = gu; 2282 g->next = u->group; 2283 u->group = g; 2284 } 2285 net.ApiBufferFree(loc); 2286 } 2287 } 2288 2289 static SID* 2290 dupsid(SID *sid) 2291 { 2292 SID *nsid; 2293 int n; 2294 2295 n = GetLengthSid(sid); 2296 nsid = malloc(n); 2297 if(nsid == nil || !CopySid(n, nsid, sid)) 2298 panic("can't copy sid"); 2299 return nsid; 2300 } 2301 2302 /* 2303 * return the name of the server machine for file 2304 */ 2305 static Rune* 2306 filesrv(char *file) 2307 { 2308 int n; 2309 Rune *srv; 2310 char *p, uni[MAX_PATH], mfile[MAX_PATH]; 2311 wchar_t vol[3]; 2312 2313 strcpy(mfile, file); 2314 /* assume file is a fully qualified name - X: or \\server */ 2315 if(file[1] == ':') { 2316 vol[0] = file[0]; 2317 vol[1] = file[1]; 2318 vol[2] = 0; 2319 if(GetDriveType(vol) != DRIVE_REMOTE) 2320 return 0; 2321 n = sizeof(uni); 2322 if(WNetGetUniversalName(vol, UNIVERSAL_NAME_INFO_LEVEL, uni, &n) != NO_ERROR) 2323 return nil; 2324 runestoutf(mfile, ((UNIVERSAL_NAME_INFO*)uni)->lpUniversalName, MAX_PATH); 2325 file = mfile; 2326 } 2327 file += 2; 2328 p = strchr(file, '\\'); 2329 if(p == 0) 2330 n = strlen(file); 2331 else 2332 n = p - file; 2333 if(n >= MAX_PATH) 2334 n = MAX_PATH-1; 2335 2336 memmove(uni, file, n); 2337 uni[n] = '\0'; 2338 2339 srv = malloc((n + 1) * sizeof(Rune)); 2340 if(srv == nil) 2341 panic("filesrv: no memory"); 2342 utftorunes(srv, uni, n+1); 2343 return srv; 2344 } 2345 2346 /* 2347 * does the file system support acls? 2348 */ 2349 static int 2350 fsacls(char *file) 2351 { 2352 char *p; 2353 DWORD flags; 2354 char path[MAX_PATH]; 2355 wchar_t wpath[MAX_PATH]; 2356 2357 /* assume file is a fully qualified name - X: or \\server */ 2358 if(file[1] == ':') { 2359 path[0] = file[0]; 2360 path[1] = file[1]; 2361 path[2] = '\\'; 2362 path[3] = 0; 2363 } else { 2364 strcpy(path, file); 2365 p = strchr(path+2, '\\'); 2366 if(p == 0) 2367 return 0; 2368 p = strchr(p+1, '\\'); 2369 if(p == 0) 2370 strcat(path, "\\"); 2371 else 2372 p[1] = 0; 2373 } 2374 utftorunes(wpath, path, MAX_PATH); 2375 if(!GetVolumeInformation(wpath, NULL, 0, NULL, NULL, &flags, NULL, 0)) 2376 return 0; 2377 2378 return flags & FS_PERSISTENT_ACLS; 2379 } 2380 2381 /* 2382 * given a domain, find out the server to ask about its users. 2383 * we just ask the local machine to do the translation, 2384 * so it might fail sometimes. in those cases, we don't 2385 * trust the domain anyway, and vice versa, so it's not 2386 * clear what benifit we would gain by getting the answer "right". 2387 */ 2388 static Rune* 2389 domsrv(Rune *dom, Rune srv[MAX_PATH]) 2390 { 2391 Rune *psrv; 2392 int n, r; 2393 2394 if(dom[0] == 0) 2395 return nil; 2396 2397 r = net.GetAnyDCName(NULL, dom, (LPBYTE*)&psrv); 2398 if(r == NERR_Success) { 2399 n = runeslen(psrv); 2400 if(n >= MAX_PATH) 2401 n = MAX_PATH-1; 2402 memmove(srv, psrv, n*sizeof(Rune)); 2403 srv[n] = 0; 2404 net.ApiBufferFree(psrv); 2405 return srv; 2406 } 2407 2408 return nil; 2409 } 2410 2411 Rune* 2412 runesdup(Rune *r) 2413 { 2414 int n; 2415 Rune *s; 2416 2417 n = runeslen(r) + 1; 2418 s = malloc(n * sizeof(Rune)); 2419 if(s == nil) 2420 error(Enomem); 2421 memmove(s, r, n * sizeof(Rune)); 2422 return s; 2423 } 2424 2425 int 2426 runeslen(Rune *r) 2427 { 2428 int n; 2429 2430 n = 0; 2431 while(*r++ != 0) 2432 n++; 2433 return n; 2434 } 2435 2436 char* 2437 runestoutf(char *p, Rune *r, int nc) 2438 { 2439 char *op, *ep; 2440 int n, c; 2441 2442 op = p; 2443 ep = p + nc; 2444 while(c = *r++) { 2445 n = 1; 2446 if(c >= Runeself) 2447 n = runelen(c); 2448 if(p + n >= ep) 2449 break; 2450 if(c < Runeself) 2451 *p++ = c; 2452 else 2453 p += runetochar(p, r-1); 2454 } 2455 *p = '\0'; 2456 return op; 2457 } 2458 2459 Rune* 2460 utftorunes(Rune *r, char *p, int nc) 2461 { 2462 Rune *or, *er; 2463 2464 or = r; 2465 er = r + nc; 2466 while(*p != '\0' && r + 1 < er) 2467 p += chartorune(r++, p); 2468 *r = '\0'; 2469 return or; 2470 } 2471 2472 int 2473 runescmp(Rune *s1, Rune *s2) 2474 { 2475 Rune r1, r2; 2476 2477 for(;;) { 2478 r1 = *s1++; 2479 r2 = *s2++; 2480 if(r1 != r2) { 2481 if(r1 > r2) 2482 return 1; 2483 return -1; 2484 } 2485 if(r1 == 0) 2486 return 0; 2487 } 2488 } 2489 2490 Dev fsdevtab = { 2491 'U', 2492 "fs", 2493 2494 fsinit, 2495 fsattach, 2496 fswalk, 2497 fsstat, 2498 fsopen, 2499 fscreate, 2500 fsclose, 2501 fsread, 2502 devbread, 2503 fswrite, 2504 devbwrite, 2505 fsremove, 2506 fswstat 2507 }; 2508