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