#include <9pm/windows.h>
#include <9pm/u.h>
#include <9pm/libc.h>
#include "dat.h"
#include "fns.h"
enum
{
MAX_SID = sizeof(SID) + SID_MAX_SUB_AUTHORITIES*sizeof(DWORD),
BIG_SD = SECURITY_DESCRIPTOR_MIN_LENGTH
+ sizeof(ACL) + 20*(sizeof(ACCESS_ALLOWED_ACE)+MAX_SID)
};
typedef struct User User;
typedef struct Gmem Gmem;
struct User
{
QLock lk;
SID *sid;
Rune *name;
Rune *dom;
int type;
int gotgroup; /* tried to add group */
Gmem *group;
User *next;
};
struct Gmem
{
User *user;
Gmem *next;
};
static struct {
NET_API_STATUS (NET_API_FUNCTION *UserGetLocalGroups)(
LPWSTR servername,
LPWSTR username,
DWORD level,
DWORD flags,
LPBYTE *bufptr,
DWORD prefmaxlen,
LPDWORD entriesread,
LPDWORD totalentries);
NET_API_STATUS (NET_API_FUNCTION *UserGetGroups)(
LPWSTR servername,
LPWSTR username,
DWORD level,
LPBYTE *bufptr,
DWORD prefmaxlen,
LPDWORD entriesread,
LPDWORD totalentries);
NET_API_STATUS (NET_API_FUNCTION *GetAnyDCName)(
LPCWSTR ServerName,
LPCWSTR DomainName,
LPBYTE *Buffer);
NET_API_STATUS (NET_API_FUNCTION *ApiBufferFree)(LPVOID Buffer);
} net;
typedef struct Valmap Valmap;
struct Valmap {
int val;
char *name;
};
static SID *dupsid(SID*);
static int ismember(Rune*, User*, SID*);
static User *sidtouser(Rune*, SID*);
static User *domnametouser(Rune*, Rune*, Rune*);
static User *nametouser(Rune*, Rune*);
static void addgroups(User*);
static User *mkuser(SID*, int, Rune*, Rune*);
static Rune *filesrv(Rune*, Rune[MAX_PATH]);
static Rune *domsrv(Rune *, Rune[MAX_PATH]);
static int fsacls(Rune*);
static void perminit(int);
static SID *creatorowner;
static SID *creatorgroup;
static SID *everyone;
static SID *ntignore;
static struct
{
QLock lk;
User *u;
}users;
int win_usesecurity;
void
win_secinit(void)
{
HMODULE lib;
SID_IDENTIFIER_AUTHORITY id = SECURITY_CREATOR_SID_AUTHORITY;
SID_IDENTIFIER_AUTHORITY wid = SECURITY_WORLD_SID_AUTHORITY;
SID_IDENTIFIER_AUTHORITY ntid = SECURITY_NT_AUTHORITY;
lib = LoadLibrary(L"netapi32");
if(lib == 0) {
win_usesecurity = 0;
return;
}
win_usesecurity = 1;
net.UserGetGroups = (void*)GetProcAddress(lib, "NetUserGetGroups");
if(net.UserGetGroups == 0)
win_fatal("bad netapi32 library");
net.UserGetLocalGroups = (void*)GetProcAddress(lib, "NetUserGetLocalGroups");
if(net.UserGetLocalGroups == 0)
win_fatal("bad netapi32 library");
net.GetAnyDCName = (void*)GetProcAddress(lib, "NetGetAnyDCName");
if(net.GetAnyDCName == 0)
win_fatal("bad netapi32 library");
net.ApiBufferFree = (void*)GetProcAddress(lib, "NetApiBufferFree");
if(net.ApiBufferFree == 0)
win_fatal("bad netapi32 library");
AllocateAndInitializeSid(&id, 1, SECURITY_CREATOR_OWNER_RID,
1, 2, 3, 4, 5, 6, 7, &creatorowner);
AllocateAndInitializeSid(&id, 1, SECURITY_CREATOR_GROUP_RID,
1, 2, 3, 4, 5, 6, 7, &creatorgroup);
AllocateAndInitializeSid(&wid, 1, SECURITY_WORLD_RID,
1, 2, 3, 4, 5, 6, 7, &everyone);
AllocateAndInitializeSid(&ntid, 1, 0,
1, 2, 3, 4, 5, 6, 7, &ntignore);
perminit(0);
}
static void
perminit(int backup)
{
TOKEN_PRIVILEGES *priv;
char privrock[sizeof(TOKEN_PRIVILEGES) + 1*sizeof(LUID_AND_ATTRIBUTES)];
HANDLE token;
/*
* enabling take ownership allows us to chown to ourself
* regargless of permissions on the file.
* enabling restore allows chowning to others
* if we have permission to write the owner.
*/
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token))
return;
priv = (TOKEN_PRIVILEGES*)privrock;
priv->PrivilegeCount = 1;
priv->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
/* lets be super user */
if(LookupPrivilegeValue(NULL, SE_TAKE_OWNERSHIP_NAME, &priv->Privileges[0].Luid))
if(AdjustTokenPrivileges(token, 0, priv, 0, NULL, NULL))
;
if(backup) {
/* netapp is broken - until it is fixed these privleges can not be enabled
* I do try and enable these when I do a secwperm...
*/
if(LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &priv->Privileges[0].Luid))
if(AdjustTokenPrivileges(token, 0, priv, 0, NULL, NULL))
;
if(LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &priv->Privileges[0].Luid))
if(AdjustTokenPrivileges(token, 0, priv, 0, NULL, NULL))
;
}
if(LookupPrivilegeValue(NULL, SE_CHANGE_NOTIFY_NAME, &priv->Privileges[0].Luid))
if(AdjustTokenPrivileges(token, 0, priv, 0, NULL, NULL))
;
CloseHandle(token);
}
int
win_secperm(Dir *dir, Rune *file)
{
SECURITY_INFORMATION si;
SECURITY_DESCRIPTOR *sd;
ACL_SIZE_INFORMATION size;
ACL *acl;
ACE_HEADER *aceh;
ACCESS_ALLOWED_ACE *ace;
SID *sid, *osid, *gsid;
BOOL hasacl, b;
DWORD need, i;
User *owner, *group;
int allow, deny, *p, m;
Rune *srv, *srvrock;
char *sdrock;
sdrock = win_malloc(BIG_SD);
srvrock = win_malloc(sizeof(Rune)*MAX_PATH);
/* secdump(file);*/
osid = NULL;
gsid = NULL;
si = OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
sd = (SECURITY_DESCRIPTOR*)sdrock;
need = 0;
if(!GetFileSecurity(file, si, sd, BIG_SD, &need)) {
if(GetLastError() == ERROR_ACCESS_DENIED) {
dir->uid = strdup("unknown");
dir->gid = strdup("unknown");
dir->mode = 0;
goto good;
}
if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
goto bad;
sd = mallocz(need, 1);
if(!GetFileSecurity(file, si, sd, need, &need))
goto bad;
}
if(!GetSecurityDescriptorOwner(sd, &osid, &b)
|| !GetSecurityDescriptorDacl(sd, &hasacl, &acl, &b))
goto bad;
if(acl == 0)
size.AceCount = 0;
else if(!GetAclInformation(acl, &size, sizeof(size), AclSizeInformation))
goto bad;
srv = filesrv(file, srvrock);
/*
* first pass through acl finds group
*/
for(i = 0; i < size.AceCount; i++){
if(!GetAce(acl, i, &aceh))
continue;
if(aceh->AceFlags & INHERIT_ONLY_ACE)
continue;
if(aceh->AceType != ACCESS_ALLOWED_ACE_TYPE
&& aceh->AceType != ACCESS_DENIED_ACE_TYPE)
continue;
ace = (ACCESS_ALLOWED_ACE*)aceh;
sid = (SID*)&ace->SidStart;
if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup))
continue;
if(EqualSid(sid, everyone))
;
else if(EqualSid(sid, osid))
;
else if(EqualPrefixSid(sid, ntignore))
continue; /* boring nt accounts */
else{
gsid = sid;
break;
}
}
if(gsid == NULL)
gsid = osid;
owner = sidtouser(srv, osid);
group = sidtouser(srv, gsid);
if(owner == 0 || group == 0)
goto bad;
/* no acl means full access */
if(acl == 0)
allow = 0777;
else
allow = 0;
deny = 0;
for(i = 0; i < size.AceCount; i++){
if(!GetAce(acl, i, &aceh))
continue;
if(aceh->AceFlags & INHERIT_ONLY_ACE)
continue;
if(aceh->AceType == ACCESS_ALLOWED_ACE_TYPE)
p = &allow;
else if(aceh->AceType == ACCESS_DENIED_ACE_TYPE)
p = &deny;
else
continue;
ace = (ACCESS_ALLOWED_ACE*)aceh;
sid = (SID*)&ace->SidStart;
if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup))
continue;
m = 0;
if(ace->Mask & FILE_EXECUTE)
m |= 1;
if(ace->Mask & FILE_WRITE_DATA)
m |= 2;
if(ace->Mask & FILE_READ_DATA)
m |= 4;
/*
if(ismember(srv, owner, sid))
*p |= (m << 6) & ~(allow|deny) & 0700;
if(ismember(srv, group, sid))
*p |= (m << 3) & ~(allow|deny) & 0070;
*/
if(EqualSid(osid, sid))
*p |= (m << 6) & ~(allow|deny) & 0700;
if(EqualSid(gsid, sid))
*p |= (m << 3) & ~(allow|deny) & 0070;
if(EqualSid(everyone, sid))
*p |= m & ~(allow|deny) & 0007;
}
dir->mode = allow&~deny;
dir->uid = win_wstr2utf(owner->name);
dir->gid = win_wstr2utf(group->name);
good:
if((char*)sd != sdrock)
free(sd);
free(sdrock);
free(srvrock);
return 0;
bad:
oserror();
dir->mode = 0;
if((char*)sd != sdrock)
free(sd);
free(sdrock);
free(srvrock);
return -1;
}
#define NOMODE (READ_CONTROL|FILE_READ_EA|FILE_READ_ATTRIBUTES)
#define RMODE (READ_CONTROL|SYNCHRONIZE\
|FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
#define XMODE (READ_CONTROL|SYNCHRONIZE\
|FILE_EXECUTE|FILE_READ_ATTRIBUTES)
#define WMODE (DELETE|READ_CONTROL|SYNCHRONIZE|WRITE_DAC|WRITE_OWNER\
|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA\
|FILE_DELETE_CHILD|FILE_WRITE_ATTRIBUTES)
static int
modetomask[] =
{
NOMODE,
XMODE,
WMODE,
WMODE|XMODE,
RMODE,
RMODE|XMODE,
RMODE|WMODE,
RMODE|WMODE|XMODE,
};
int
win_secwperm(Rune *file, Dir *dir, int isdir)
{
int m;
BOOL b;
ACL *dacl;
SID *osid;
ulong mode;
DWORD need;
User *ou, *gu;
ACE_HEADER *aceh;
SECURITY_DESCRIPTOR *sd;
SECURITY_INFORMATION si;
Rune *srv, srvrock[MAX_PATH], name[MAX_PATH];
char sdrock[SECURITY_DESCRIPTOR_MIN_LENGTH + MAX_SID];
char aclrock[sizeof(ACL)+4*(sizeof(ACCESS_ALLOWED_ACE)+2*MAX_SID)];
perminit(1);
dacl = 0;
if(!fsacls(file))
return 0;
srv = filesrv(file, srvrock);
win_utf2wstrn(name, nelem(name), dir->uid);
ou = nametouser(srv, name);
if(ou == 0)
return -1;
win_utf2wstrn(name, nelem(name), dir->gid);
gu = nametouser(srv, name);
if(gu == 0) {
if (strcmp(dir->gid, "unknown") != 0
&& strcmp(dir->gid, "deleted") != 0)
return -1;
gu = ou;
}
sd = (SECURITY_DESCRIPTOR*)sdrock;
need = 0;
osid = 0;
if(GetFileSecurity(file, OWNER_SECURITY_INFORMATION, sd, sizeof(sdrock), &need))
GetSecurityDescriptorOwner(sd, &osid, &b);
dacl = (ACL*)aclrock;
if(!InitializeAcl(dacl, sizeof(aclrock), ACL_REVISION2)){
oserror();
return -1;
}
mode = dir->mode;
if(ou == gu){
mode |= (mode >> 3) & 0070;
mode |= (mode << 3) & 0700;
}
m = modetomask[(mode>>6) & 7];
if(!AddAccessAllowedAce(dacl, ACL_REVISION2, m, ou->sid)){
oserror();
return -1;
}
if(isdir && !AddAccessAllowedAce(dacl, ACL_REVISION2, m, creatorowner)){
oserror();
return -1;
}
m = modetomask[(mode>>3) & 7];
if(!AddAccessAllowedAce(dacl, ACL_REVISION2, m, gu->sid)){
oserror();
return -1;
}
m = modetomask[(mode>>0) & 7];
if(!AddAccessAllowedAce(dacl, ACL_REVISION2, m, everyone)){
oserror();
return -1;
}
if(isdir) {
/* hack to add inherit flags */
if(GetAce(dacl, 1, &aceh))
aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE;
if(GetAce(dacl, 2, &aceh))
aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE;
if(GetAce(dacl, 3, &aceh))
aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE;
}
if(!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION)){
oserror();
return -1;
}
if(!SetSecurityDescriptorDacl(sd, 1, dacl, 0)){
oserror();
return -1;
}
si = DACL_SECURITY_INFORMATION;
if(osid == NULL || !EqualSid(osid, ou->sid)){
si |= OWNER_SECURITY_INFORMATION;
if(!SetSecurityDescriptorOwner(sd, ou->sid, 0)){
oserror();
return -1;
}
}
if(!SetFileSecurity(file, si, sd)){
/* two steps will sometimes work where one will not */
if((si & OWNER_SECURITY_INFORMATION)
&& !SetFileSecurity(file, OWNER_SECURITY_INFORMATION, sd)) {
oserror();
return -1;
}
if(!SetFileSecurity(file, DACL_SECURITY_INFORMATION, sd)) {
oserror();
return -1;
}
}
return 0;
}
static User*
sidtouser(Rune *srv, SID *s)
{
SID_NAME_USE type;
WCHAR aname[100], dname[100];
DWORD naname, ndname;
User *u;
qlock(&users.lk);
for(u = users.u; u != 0; u = u->next)
if(EqualSid(s, u->sid))
break;
qunlock(&users.lk);
if(u != 0)
return u;
naname = sizeof(aname);
ndname = sizeof(dname);
if(!LookupAccountSid(srv, s, aname, &naname, dname, &ndname, &type)) {
oserror();
return 0;
}
return mkuser(s, type, aname, dname);
}
static User*
domnametouser(Rune *srv, Rune *name, Rune *dom)
{
User *u;
qlock(&users.lk);
for(u = users.u; u != 0; u = u->next)
if(runestrcmp(name, u->name) == 0 && runestrcmp(dom, u->dom) == 0)
break;
qunlock(&users.lk);
if(u == 0)
u = nametouser(srv, name);
return u;
}
static User*
nametouser(Rune *srv, Rune *name)
{
char sidrock[MAX_SID];
SID *sid;
SID_NAME_USE type;
Rune dom[MAX_PATH];
DWORD nsid, ndom;
sid = (SID*)sidrock;
nsid = sizeof(sidrock);
ndom = sizeof(dom);
if(!LookupAccountName(srv, name, sid, &nsid, dom, &ndom, &type)){
oserror();
return NULL;
}
return mkuser(sid, type, name, dom);
}
static User*
mkuser(SID *sid, int type, Rune *name, Rune *dom)
{
User *u;
qlock(&users.lk);
for(u = users.u; u != 0; u = u->next){
if(EqualSid(sid, u->sid)){
qunlock(&users.lk);
return u;
}
}
switch(type) {
default:
break;
case SidTypeDeletedAccount:
name = L"deleted";
break;
case SidTypeInvalid:
name = L"invalid";
break;
case SidTypeUnknown:
name = L"unknown";
break;
}
u = mallocz(sizeof(User), 1);
if(u == 0){
qunlock(&users.lk);
return 0;
}
u->next = 0;
u->group = 0;
u->sid = dupsid(sid);
u->type = type;
u->name = 0;
if(name != 0)
u->name = runestrdup(name);
u->dom = 0;
if(dom != 0)
u->dom = runestrdup(dom);
u->next = users.u;
users.u = u;
qunlock(&users.lk);
return u;
}
static int
ismember(Rune *srv, User *u, SID *gsid)
{
User *g;
Gmem *grps;
if(EqualSid(u->sid, gsid))
return 1;
if(EqualSid(gsid, everyone))
return 1;
g = sidtouser(srv, gsid);
if(g == 0)
return 0;
qlock(&u->lk);
addgroups(u);
for(grps = u->group; grps != 0; grps = grps->next){
if(EqualSid(grps->user->sid, gsid)){
qunlock(&u->lk);
return 1;
}
}
qunlock(&u->lk);
return 0;
}
static void
addgroups(User *u)
{
LOCALGROUP_USERS_INFO_0 *loc;
GROUP_USERS_INFO_0 *grp;
DWORD i, n, rem;
User *gu;
Gmem *g;
Rune *srv, srvrock[MAX_PATH];
// assert(holdqlock(&u->lk));
if(u->gotgroup)
return;
u->gotgroup = 1;
rem = 1;
n = 0;
srv = domsrv(u->dom, srvrock);
while(rem != n){
i = net.UserGetGroups(srv, u->name, 0,
(BYTE**)&grp, 1024, &n, &rem);
if(i != NERR_Success && i != ERROR_MORE_DATA)
break;
for(i = 0; i < n; i++){
gu = domnametouser(srv, grp[i].grui0_name, u->dom);
if(gu == 0)
continue;
g = mallocz(sizeof(Gmem), 1);
g->user = gu;
g->next = u->group;
u->group = g;
}
net.ApiBufferFree(grp);
}
rem = 1;
n = 0;
while(rem != n){
i = net.UserGetLocalGroups(srv, u->name, 0, LG_INCLUDE_INDIRECT,
(BYTE**)&loc, 1024, &n, &rem);
if(i != NERR_Success && i != ERROR_MORE_DATA)
break;
for(i = 0; i < n; i++){
gu = domnametouser(srv, loc[i].lgrui0_name, u->dom);
if(gu == NULL)
continue;
g = mallocz(sizeof(Gmem), 1);
g->user = gu;
g->next = u->group;
u->group = g;
}
net.ApiBufferFree(loc);
}
}
static SID*
dupsid(SID *sid)
{
SID *nsid;
int n;
n = GetLengthSid(sid);
nsid = mallocz(n, 1);
if(!CopySid(n, nsid, sid))
oserror();
return nsid;
}
static Rune*
filesrv(Rune *file, Rune srv[MAX_PATH])
{
Rune *p;
int n;
Rune vol[3], uni[MAX_PATH];
/* assume file is a fully qualified name - X: or \\server */
if(file[1] == ':') {
vol[0] = file[0];
vol[1] = file[1];
vol[2] = 0;
if(GetDriveType(vol) != DRIVE_REMOTE)
return 0;
n = sizeof(uni);
if(WNetGetUniversalName(vol, UNIVERSAL_NAME_INFO_LEVEL, uni, &n) != NO_ERROR)
return 0;
file = ((UNIVERSAL_NAME_INFO*)uni)->lpUniversalName;
}
p = runestrchr(file+2, '\\');
if(p == 0)
n = runestrlen(file+2);
else
n = p-(file+2);
if(n >= MAX_PATH)
n = MAX_PATH-1;
memcpy(srv, file+2, n*sizeof(Rune));
srv[n] = 0;
return srv;
}
static int
fsacls(Rune *file)
{
Rune *p;
DWORD flags;
Rune path[MAX_PATH];
/* assume file is a fully qualified name - X: or \\server */
if(file[1] == ':') {
path[0] = file[0];
path[1] = file[1];
path[2] = '\\';
path[3] = 0;
} else {
runestrcpy(path, file);
p = runestrchr(path+2, '\\');
if(p == 0)
return 0;
p = runestrchr(p+1, '\\');
if(p == 0)
runestrcat(path, L"\\");
else
p[1] = 0;
}
if(!GetVolumeInformation(path, NULL, 0, NULL, NULL, &flags, NULL, 0))
return 0;
return flags & FS_PERSISTENT_ACLS;
}
static Rune*
domsrv(Rune *dom, Rune srv[MAX_PATH])
{
Rune *psrv;
int n, r;
if(dom[0] == 0)
return 0;
r = net.GetAnyDCName(NULL, dom, (LPBYTE*)&psrv);
if(r == NERR_Success) {
n = runestrlen(psrv);
if(n >= MAX_PATH)
n = MAX_PATH-1;
memcpy(srv, psrv, n*sizeof(Rune));
srv[n] = 0;
net.ApiBufferFree(psrv);
return srv;
}
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1