From eea4e4820871fcc7c39392606e8ac07620ebe49b Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Fri, 20 Jul 2007 14:29:43 +0000 Subject: [PATCH] * fhandler.cc (fhandler_base::fhaccess): Accommodate interface changes of access control functions throughout. * fhandler_disk_file.cc: Ditto. * fhandler_registry.cc: Ditto. * sec_acl.cc: Drop unnecessary includes. (setacl): Take path_conv instead of file name as parameter. Accommodate interface changes of access control functions. (getacl): Ditto. * sec_auth.cc: New file, taking over all authentication related functions from security.cc. * sec_helper.cc: Drop unnecessary includes. * security.cc: Ditto. Move all authentication related functions to sec_auth.cc. (ALL_SECURITY_INFORMATION): New define. Use throughout. (set_file_sd): New function, replacing read_sd and the file related part of get_nt_object_security. (get_reg_sd): Rename from get_reg_security. Drop type parameter. (get_reg_attribute): New function, replacing the registry related part of get_nt_object_security. (get_file_attribute): Take path_conv instead of file name as parameter. Use new get_file_sd call. (set_file_attribute): Ditto plus new set_file_sd. Drop unnecessary implementation without uid/gid parameters. (check_file_access): Take path_conv instead of file name as parameter. Use new get_file_sd call. (check_registry_access): Use new get_reg_sd call. * security.h: Accommodate above interface changes. --- winsup/cygwin/ChangeLog | 33 + winsup/cygwin/Makefile.in | 12 +- winsup/cygwin/fhandler.cc | 2 +- winsup/cygwin/fhandler_disk_file.cc | 21 +- winsup/cygwin/fhandler_registry.cc | 4 +- winsup/cygwin/sec_acl.cc | 35 +- winsup/cygwin/sec_auth.cc | 1119 +++++++++++++++++++++ winsup/cygwin/sec_helper.cc | 10 - winsup/cygwin/security.cc | 1409 ++------------------------- winsup/cygwin/security.h | 23 +- 10 files changed, 1287 insertions(+), 1381 deletions(-) create mode 100644 winsup/cygwin/sec_auth.cc diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 47f009c7e..72cf45ceb 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,36 @@ +2007-07-20 Corinna Vinschen + + * Makefile.in (DLL_OFILES): Add sec_auth.o. + + + * fhandler.cc (fhandler_base::fhaccess): Accommodate interface changes + of access control functions throughout. + * fhandler_disk_file.cc: Ditto. + * fhandler_registry.cc: Ditto. + * sec_acl.cc: Drop unnecessary includes. + (setacl): Take path_conv instead of file name as parameter. + Accommodate interface changes of access control functions. + (getacl): Ditto. + * sec_auth.cc: New file, taking over all authentication related + functions from security.cc. + * sec_helper.cc: Drop unnecessary includes. + * security.cc: Ditto. Move all authentication related functions to + sec_auth.cc. + (ALL_SECURITY_INFORMATION): New define. Use throughout. + (set_file_sd): New function, replacing read_sd and the file related + part of get_nt_object_security. + (get_reg_sd): Rename from get_reg_security. Drop type parameter. + (get_reg_attribute): New function, replacing the registry related part + of get_nt_object_security. + (get_file_attribute): Take path_conv instead of file name as parameter. + Use new get_file_sd call. + (set_file_attribute): Ditto plus new set_file_sd. Drop unnecessary + implementation without uid/gid parameters. + (check_file_access): Take path_conv instead of file name as parameter. + Use new get_file_sd call. + (check_registry_access): Use new get_reg_sd call. + * security.h: Accommodate above interface changes. + 2007-07-19 Corinna Vinschen * security.cc (set_nt_attribute): Remove. diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in index eb0c85814..5251ff9b8 100644 --- a/winsup/cygwin/Makefile.in +++ b/winsup/cygwin/Makefile.in @@ -139,12 +139,12 @@ DLL_OFILES:=assert.o autoload.o bsdlib.o ctype.o cxx.o cygheap.o cygthread.o \ minires.o miscfuncs.o mktemp.o mmap.o msg.o net.o netdb.o nftw.o \ passwd.o path.o pinfo.o pipe.o poll.o posix_ipc.o pthread.o random.o \ regcomp.o regerror.o regexec.o regfree.o registry.o resource.o rexec.o \ - rcmd.o scandir.o sched.o sec_acl.o sec_helper.o security.o select.o \ - sem.o shared.o shm.o sigfe.o signal.o sigproc.o smallprint.o spawn.o \ - strace.o strptime.o strsep.o strsig.o sync.o syscalls.o sysconf.o \ - syslog.o termios.o thread.o timelocal.o timer.o times.o tty.o uinfo.o \ - uname.o v8_regexp.o v8_regerror.o v8_regsub.o wait.o wincap.o window.o \ - winf.o xsique.o \ + rcmd.o scandir.o sched.o sec_acl.o sec_auth.o sec_helper.o security.o \ + select.o sem.o shared.o shm.o sigfe.o signal.o sigproc.o smallprint.o \ + spawn.o strace.o strptime.o strsep.o strsig.o sync.o syscalls.o \ + sysconf.o syslog.o termios.o thread.o timelocal.o timer.o times.o \ + tty.o uinfo.o uname.o v8_regexp.o v8_regerror.o v8_regsub.o wait.o \ + wincap.o window.o winf.o xsique.o \ $(EXTRA_DLL_OFILES) $(EXTRA_OFILES) $(MALLOC_OFILES) $(MT_SAFE_OBJECTS) GMON_OFILES:=gmon.o mcount.o profil.o diff --git a/winsup/cygwin/fhandler.cc b/winsup/cygwin/fhandler.cc index 960b37549..0ee0bbb8a 100644 --- a/winsup/cygwin/fhandler.cc +++ b/winsup/cygwin/fhandler.cc @@ -374,7 +374,7 @@ fhandler_base::fhaccess (int flags) goto eaccess_done; else if (has_acls () && allow_ntsec) { - res = check_file_access (get_win32_name (), flags); + res = check_file_access (pc, flags); goto done; } else if (get_device () == FH_REGISTRY && allow_ntsec && open (O_RDONLY, 0) diff --git a/winsup/cygwin/fhandler_disk_file.cc b/winsup/cygwin/fhandler_disk_file.cc index 5d37d04d5..59fcb4450 100644 --- a/winsup/cygwin/fhandler_disk_file.cc +++ b/winsup/cygwin/fhandler_disk_file.cc @@ -433,17 +433,15 @@ fhandler_base::fstat_helper (struct __stat64 *buf, buf->st_size = pc.get_symlink_length (); /* symlinks are everything for everyone! */ buf->st_mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO; - get_file_attribute (pc.has_acls (), get_io_handle (), get_win32_name (), - NULL, &buf->st_uid, &buf->st_gid); + get_file_attribute (get_io_handle (), pc, NULL, + &buf->st_uid, &buf->st_gid); goto done; } else if (pc.issocket ()) buf->st_mode = S_IFSOCK; - if (!get_file_attribute (pc.has_acls (), - is_fs_special () ? NULL: get_io_handle (), - get_win32_name (), &buf->st_mode, - &buf->st_uid, &buf->st_gid)) + if (!get_file_attribute (is_fs_special () ? NULL: get_io_handle (), pc, + &buf->st_mode, &buf->st_uid, &buf->st_gid)) { /* If read-only attribute is set, modify ntsec return value */ if (::has_attribute (dwFileAttributes, FILE_ATTRIBUTE_READONLY) @@ -660,7 +658,7 @@ fhandler_disk_file::fchmod (mode_t mode) { if (pc.isdir ()) mode |= S_IFDIR; - if (!set_file_attribute (pc.has_acls (), get_io_handle (), pc, + if (!set_file_attribute (get_io_handle (), pc, ILLEGAL_UID, ILLEGAL_GID, mode) && allow_ntsec) res = 0; @@ -706,7 +704,7 @@ fhandler_disk_file::fchown (__uid32_t uid, __gid32_t gid) mode_t attrib = 0; if (pc.isdir ()) attrib |= S_IFDIR; - int res = get_file_attribute (pc.has_acls (), get_io_handle (), pc, &attrib); + int res = get_file_attribute (get_io_handle (), pc, &attrib, NULL, NULL); if (!res) { /* Typical Windows default ACLs can contain permissions for one @@ -718,8 +716,7 @@ fhandler_disk_file::fchown (__uid32_t uid, __gid32_t gid) world to read the symlink and only the new owner to change it. */ if (pc.issymlink ()) attrib = S_IFLNK | STD_RBITS | STD_WBITS; - res = set_file_attribute (pc.has_acls (), get_io_handle (), pc, - uid, gid, attrib); + res = set_file_attribute (get_io_handle (), pc, uid, gid, attrib); } if (oret) close (); @@ -808,10 +805,10 @@ fhandler_disk_file::facl (int cmd, int nentries, __aclent32_t *aclbufp) if (!aclbufp) set_errno(EFAULT); else - res = getacl (get_io_handle (), pc, pc, nentries, aclbufp); + res = getacl (get_io_handle (), pc, nentries, aclbufp); break; case GETACLCNT: - res = getacl (get_io_handle (), pc, pc, 0, NULL); + res = getacl (get_io_handle (), pc, 0, NULL); break; default: set_errno (EINVAL); diff --git a/winsup/cygwin/fhandler_registry.cc b/winsup/cygwin/fhandler_registry.cc index 62cd8f454..23d88c82a 100644 --- a/winsup/cygwin/fhandler_registry.cc +++ b/winsup/cygwin/fhandler_registry.cc @@ -269,9 +269,7 @@ fhandler_registry::fstat (struct __stat64 *buf) } __uid32_t uid; __gid32_t gid; - if (get_object_attribute - ((HANDLE) hKey, SE_REGISTRY_KEY, &buf->st_mode, &uid, - &gid) == 0) + if (get_reg_attribute (hKey, &buf->st_mode, &uid, &gid) == 0) { buf->st_uid = uid; buf->st_gid = gid; diff --git a/winsup/cygwin/sec_acl.cc b/winsup/cygwin/sec_acl.cc index 87c6428fe..54d443b17 100644 --- a/winsup/cygwin/sec_acl.cc +++ b/winsup/cygwin/sec_acl.cc @@ -11,17 +11,9 @@ Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ #include "winsup.h" -#include -#include -#include #include -#include -#include -#include #include #include -#include -#include #include "cygerrno.h" #include "security.h" #include "path.h" @@ -44,17 +36,13 @@ searchace (__aclent32_t *aclp, int nentries, int type, __uid32_t id = ILLEGAL_UI } int -setacl (HANDLE handle, const char *file, int nentries, __aclent32_t *aclbufp, +setacl (HANDLE handle, path_conv &pc, int nentries, __aclent32_t *aclbufp, bool &writable) { security_descriptor sd_ret; - if ((!handle || get_nt_object_security (handle, SE_FILE_OBJECT, sd_ret)) - && read_sd (file, sd_ret) <= 0) - { - debug_printf ("read_sd %E"); - return -1; - } + if (get_file_sd (handle, pc, sd_ret)) + return -1; BOOL dummy; @@ -227,7 +215,7 @@ setacl (HANDLE handle, const char *file, int nentries, __aclent32_t *aclbufp, return -1; } debug_printf ("Created SD-Size: %d", sd_ret.size ()); - return write_sd (handle, file, sd_ret); + return set_file_sd (handle, pc, sd_ret); } /* Temporary access denied bits */ @@ -262,17 +250,12 @@ getace (__aclent32_t &acl, int type, int id, DWORD win_ace_mask, } int -getacl (HANDLE handle, const char *file, DWORD attr, int nentries, - __aclent32_t *aclbufp) +getacl (HANDLE handle, path_conv &pc, int nentries, __aclent32_t *aclbufp) { security_descriptor sd; - if ((!handle || get_nt_object_security (handle, SE_FILE_OBJECT, sd)) - && read_sd (file, sd) <= 0) - { - debug_printf ("read_sd %E"); - return -1; - } + if (get_file_sd (handle, pc, sd)) + return -1; cygpsid owner_sid; cygpsid group_sid; @@ -372,7 +355,7 @@ getacl (HANDLE handle, const char *file, DWORD attr, int nentries, getace (lacl[pos], type, id, ace->Mask, ace->Header.AceType); } if ((ace->Header.AceFlags & SUB_CONTAINERS_AND_OBJECTS_INHERIT) - && (attr & FILE_ATTRIBUTE_DIRECTORY)) + && pc.isdir ()) { if (type == USER_OBJ) type = USER; @@ -408,7 +391,7 @@ getacl (HANDLE handle, const char *file, DWORD attr, int nentries, aclbufp[i].a_perm &= ~(DENY_R | DENY_W | DENY_X); aclsort32 (pos, 0, aclbufp); } - syscall_printf ("%d = getacl (%s)", pos, file); + syscall_printf ("%d = getacl (%s)", pos, pc.get_win32 ()); return pos; } diff --git a/winsup/cygwin/sec_auth.cc b/winsup/cygwin/sec_auth.cc new file mode 100644 index 000000000..7cf2cb43f --- /dev/null +++ b/winsup/cygwin/sec_auth.cc @@ -0,0 +1,1119 @@ +/* sec_auth.cc: NT authentication functions + + Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, + 2006, 2007 Red Hat, Inc. + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#include "winsup.h" +#include +#include +#include +#include +#include "cygerrno.h" +#include "security.h" +#include "path.h" +#include "fhandler.h" +#include "dtable.h" +#include "pinfo.h" +#include "cygheap.h" +#include "ntdll.h" +#include "lm.h" +#include "pwdgrp.h" +#include "cyglsa.h" +#include + +extern "C" void +cygwin_set_impersonation_token (const HANDLE hToken) +{ + debug_printf ("set_impersonation_token (%d)", hToken); + cygheap->user.external_token = hToken == INVALID_HANDLE_VALUE ? NO_IMPERSONATION : hToken; +} + +void +extract_nt_dom_user (const struct passwd *pw, char *domain, char *user) +{ + char *d, *u, *c; + + domain[0] = 0; + strlcpy (user, pw->pw_name, UNLEN + 1); + debug_printf ("pw_gecos %x (%s)", pw->pw_gecos, pw->pw_gecos); + + if ((d = strstr (pw->pw_gecos, "U-")) != NULL && + (d == pw->pw_gecos || d[-1] == ',')) + { + c = strechr (d + 2, ','); + if ((u = strechr (d + 2, '\\')) >= c) + u = d + 1; + else if (u - d <= INTERNET_MAX_HOST_NAME_LENGTH + 2) + strlcpy (domain, d + 2, u - d - 1); + if (c - u <= UNLEN + 1) + strlcpy (user, u + 1, c - u); + } + if (domain[0]) + return; + + cygsid psid; + DWORD ulen = UNLEN + 1; + DWORD dlen = INTERNET_MAX_HOST_NAME_LENGTH + 1; + SID_NAME_USE use; + if (psid.getfrompw (pw)) + LookupAccountSid (NULL, psid, user, &ulen, domain, &dlen, &use); +} + +extern "C" HANDLE +cygwin_logon_user (const struct passwd *pw, const char *password) +{ + if (!pw) + { + set_errno (EINVAL); + return INVALID_HANDLE_VALUE; + } + + char nt_domain[INTERNET_MAX_HOST_NAME_LENGTH + 1]; + char nt_user[UNLEN + 1]; + HANDLE hToken; + + extract_nt_dom_user (pw, nt_domain, nt_user); + debug_printf ("LogonUserA (%s, %s, %s, ...)", nt_user, nt_domain, password); + /* CV 2005-06-08: LogonUser should run under the primary process token, + otherwise it returns with ERROR_ACCESS_DENIED on W2K. Don't ask me why. */ + RevertToSelf (); + if (!LogonUserA (nt_user, *nt_domain ? nt_domain : NULL, (char *) password, + LOGON32_LOGON_INTERACTIVE, + LOGON32_PROVIDER_DEFAULT, + &hToken)) + { + __seterrno (); + hToken = INVALID_HANDLE_VALUE; + } + else if (!SetHandleInformation (hToken, + HANDLE_FLAG_INHERIT, + HANDLE_FLAG_INHERIT)) + { + __seterrno (); + CloseHandle (hToken); + hToken = INVALID_HANDLE_VALUE; + } + cygheap->user.reimpersonate (); + debug_printf ("%d = logon_user(%s,...)", hToken, pw->pw_name); + return hToken; +} + +static void +str2lsa (LSA_STRING &tgt, const char *srcstr) +{ + tgt.Length = strlen (srcstr); + tgt.MaximumLength = tgt.Length + 1; + tgt.Buffer = (PCHAR) srcstr; +} + +static void +str2buf2lsa (LSA_STRING &tgt, char *buf, const char *srcstr) +{ + tgt.Length = strlen (srcstr); + tgt.MaximumLength = tgt.Length + 1; + tgt.Buffer = (PCHAR) buf; + memcpy (buf, srcstr, tgt.MaximumLength); +} + +/* The dimension of buf is assumed to be at least strlen(srcstr) + 1, + The result will be shorter if the input has multibyte chars */ +void +str2buf2uni (UNICODE_STRING &tgt, WCHAR *buf, const char *srcstr) +{ + tgt.Buffer = (PWCHAR) buf; + tgt.MaximumLength = (strlen (srcstr) + 1) * sizeof (WCHAR); + tgt.Length = sys_mbstowcs (buf, srcstr, tgt.MaximumLength / sizeof (WCHAR)) + * sizeof (WCHAR); + if (tgt.Length) + tgt.Length -= sizeof (WCHAR); +} + +void +str2uni_cat (UNICODE_STRING &tgt, const char *srcstr) +{ + int len = sys_mbstowcs (tgt.Buffer + tgt.Length / sizeof (WCHAR), srcstr, + (tgt.MaximumLength - tgt.Length) / sizeof (WCHAR)); + if (len) + tgt.Length += (len - 1) * sizeof (WCHAR); + else + tgt.Length = tgt.MaximumLength = 0; +} + +static LSA_HANDLE +open_local_policy () +{ + LSA_OBJECT_ATTRIBUTES oa = { 0, 0, 0, 0, 0, 0 }; + LSA_HANDLE lsa = INVALID_HANDLE_VALUE; + + NTSTATUS ret = LsaOpenPolicy (NULL, &oa, POLICY_EXECUTE, &lsa); + if (ret != STATUS_SUCCESS) + __seterrno_from_win_error (LsaNtStatusToWinError (ret)); + return lsa; +} + +static void +close_local_policy (LSA_HANDLE &lsa) +{ + if (lsa != INVALID_HANDLE_VALUE) + LsaClose (lsa); + lsa = INVALID_HANDLE_VALUE; +} + +/* CV, 2006-07-06: Missing in w32api. */ +extern "C" DWORD WINAPI DsGetDcNameA (LPCSTR, LPCSTR, GUID *, LPCSTR, ULONG, + PDOMAIN_CONTROLLER_INFOA *); +#define DS_FORCE_REDISCOVERY 1 + +bool +get_logon_server (const char *domain, char *server, WCHAR *wserver, + bool rediscovery) +{ + DWORD dret; + PDOMAIN_CONTROLLER_INFOA pci; + WCHAR *buf; + DWORD size = INTERNET_MAX_HOST_NAME_LENGTH + 1; + WCHAR wdomain[size]; + + /* Empty domain is interpreted as local system */ + if ((GetComputerName (server + 2, &size)) && + (strcasematch (domain, server + 2) || !domain[0])) + { + server[0] = server[1] = '\\'; + if (wserver) + sys_mbstowcs (wserver, server, INTERNET_MAX_HOST_NAME_LENGTH + 1); + return true; + } + + /* Try to get any available domain controller for this domain */ + dret = DsGetDcNameA (NULL, domain, NULL, NULL, + rediscovery ? DS_FORCE_REDISCOVERY : 0, &pci); + if (dret == ERROR_SUCCESS) + { + strcpy (server, pci->DomainControllerName); + sys_mbstowcs (wserver, server, INTERNET_MAX_HOST_NAME_LENGTH + 1); + NetApiBufferFree (pci); + debug_printf ("DC: rediscovery: %d, server: %s", rediscovery, server); + return true; + } + else if (dret == ERROR_PROC_NOT_FOUND) + { + /* NT4 w/o DSClient */ + sys_mbstowcs (wdomain, domain, INTERNET_MAX_HOST_NAME_LENGTH + 1); + if (rediscovery) + dret = NetGetAnyDCName (NULL, wdomain, (LPBYTE *) &buf); + else + dret = NetGetDCName (NULL, wdomain, (LPBYTE *) &buf); + if (dret == NERR_Success) + { + sys_wcstombs (server, INTERNET_MAX_HOST_NAME_LENGTH + 1, buf); + if (wserver) + for (WCHAR *ptr1 = buf; (*wserver++ = *ptr1++);) + ; + NetApiBufferFree (buf); + debug_printf ("NT: rediscovery: %d, server: %s", rediscovery, server); + return true; + } + } + __seterrno_from_win_error (dret); + return false; +} + +static bool +get_user_groups (WCHAR *wlogonserver, cygsidlist &grp_list, char *user, + char *domain) +{ + char dgroup[INTERNET_MAX_HOST_NAME_LENGTH + GNLEN + 2]; + WCHAR wuser[UNLEN + 1]; + sys_mbstowcs (wuser, user, UNLEN + 1); + LPGROUP_USERS_INFO_0 buf; + DWORD cnt, tot, len; + NET_API_STATUS ret; + + /* Look only on logonserver */ + ret = NetUserGetGroups (wlogonserver, wuser, 0, (LPBYTE *) &buf, + MAX_PREFERRED_LENGTH, &cnt, &tot); + if (ret) + { + __seterrno_from_win_error (ret); + /* It's no error when the user name can't be found. */ + return ret == NERR_UserNotFound; + } + + len = strlen (domain); + strcpy (dgroup, domain); + dgroup[len++] = '\\'; + + for (DWORD i = 0; i < cnt; ++i) + { + cygsid gsid; + DWORD glen = MAX_SID_LEN; + char domain[INTERNET_MAX_HOST_NAME_LENGTH + 1]; + DWORD dlen = sizeof (domain); + SID_NAME_USE use = SidTypeInvalid; + + sys_wcstombs (dgroup + len, GNLEN + 1, buf[i].grui0_name); + if (!LookupAccountName (NULL, dgroup, gsid, &glen, domain, &dlen, &use)) + debug_printf ("LookupAccountName(%s), %E", dgroup); + else if (legal_sid_type (use)) + grp_list += gsid; + else + debug_printf ("Global group %s invalid. Domain: %s Use: %d", + dgroup, domain, use); + } + + NetApiBufferFree (buf); + return true; +} + +static bool +is_group_member (WCHAR *wgroup, PSID pusersid, cygsidlist &grp_list) +{ + LPLOCALGROUP_MEMBERS_INFO_1 buf; + DWORD cnt, tot; + NET_API_STATUS ret; + + /* Members can be users or global groups */ + ret = NetLocalGroupGetMembers (NULL, wgroup, 1, (LPBYTE *) &buf, + MAX_PREFERRED_LENGTH, &cnt, &tot, NULL); + if (ret) + return false; + + bool retval = true; + for (DWORD bidx = 0; bidx < cnt; ++bidx) + if (EqualSid (pusersid, buf[bidx].lgrmi1_sid)) + goto done; + else + { + /* The extra test for the group being a global group or a well-known + group is necessary, since apparently also aliases (for instance + Administrators or Users) can be members of local groups, even + though MSDN states otherwise. The GUI refuses to put aliases into + local groups, but the CLI interface allows it. However, a normal + logon token does not contain groups, in which the user is only + indirectly a member by being a member of an alias in this group. + So we also should not put them into the token group list. + Note: Allowing those groups in our group list renders external + tokens invalid, so that it becomes impossible to logon with + password and valid logon token. */ + for (int glidx = 0; glidx < grp_list.count (); ++glidx) + if ((buf[bidx].lgrmi1_sidusage == SidTypeGroup + || buf[bidx].lgrmi1_sidusage == SidTypeWellKnownGroup) + && EqualSid (grp_list.sids[glidx], buf[bidx].lgrmi1_sid)) + goto done; + } + + retval = false; + done: + NetApiBufferFree (buf); + return retval; +} + +static bool +get_user_local_groups (cygsidlist &grp_list, PSID pusersid) +{ + LPLOCALGROUP_INFO_0 buf; + DWORD cnt, tot; + NET_API_STATUS ret; + + ret = NetLocalGroupEnum (NULL, 0, (LPBYTE *) &buf, + MAX_PREFERRED_LENGTH, &cnt, &tot, NULL); + if (ret) + { + __seterrno_from_win_error (ret); + return false; + } + + char bgroup[INTERNET_MAX_HOST_NAME_LENGTH + GNLEN + 2]; + char lgroup[INTERNET_MAX_HOST_NAME_LENGTH + GNLEN + 2]; + DWORD blen, llen; + SID_NAME_USE use; + + blen = llen = INTERNET_MAX_HOST_NAME_LENGTH + 1; + if (!LookupAccountSid (NULL, well_known_admins_sid, lgroup, &llen, bgroup, &blen, &use) + || !GetComputerNameA (lgroup, &(llen = INTERNET_MAX_HOST_NAME_LENGTH + 1))) + { + __seterrno (); + return false; + } + bgroup[blen++] = lgroup[llen++] = '\\'; + + for (DWORD i = 0; i < cnt; ++i) + if (is_group_member (buf[i].lgrpi0_name, pusersid, grp_list)) + { + cygsid gsid; + DWORD glen = MAX_SID_LEN; + char domain[INTERNET_MAX_HOST_NAME_LENGTH + 1]; + DWORD dlen = sizeof (domain); + + use = SidTypeInvalid; + sys_wcstombs (bgroup + blen, GNLEN + 1, buf[i].lgrpi0_name); + if (!LookupAccountName (NULL, bgroup, gsid, &glen, domain, &dlen, &use)) + { + if (GetLastError () != ERROR_NONE_MAPPED) + debug_printf ("LookupAccountName(%s), %E", bgroup); + strcpy (lgroup + llen, bgroup + blen); + if (!LookupAccountName (NULL, lgroup, gsid, &glen, + domain, &dlen, &use)) + debug_printf ("LookupAccountName(%s), %E", lgroup); + } + if (!legal_sid_type (use)) + debug_printf ("Rejecting local %s. use: %d", bgroup + blen, use); + grp_list *= gsid; + } + NetApiBufferFree (buf); + return true; +} + +static bool +sid_in_token_groups (PTOKEN_GROUPS grps, cygpsid sid) +{ + if (!grps) + return false; + for (DWORD i = 0; i < grps->GroupCount; ++i) + if (sid == grps->Groups[i].Sid) + return true; + return false; +} + +static void +get_unix_group_sidlist (struct passwd *pw, cygsidlist &grp_list) +{ + struct __group32 *gr; + cygsid gsid; + + for (int gidx = 0; (gr = internal_getgrent (gidx)); ++gidx) + { + if (gr->gr_gid == (__gid32_t) pw->pw_gid) + goto found; + else if (gr->gr_mem) + for (int gi = 0; gr->gr_mem[gi]; ++gi) + if (strcasematch (pw->pw_name, gr->gr_mem[gi])) + goto found; + continue; + found: + if (gsid.getfromgr (gr)) + grp_list += gsid; + + } +} + +static void +get_token_group_sidlist (cygsidlist &grp_list, PTOKEN_GROUPS my_grps, + LUID auth_luid, int &auth_pos) +{ + auth_pos = -1; + if (my_grps) + { + grp_list += well_known_local_sid; + if (sid_in_token_groups (my_grps, well_known_dialup_sid)) + grp_list *= well_known_dialup_sid; + if (sid_in_token_groups (my_grps, well_known_network_sid)) + grp_list *= well_known_network_sid; + if (sid_in_token_groups (my_grps, well_known_batch_sid)) + grp_list *= well_known_batch_sid; + grp_list *= well_known_interactive_sid; + if (sid_in_token_groups (my_grps, well_known_service_sid)) + grp_list *= well_known_service_sid; + if (sid_in_token_groups (my_grps, well_known_this_org_sid)) + grp_list *= well_known_this_org_sid; + } + else + { + grp_list += well_known_local_sid; + grp_list *= well_known_interactive_sid; + } + if (get_ll (auth_luid) != 999LL) /* != SYSTEM_LUID */ + { + for (DWORD i = 0; i < my_grps->GroupCount; ++i) + if (my_grps->Groups[i].Attributes & SE_GROUP_LOGON_ID) + { + grp_list += my_grps->Groups[i].Sid; + auth_pos = grp_list.count () - 1; + break; + } + } +} + +bool +get_server_groups (cygsidlist &grp_list, PSID usersid, struct passwd *pw) +{ + char user[UNLEN + 1]; + char domain[INTERNET_MAX_HOST_NAME_LENGTH + 1]; + WCHAR wserver[INTERNET_MAX_HOST_NAME_LENGTH + 3]; + char server[INTERNET_MAX_HOST_NAME_LENGTH + 3]; + + if (well_known_system_sid == usersid) + { + grp_list *= well_known_admins_sid; + get_unix_group_sidlist (pw, grp_list); + return true; + } + + grp_list *= well_known_world_sid; + grp_list *= well_known_authenticated_users_sid; + extract_nt_dom_user (pw, domain, user); + if (get_logon_server (domain, server, wserver, false) + && !get_user_groups (wserver, grp_list, user, domain) + && get_logon_server (domain, server, wserver, true)) + get_user_groups (wserver, grp_list, user, domain); + if (get_user_local_groups (grp_list, usersid)) + { + get_unix_group_sidlist (pw, grp_list); + return true; + } + return false; +} + +static bool +get_initgroups_sidlist (cygsidlist &grp_list, + PSID usersid, PSID pgrpsid, struct passwd *pw, + PTOKEN_GROUPS my_grps, LUID auth_luid, int &auth_pos) +{ + grp_list *= well_known_world_sid; + grp_list *= well_known_authenticated_users_sid; + if (well_known_system_sid == usersid) + auth_pos = -1; + else + get_token_group_sidlist (grp_list, my_grps, auth_luid, auth_pos); + if (!get_server_groups (grp_list, usersid, pw)) + return false; + + /* special_pgrp true if pgrpsid is not in normal groups */ + grp_list += pgrpsid; + return true; +} + +static void +get_setgroups_sidlist (cygsidlist &tmp_list, PSID usersid, struct passwd *pw, + PTOKEN_GROUPS my_grps, user_groups &groups, + LUID auth_luid, int &auth_pos) +{ + tmp_list *= well_known_world_sid; + tmp_list *= well_known_authenticated_users_sid; + get_token_group_sidlist (tmp_list, my_grps, auth_luid, auth_pos); + get_server_groups (tmp_list, usersid, pw); + for (int gidx = 0; gidx < groups.sgsids.count (); gidx++) + tmp_list += groups.sgsids.sids[gidx]; + tmp_list += groups.pgsid; +} + +static ULONG sys_privs[] = { + SE_CREATE_TOKEN_PRIVILEGE, + SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, + SE_LOCK_MEMORY_PRIVILEGE, + SE_INCREASE_QUOTA_PRIVILEGE, + SE_TCB_PRIVILEGE, + SE_SECURITY_PRIVILEGE, + SE_TAKE_OWNERSHIP_PRIVILEGE, + SE_LOAD_DRIVER_PRIVILEGE, + SE_SYSTEM_PROFILE_PRIVILEGE, /* Vista ONLY */ + SE_SYSTEMTIME_PRIVILEGE, + SE_PROF_SINGLE_PROCESS_PRIVILEGE, + SE_INC_BASE_PRIORITY_PRIVILEGE, + SE_CREATE_PAGEFILE_PRIVILEGE, + SE_CREATE_PERMANENT_PRIVILEGE, + SE_BACKUP_PRIVILEGE, + SE_RESTORE_PRIVILEGE, + SE_SHUTDOWN_PRIVILEGE, + SE_DEBUG_PRIVILEGE, + SE_AUDIT_PRIVILEGE, + SE_SYSTEM_ENVIRONMENT_PRIVILEGE, + SE_CHANGE_NOTIFY_PRIVILEGE, + SE_UNDOCK_PRIVILEGE, + SE_MANAGE_VOLUME_PRIVILEGE, + SE_IMPERSONATE_PRIVILEGE, + SE_CREATE_GLOBAL_PRIVILEGE, + SE_INCREASE_WORKING_SET_PRIVILEGE, + SE_TIME_ZONE_PRIVILEGE, + SE_CREATE_SYMBOLIC_LINK_PRIVILEGE +}; + +#define SYSTEM_PRIVILEGES_COUNT (sizeof sys_privs / sizeof *sys_privs) + +static PTOKEN_PRIVILEGES +get_system_priv_list (size_t &size) +{ + ULONG max_idx = 0; + while (max_idx < SYSTEM_PRIVILEGES_COUNT + && sys_privs[max_idx] != wincap.max_sys_priv ()) + ++max_idx; + if (max_idx >= SYSTEM_PRIVILEGES_COUNT) + api_fatal ("Coding error: wincap privilege %u doesn't exist in sys_privs", + wincap.max_sys_priv ()); + size = sizeof (ULONG) + (max_idx + 1) * sizeof (LUID_AND_ATTRIBUTES); + PTOKEN_PRIVILEGES privs = (PTOKEN_PRIVILEGES) malloc (size); + if (!privs) + { + debug_printf ("malloc (system_privs) failed."); + return NULL; + } + privs->PrivilegeCount = 0; + for (ULONG i = 0; i <= max_idx; ++i) + { + privs->Privileges[privs->PrivilegeCount].Luid.HighPart = 0L; + privs->Privileges[privs->PrivilegeCount].Luid.LowPart = sys_privs[i]; + privs->Privileges[privs->PrivilegeCount].Attributes = + SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_ENABLED_BY_DEFAULT; + ++privs->PrivilegeCount; + } + return privs; +} + +static PTOKEN_PRIVILEGES +get_priv_list (LSA_HANDLE lsa, cygsid &usersid, cygsidlist &grp_list, + size_t &size) +{ + PLSA_UNICODE_STRING privstrs; + ULONG cnt; + PTOKEN_PRIVILEGES privs = NULL; + NTSTATUS ret; + char buf[INTERNET_MAX_HOST_NAME_LENGTH + 1]; + + if (usersid == well_known_system_sid) + return get_system_priv_list (size); + + for (int grp = -1; grp < grp_list.count (); ++grp) + { + if (grp == -1) + { + if ((ret = LsaEnumerateAccountRights (lsa, usersid, &privstrs, + &cnt)) != STATUS_SUCCESS) + continue; + } + else if ((ret = LsaEnumerateAccountRights (lsa, grp_list.sids[grp], + &privstrs, &cnt)) + != STATUS_SUCCESS) + continue; + for (ULONG i = 0; i < cnt; ++i) + { + LUID priv; + PTOKEN_PRIVILEGES tmp; + DWORD tmp_count; + + sys_wcstombs (buf, sizeof (buf), + privstrs[i].Buffer, privstrs[i].Length / 2); + if (!privilege_luid (buf, &priv)) + continue; + + if (privs) + { + DWORD pcnt = privs->PrivilegeCount; + LUID_AND_ATTRIBUTES *p = privs->Privileges; + for (; pcnt > 0; --pcnt, ++p) + if (priv.HighPart == p->Luid.HighPart + && priv.LowPart == p->Luid.LowPart) + goto next_account_right; + } + + tmp_count = privs ? privs->PrivilegeCount : 0; + size = sizeof (DWORD) + + (tmp_count + 1) * sizeof (LUID_AND_ATTRIBUTES); + tmp = (PTOKEN_PRIVILEGES) realloc (privs, size); + if (!tmp) + { + if (privs) + free (privs); + LsaFreeMemory (privstrs); + debug_printf ("realloc (privs) failed."); + return NULL; + } + tmp->PrivilegeCount = tmp_count; + privs = tmp; + privs->Privileges[privs->PrivilegeCount].Luid = priv; + privs->Privileges[privs->PrivilegeCount].Attributes = + SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_ENABLED_BY_DEFAULT; + ++privs->PrivilegeCount; + + next_account_right: + ; + } + LsaFreeMemory (privstrs); + } + return privs; +} + +/* Accept a token if + - the requested usersid matches the TokenUser and + - if setgroups has been called: + the token groups that are listed in /etc/group match the union of + the requested primary and supplementary groups in gsids. + - else the (unknown) implicitly requested supplementary groups and those + in the token are the groups associated with the usersid. We assume + they match and verify only the primary groups. + The requested primary group must appear in the token. + The primary group in the token is a group associated with the usersid, + except if the token is internal and the group is in the token SD + (see create_token). In that latter case that group must match the + requested primary group. */ +bool +verify_token (HANDLE token, cygsid &usersid, user_groups &groups, bool *pintern) +{ + DWORD size; + bool intern = false; + + if (pintern) + { + TOKEN_SOURCE ts; + if (!GetTokenInformation (token, TokenSource, + &ts, sizeof ts, &size)) + debug_printf ("GetTokenInformation(), %E"); + else + *pintern = intern = !memcmp (ts.SourceName, "Cygwin.1", 8); + } + /* Verify usersid */ + cygsid tok_usersid = NO_SID; + if (!GetTokenInformation (token, TokenUser, + &tok_usersid, sizeof tok_usersid, &size)) + debug_printf ("GetTokenInformation(), %E"); + if (usersid != tok_usersid) + return false; + + /* For an internal token, if setgroups was not called and if the sd group + is not well_known_null_sid, it must match pgrpsid */ + if (intern && !groups.issetgroups ()) + { + const DWORD sd_buf_siz = MAX_SID_LEN + sizeof (SECURITY_DESCRIPTOR); + PSECURITY_DESCRIPTOR sd_buf = (PSECURITY_DESCRIPTOR) alloca (sd_buf_siz); + cygpsid gsid (NO_SID); + if (!GetKernelObjectSecurity (token, GROUP_SECURITY_INFORMATION, + sd_buf, sd_buf_siz, &size)) + debug_printf ("GetKernelObjectSecurity(), %E"); + else if (!GetSecurityDescriptorGroup (sd_buf, (PSID *) &gsid, + (BOOL *) &size)) + debug_printf ("GetSecurityDescriptorGroup(), %E"); + if (well_known_null_sid != gsid) + return gsid == groups.pgsid; + } + + PTOKEN_GROUPS my_grps; + bool sawpg = false, ret = false; + + if (!GetTokenInformation (token, TokenGroups, NULL, 0, &size) && + GetLastError () != ERROR_INSUFFICIENT_BUFFER) + debug_printf ("GetTokenInformation(token, TokenGroups), %E"); + else if (!(my_grps = (PTOKEN_GROUPS) alloca (size))) + debug_printf ("alloca (my_grps) failed."); + else if (!GetTokenInformation (token, TokenGroups, my_grps, size, &size)) + debug_printf ("GetTokenInformation(my_token, TokenGroups), %E"); + else + { + if (groups.issetgroups ()) /* setgroups was called */ + { + cygsid gsid; + struct __group32 *gr; + bool saw[groups.sgsids.count ()]; + memset (saw, 0, sizeof(saw)); + + /* token groups found in /etc/group match the user.gsids ? */ + for (int gidx = 0; (gr = internal_getgrent (gidx)); ++gidx) + if (gsid.getfromgr (gr) && sid_in_token_groups (my_grps, gsid)) + { + int pos = groups.sgsids.position (gsid); + if (pos >= 0) + saw[pos] = true; + else if (groups.pgsid == gsid) + sawpg = true; + else if (gsid != well_known_world_sid + && gsid != usersid) + goto done; + } + /* user.sgsids groups must be in the token */ + for (int gidx = 0; gidx < groups.sgsids.count (); gidx++) + if (!saw[gidx] && !sid_in_token_groups (my_grps, groups.sgsids.sids[gidx])) + goto done; + } + /* The primary group must be in the token */ + ret = sawpg + || sid_in_token_groups (my_grps, groups.pgsid) + || groups.pgsid == usersid; + } +done: + return ret; +} + +HANDLE +create_token (cygsid &usersid, user_groups &new_groups, struct passwd *pw) +{ + NTSTATUS ret; + LSA_HANDLE lsa = INVALID_HANDLE_VALUE; + + cygsidlist tmp_gsids (cygsidlist_auto, 12); + + SECURITY_QUALITY_OF_SERVICE sqos = + { sizeof sqos, SecurityImpersonation, SECURITY_STATIC_TRACKING, FALSE }; + OBJECT_ATTRIBUTES oa = { sizeof oa, 0, 0, 0, 0, &sqos }; + LUID auth_luid = SYSTEM_LUID; + LARGE_INTEGER exp = { QuadPart:INT64_MAX }; + + TOKEN_USER user; + PTOKEN_GROUPS new_tok_gsids = NULL; + PTOKEN_PRIVILEGES privs = NULL; + TOKEN_OWNER owner; + TOKEN_PRIMARY_GROUP pgrp; + TOKEN_DEFAULT_DACL dacl = {}; + TOKEN_SOURCE source; + TOKEN_STATISTICS stats; + memcpy (source.SourceName, "Cygwin.1", 8); + source.SourceIdentifier.HighPart = 0; + source.SourceIdentifier.LowPart = 0x0101; + + HANDLE token = INVALID_HANDLE_VALUE; + HANDLE primary_token = INVALID_HANDLE_VALUE; + + PTOKEN_GROUPS my_tok_gsids = NULL; + DWORD size; + size_t psize = 0; + + /* SE_CREATE_TOKEN_NAME privilege needed to call NtCreateToken. */ + push_self_privilege (SE_CREATE_TOKEN_PRIVILEGE, true); + + /* Open policy object. */ + if ((lsa = open_local_policy ()) == INVALID_HANDLE_VALUE) + goto out; + + /* User, owner, primary group. */ + user.User.Sid = usersid; + user.User.Attributes = 0; + owner.Owner = usersid; + + /* Retrieve authentication id and group list from own process. */ + if (hProcToken) + { + /* Switching user context to SYSTEM doesn't inherit the authentication + id of the user account running current process. */ + if (usersid != well_known_system_sid) + if (!GetTokenInformation (hProcToken, TokenStatistics, + &stats, sizeof stats, &size)) + debug_printf + ("GetTokenInformation(hProcToken, TokenStatistics), %E"); + else + auth_luid = stats.AuthenticationId; + + /* Retrieving current processes group list to be able to inherit + some important well known group sids. */ + if (!GetTokenInformation (hProcToken, TokenGroups, NULL, 0, &size) + && GetLastError () != ERROR_INSUFFICIENT_BUFFER) + debug_printf ("GetTokenInformation(hProcToken, TokenGroups), %E"); + else if (!(my_tok_gsids = (PTOKEN_GROUPS) malloc (size))) + debug_printf ("malloc (my_tok_gsids) failed."); + else if (!GetTokenInformation (hProcToken, TokenGroups, my_tok_gsids, + size, &size)) + { + debug_printf ("GetTokenInformation(hProcToken, TokenGroups), %E"); + free (my_tok_gsids); + my_tok_gsids = NULL; + } + } + + /* Create list of groups, the user is member in. */ + int auth_pos; + if (new_groups.issetgroups ()) + get_setgroups_sidlist (tmp_gsids, usersid, pw, my_tok_gsids, new_groups, + auth_luid, auth_pos); + else if (!get_initgroups_sidlist (tmp_gsids, usersid, new_groups.pgsid, pw, + my_tok_gsids, auth_luid, auth_pos)) + goto out; + + /* Primary group. */ + pgrp.PrimaryGroup = new_groups.pgsid; + + /* Create a TOKEN_GROUPS list from the above retrieved list of sids. */ + new_tok_gsids = (PTOKEN_GROUPS) + alloca (sizeof (DWORD) + tmp_gsids.count () + * sizeof (SID_AND_ATTRIBUTES)); + new_tok_gsids->GroupCount = tmp_gsids.count (); + for (DWORD i = 0; i < new_tok_gsids->GroupCount; ++i) + { + new_tok_gsids->Groups[i].Sid = tmp_gsids.sids[i]; + new_tok_gsids->Groups[i].Attributes = SE_GROUP_MANDATORY + | SE_GROUP_ENABLED_BY_DEFAULT + | SE_GROUP_ENABLED; + } + if (auth_pos >= 0) + new_tok_gsids->Groups[auth_pos].Attributes |= SE_GROUP_LOGON_ID; + /* Retrieve list of privileges of that user. */ + if (!(privs = get_priv_list (lsa, usersid, tmp_gsids, psize))) + goto out; + + /* Let's be heroic... */ + ret = NtCreateToken (&token, TOKEN_ALL_ACCESS, &oa, TokenImpersonation, + &auth_luid, &exp, &user, new_tok_gsids, privs, &owner, + &pgrp, &dacl, &source); + if (ret) + __seterrno_from_nt_status (ret); + else if (GetLastError () == ERROR_PROC_NOT_FOUND) + { + __seterrno (); + debug_printf ("Loading NtCreateToken failed."); + } + else + { + /* Convert to primary token. */ + if (!DuplicateTokenEx (token, MAXIMUM_ALLOWED, &sec_none, + SecurityImpersonation, TokenPrimary, + &primary_token)) + { + __seterrno (); + debug_printf ("DuplicateTokenEx %E"); + } + } + +out: + pop_self_privilege (); + if (token != INVALID_HANDLE_VALUE) + CloseHandle (token); + if (privs) + free (privs); + if (my_tok_gsids) + free (my_tok_gsids); + close_local_policy (lsa); + + debug_printf ("0x%x = create_token ()", primary_token); + return primary_token; +} + +HANDLE +lsaauth (cygsid &usersid, user_groups &new_groups, struct passwd *pw) +{ + cygsidlist tmp_gsids (cygsidlist_auto, 12); + cygpsid pgrpsid; + LSA_STRING name; + HANDLE lsa_hdl = NULL, lsa = INVALID_HANDLE_VALUE; + LSA_OPERATIONAL_MODE sec_mode; + NTSTATUS ret, ret2; + ULONG package_id, size; + LUID auth_luid = SYSTEM_LUID; + struct { + LSA_STRING str; + CHAR buf[16]; + } origin; + cyglsa_t *authinf = NULL; + ULONG authinf_size; + TOKEN_SOURCE ts; + PCYG_TOKEN_GROUPS gsids = NULL; + PTOKEN_PRIVILEGES privs = NULL; + PACL dacl = NULL; + PVOID profile = NULL; + LUID luid; + QUOTA_LIMITS quota; + size_t psize = 0, gsize = 0, dsize = 0; + OFFSET offset, sids_offset; + int tmpidx, non_well_known_cnt; + + HANDLE user_token = NULL; + + push_self_privilege (SE_TCB_PRIVILEGE, true); + + /* Register as logon process. */ + str2lsa (name, "Cygwin"); + SetLastError (0); + ret = LsaRegisterLogonProcess (&name, &lsa_hdl, &sec_mode); + if (ret != STATUS_SUCCESS) + { + debug_printf ("LsaRegisterLogonProcess: %p", ret); + __seterrno_from_win_error (LsaNtStatusToWinError (ret)); + goto out; + } + else if (GetLastError () == ERROR_PROC_NOT_FOUND) + { + debug_printf ("Couldn't load Secur32.dll"); + goto out; + } + /* Get handle to our own LSA package. */ + str2lsa (name, CYG_LSA_PKGNAME); + ret = LsaLookupAuthenticationPackage (lsa_hdl, &name, &package_id); + if (ret != STATUS_SUCCESS) + { + debug_printf ("LsaLookupAuthenticationPackage: %p", ret); + __seterrno_from_win_error (LsaNtStatusToWinError (ret)); + goto out; + } + + /* Open policy object. */ + if ((lsa = open_local_policy ()) == INVALID_HANDLE_VALUE) + goto out; + + /* Create origin. */ + str2buf2lsa (origin.str, origin.buf, "Cygwin"); + /* Create token source. */ + memcpy (ts.SourceName, "Cygwin.1", 8); + ts.SourceIdentifier.HighPart = 0; + ts.SourceIdentifier.LowPart = 0x0103; + + /* Create list of groups, the user is member in. */ + int auth_pos; + if (new_groups.issetgroups ()) + get_setgroups_sidlist (tmp_gsids, usersid, pw, NULL, new_groups, auth_luid, + auth_pos); + else if (!get_initgroups_sidlist (tmp_gsids, usersid, new_groups.pgsid, pw, + NULL, auth_luid, auth_pos)) + goto out; + /* The logon SID entry is not generated automatically on Windows 2000 + and earlier for some reason. So add fake logon sid here, which is + filled with logon id values in the authentication package. */ + if (wincap.needs_logon_sid_in_sid_list ()) + tmp_gsids += fake_logon_sid; + + tmp_gsids.debug_print ("tmp_gsids"); + + /* Evaluate size of TOKEN_GROUPS list */ + non_well_known_cnt = tmp_gsids.non_well_known_count (); + gsize = sizeof (DWORD) + non_well_known_cnt * sizeof (SID_AND_ATTRIBUTES); + tmpidx = -1; + for (int i = 0; i < non_well_known_cnt; ++i) + if ((tmpidx = tmp_gsids.next_non_well_known_sid (tmpidx)) >= 0) + gsize += GetLengthSid (tmp_gsids.sids[tmpidx]); + + /* Retrieve list of privileges of that user. */ + if (!(privs = get_priv_list (lsa, usersid, tmp_gsids, psize))) + goto out; + + /* Create DefaultDacl. */ + dsize = sizeof (ACL) + 3 * sizeof (ACCESS_ALLOWED_ACE) + + GetLengthSid (usersid) + + GetLengthSid (well_known_admins_sid) + + GetLengthSid (well_known_system_sid); + dacl = (PACL) alloca (dsize); + if (!InitializeAcl (dacl, dsize, ACL_REVISION)) + goto out; + if (!AddAccessAllowedAce (dacl, ACL_REVISION, GENERIC_ALL, usersid)) + goto out; + if (!AddAccessAllowedAce (dacl, ACL_REVISION, GENERIC_ALL, + well_known_admins_sid)) + goto out; + if (!AddAccessAllowedAce (dacl, ACL_REVISION, GENERIC_ALL, + well_known_system_sid)) + goto out; + + /* Evaluate authinf size and allocate authinf. */ + authinf_size = (authinf->data - (PBYTE) authinf); + authinf_size += GetLengthSid (usersid); /* User SID */ + authinf_size += gsize; /* Groups + Group SIDs */ + /* When trying to define the admins group as primary group on Vista, + LsaLogonUser fails with error STATUS_INVALID_OWNER. As workaround + we define "Local" as primary group here. First, this adds the otherwise + missing "Local" group to the group list and second, seteuid32 + sets the primary group to the group set in /etc/passwd anyway. */ + pgrpsid = well_known_local_sid; + authinf_size += GetLengthSid (pgrpsid); /* Primary Group SID */ + + authinf_size += psize; /* Privileges */ + authinf_size += 0; /* Owner SID */ + authinf_size += dsize; /* Default DACL */ + + authinf = (cyglsa_t *) alloca (authinf_size); + authinf->inf_size = authinf_size - ((PBYTE) &authinf->inf - (PBYTE) authinf); + + authinf->magic = CYG_LSA_MAGIC; + + extract_nt_dom_user (pw, authinf->domain, authinf->username); + + /* Store stuff in authinf with offset relative to start of "inf" member, + instead of using pointers. */ + offset = authinf->data - (PBYTE) &authinf->inf; + + authinf->inf.ExpirationTime.LowPart = 0xffffffffL; + authinf->inf.ExpirationTime.HighPart = 0x7fffffffL; + /* User SID */ + authinf->inf.User.User.Sid = offset; + authinf->inf.User.User.Attributes = 0; + CopySid (GetLengthSid (usersid), (PSID) ((PBYTE) &authinf->inf + offset), + usersid); + offset += GetLengthSid (usersid); + /* Groups */ + authinf->inf.Groups = offset; + gsids = (PCYG_TOKEN_GROUPS) ((PBYTE) &authinf->inf + offset); + sids_offset = offset + sizeof (ULONG) + non_well_known_cnt + * sizeof (SID_AND_ATTRIBUTES); + gsids->GroupCount = non_well_known_cnt; + /* Group SIDs */ + tmpidx = -1; + for (int i = 0; i < non_well_known_cnt; ++i) + { + if ((tmpidx = tmp_gsids.next_non_well_known_sid (tmpidx)) < 0) + break; + gsids->Groups[i].Sid = sids_offset; + gsids->Groups[i].Attributes = SE_GROUP_MANDATORY + | SE_GROUP_ENABLED_BY_DEFAULT + | SE_GROUP_ENABLED; + /* Mark logon SID as logon SID :) */ + if (wincap.needs_logon_sid_in_sid_list () + && tmp_gsids.sids[tmpidx] == fake_logon_sid) + gsids->Groups[i].Attributes += SE_GROUP_LOGON_ID; + CopySid (GetLengthSid (tmp_gsids.sids[tmpidx]), + (PSID) ((PBYTE) &authinf->inf + sids_offset), + tmp_gsids.sids[tmpidx]); + sids_offset += GetLengthSid (tmp_gsids.sids[tmpidx]); + } + offset += gsize; + /* Primary Group SID */ + authinf->inf.PrimaryGroup.PrimaryGroup = offset; + CopySid (GetLengthSid (pgrpsid), (PSID) ((PBYTE) &authinf->inf + offset), + pgrpsid); + offset += GetLengthSid (pgrpsid); + /* Privileges */ + authinf->inf.Privileges = offset; + memcpy ((PBYTE) &authinf->inf + offset, privs, psize); + offset += psize; + /* Owner */ + authinf->inf.Owner.Owner = 0; + /* Default DACL */ + authinf->inf.DefaultDacl.DefaultDacl = offset; + memcpy ((PBYTE) &authinf->inf + offset, dacl, dsize); + + authinf->checksum = CYGWIN_VERSION_MAGIC (CYGWIN_VERSION_DLL_MAJOR, + CYGWIN_VERSION_DLL_MINOR); + PDWORD csp = (PDWORD) &authinf->username; + PDWORD csp_end = (PDWORD) ((PBYTE) authinf + authinf_size); + while (csp < csp_end) + authinf->checksum += *csp++; + + /* Try to logon... */ + ret = LsaLogonUser (lsa_hdl, (PLSA_STRING) &origin, Interactive, package_id, + authinf, authinf_size, NULL, &ts, &profile, &size, &luid, + &user_token, "a, &ret2); + if (ret != STATUS_SUCCESS) + { + debug_printf ("LsaLogonUser: %p", ret); + __seterrno_from_win_error (LsaNtStatusToWinError (ret)); + goto out; + } + if (profile) + LsaFreeReturnBuffer (profile); + + if (wincap.has_mandatory_integrity_control ()) + { + typedef struct _TOKEN_LINKED_TOKEN + { + HANDLE LinkedToken; + } TOKEN_LINKED_TOKEN, *PTOKEN_LINKED_TOKEN; +# define TokenLinkedToken ((TOKEN_INFORMATION_CLASS) 19) + + TOKEN_LINKED_TOKEN linked; + + if (GetTokenInformation (user_token, TokenLinkedToken, + (PVOID) &linked, sizeof linked, &size)) + { + debug_printf ("Linked Token: %lu", linked.LinkedToken); + if (linked.LinkedToken) + user_token = linked.LinkedToken; + } + } + +out: + if (privs) + free (privs); + close_local_policy (lsa); + if (lsa_hdl) + LsaDeregisterLogonProcess (lsa_hdl); + pop_self_privilege (); + + debug_printf ("0x%x = lsaauth ()", user_token); + return user_token; +} diff --git a/winsup/cygwin/sec_helper.cc b/winsup/cygwin/sec_helper.cc index 4fb6252bc..7fc9492ad 100644 --- a/winsup/cygwin/sec_helper.cc +++ b/winsup/cygwin/sec_helper.cc @@ -11,17 +11,8 @@ Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ #include "winsup.h" -#include -#include -#include #include -#include -#include #include -#include -#include -#include -#include #include "cygerrno.h" #include "security.h" #include "path.h" @@ -29,7 +20,6 @@ details. */ #include "dtable.h" #include "pinfo.h" #include "cygheap.h" -#include "cygtls.h" #include "pwdgrp.h" #include "ntdll.h" diff --git a/winsup/cygwin/security.cc b/winsup/cygwin/security.cc index 5fca3d8ed..5740446e7 100644 --- a/winsup/cygwin/security.cc +++ b/winsup/cygwin/security.cc @@ -1,4 +1,4 @@ -/* security.cc: NT security functions +/* security.cc: NT file access control functions Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Red Hat, Inc. @@ -13,22 +13,8 @@ Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ #include "winsup.h" -#include -#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "cygerrno.h" #include "security.h" #include "path.h" @@ -36,12 +22,12 @@ details. */ #include "dtable.h" #include "pinfo.h" #include "cygheap.h" -#include #include "ntdll.h" -#include "lm.h" #include "pwdgrp.h" -#include "cyglsa.h" -#include + +#define ALL_SECURITY_INFORMATION (DACL_SECURITY_INFORMATION \ + | GROUP_SECURITY_INFORMATION \ + | OWNER_SECURITY_INFORMATION) /* Set ntsec explicit as default. */ bool allow_ntsec = true; @@ -49,1227 +35,97 @@ bool allow_ntsec = true; It's defined here because of it's strong relationship to allow_ntsec. */ bool allow_smbntsec; -extern "C" void -cygwin_set_impersonation_token (const HANDLE hToken) -{ - debug_printf ("set_impersonation_token (%d)", hToken); - cygheap->user.external_token = hToken == INVALID_HANDLE_VALUE ? NO_IMPERSONATION : hToken; -} - -void -extract_nt_dom_user (const struct passwd *pw, char *domain, char *user) -{ - char *d, *u, *c; - - domain[0] = 0; - strlcpy (user, pw->pw_name, UNLEN + 1); - debug_printf ("pw_gecos %x (%s)", pw->pw_gecos, pw->pw_gecos); - - if ((d = strstr (pw->pw_gecos, "U-")) != NULL && - (d == pw->pw_gecos || d[-1] == ',')) - { - c = strechr (d + 2, ','); - if ((u = strechr (d + 2, '\\')) >= c) - u = d + 1; - else if (u - d <= INTERNET_MAX_HOST_NAME_LENGTH + 2) - strlcpy (domain, d + 2, u - d - 1); - if (c - u <= UNLEN + 1) - strlcpy (user, u + 1, c - u); - } - if (domain[0]) - return; - - cygsid psid; - DWORD ulen = UNLEN + 1; - DWORD dlen = INTERNET_MAX_HOST_NAME_LENGTH + 1; - SID_NAME_USE use; - if (psid.getfrompw (pw)) - LookupAccountSid (NULL, psid, user, &ulen, domain, &dlen, &use); -} - -extern "C" HANDLE -cygwin_logon_user (const struct passwd *pw, const char *password) -{ - if (!pw) - { - set_errno (EINVAL); - return INVALID_HANDLE_VALUE; - } - - char nt_domain[INTERNET_MAX_HOST_NAME_LENGTH + 1]; - char nt_user[UNLEN + 1]; - HANDLE hToken; - - extract_nt_dom_user (pw, nt_domain, nt_user); - debug_printf ("LogonUserA (%s, %s, %s, ...)", nt_user, nt_domain, password); - /* CV 2005-06-08: LogonUser should run under the primary process token, - otherwise it returns with ERROR_ACCESS_DENIED on W2K. Don't ask me why. */ - RevertToSelf (); - if (!LogonUserA (nt_user, *nt_domain ? nt_domain : NULL, (char *) password, - LOGON32_LOGON_INTERACTIVE, - LOGON32_PROVIDER_DEFAULT, - &hToken)) - { - __seterrno (); - hToken = INVALID_HANDLE_VALUE; - } - else if (!SetHandleInformation (hToken, - HANDLE_FLAG_INHERIT, - HANDLE_FLAG_INHERIT)) - { - __seterrno (); - CloseHandle (hToken); - hToken = INVALID_HANDLE_VALUE; - } - cygheap->user.reimpersonate (); - debug_printf ("%d = logon_user(%s,...)", hToken, pw->pw_name); - return hToken; -} - -static void -str2lsa (LSA_STRING &tgt, const char *srcstr) -{ - tgt.Length = strlen (srcstr); - tgt.MaximumLength = tgt.Length + 1; - tgt.Buffer = (PCHAR) srcstr; -} - -static void -str2buf2lsa (LSA_STRING &tgt, char *buf, const char *srcstr) -{ - tgt.Length = strlen (srcstr); - tgt.MaximumLength = tgt.Length + 1; - tgt.Buffer = (PCHAR) buf; - memcpy (buf, srcstr, tgt.MaximumLength); -} - -/* The dimension of buf is assumed to be at least strlen(srcstr) + 1, - The result will be shorter if the input has multibyte chars */ -void -str2buf2uni (UNICODE_STRING &tgt, WCHAR *buf, const char *srcstr) -{ - tgt.Buffer = (PWCHAR) buf; - tgt.MaximumLength = (strlen (srcstr) + 1) * sizeof (WCHAR); - tgt.Length = sys_mbstowcs (buf, srcstr, tgt.MaximumLength / sizeof (WCHAR)) - * sizeof (WCHAR); - if (tgt.Length) - tgt.Length -= sizeof (WCHAR); -} - -void -str2uni_cat (UNICODE_STRING &tgt, const char *srcstr) -{ - int len = sys_mbstowcs (tgt.Buffer + tgt.Length / sizeof (WCHAR), srcstr, - (tgt.MaximumLength - tgt.Length) / sizeof (WCHAR)); - if (len) - tgt.Length += (len - 1) * sizeof (WCHAR); - else - tgt.Length = tgt.MaximumLength = 0; -} - -static LSA_HANDLE -open_local_policy () -{ - LSA_OBJECT_ATTRIBUTES oa = { 0, 0, 0, 0, 0, 0 }; - LSA_HANDLE lsa = INVALID_HANDLE_VALUE; - - NTSTATUS ret = LsaOpenPolicy (NULL, &oa, POLICY_EXECUTE, &lsa); - if (ret != STATUS_SUCCESS) - __seterrno_from_win_error (LsaNtStatusToWinError (ret)); - return lsa; -} - -static void -close_local_policy (LSA_HANDLE &lsa) -{ - if (lsa != INVALID_HANDLE_VALUE) - LsaClose (lsa); - lsa = INVALID_HANDLE_VALUE; -} - -/* CV, 2006-07-06: Missing in w32api. */ -extern "C" DWORD WINAPI DsGetDcNameA (LPCSTR, LPCSTR, GUID *, LPCSTR, ULONG, - PDOMAIN_CONTROLLER_INFOA *); -#define DS_FORCE_REDISCOVERY 1 - -bool -get_logon_server (const char *domain, char *server, WCHAR *wserver, - bool rediscovery) -{ - DWORD dret; - PDOMAIN_CONTROLLER_INFOA pci; - WCHAR *buf; - DWORD size = INTERNET_MAX_HOST_NAME_LENGTH + 1; - WCHAR wdomain[size]; - - /* Empty domain is interpreted as local system */ - if ((GetComputerName (server + 2, &size)) && - (strcasematch (domain, server + 2) || !domain[0])) - { - server[0] = server[1] = '\\'; - if (wserver) - sys_mbstowcs (wserver, server, INTERNET_MAX_HOST_NAME_LENGTH + 1); - return true; - } - - /* Try to get any available domain controller for this domain */ - dret = DsGetDcNameA (NULL, domain, NULL, NULL, - rediscovery ? DS_FORCE_REDISCOVERY : 0, &pci); - if (dret == ERROR_SUCCESS) - { - strcpy (server, pci->DomainControllerName); - sys_mbstowcs (wserver, server, INTERNET_MAX_HOST_NAME_LENGTH + 1); - NetApiBufferFree (pci); - debug_printf ("DC: rediscovery: %d, server: %s", rediscovery, server); - return true; - } - else if (dret == ERROR_PROC_NOT_FOUND) - { - /* NT4 w/o DSClient */ - sys_mbstowcs (wdomain, domain, INTERNET_MAX_HOST_NAME_LENGTH + 1); - if (rediscovery) - dret = NetGetAnyDCName (NULL, wdomain, (LPBYTE *) &buf); - else - dret = NetGetDCName (NULL, wdomain, (LPBYTE *) &buf); - if (dret == NERR_Success) - { - sys_wcstombs (server, INTERNET_MAX_HOST_NAME_LENGTH + 1, buf); - if (wserver) - for (WCHAR *ptr1 = buf; (*wserver++ = *ptr1++);) - ; - NetApiBufferFree (buf); - debug_printf ("NT: rediscovery: %d, server: %s", rediscovery, server); - return true; - } - } - __seterrno_from_win_error (dret); - return false; -} - -static bool -get_user_groups (WCHAR *wlogonserver, cygsidlist &grp_list, char *user, - char *domain) -{ - char dgroup[INTERNET_MAX_HOST_NAME_LENGTH + GNLEN + 2]; - WCHAR wuser[UNLEN + 1]; - sys_mbstowcs (wuser, user, UNLEN + 1); - LPGROUP_USERS_INFO_0 buf; - DWORD cnt, tot, len; - NET_API_STATUS ret; - - /* Look only on logonserver */ - ret = NetUserGetGroups (wlogonserver, wuser, 0, (LPBYTE *) &buf, - MAX_PREFERRED_LENGTH, &cnt, &tot); - if (ret) - { - __seterrno_from_win_error (ret); - /* It's no error when the user name can't be found. */ - return ret == NERR_UserNotFound; - } - - len = strlen (domain); - strcpy (dgroup, domain); - dgroup[len++] = '\\'; - - for (DWORD i = 0; i < cnt; ++i) - { - cygsid gsid; - DWORD glen = MAX_SID_LEN; - char domain[INTERNET_MAX_HOST_NAME_LENGTH + 1]; - DWORD dlen = sizeof (domain); - SID_NAME_USE use = SidTypeInvalid; - - sys_wcstombs (dgroup + len, GNLEN + 1, buf[i].grui0_name); - if (!LookupAccountName (NULL, dgroup, gsid, &glen, domain, &dlen, &use)) - debug_printf ("LookupAccountName(%s), %E", dgroup); - else if (legal_sid_type (use)) - grp_list += gsid; - else - debug_printf ("Global group %s invalid. Domain: %s Use: %d", - dgroup, domain, use); - } - - NetApiBufferFree (buf); - return true; -} - -static bool -is_group_member (WCHAR *wgroup, PSID pusersid, cygsidlist &grp_list) -{ - LPLOCALGROUP_MEMBERS_INFO_1 buf; - DWORD cnt, tot; - NET_API_STATUS ret; - - /* Members can be users or global groups */ - ret = NetLocalGroupGetMembers (NULL, wgroup, 1, (LPBYTE *) &buf, - MAX_PREFERRED_LENGTH, &cnt, &tot, NULL); - if (ret) - return false; - - bool retval = true; - for (DWORD bidx = 0; bidx < cnt; ++bidx) - if (EqualSid (pusersid, buf[bidx].lgrmi1_sid)) - goto done; - else - { - /* The extra test for the group being a global group or a well-known - group is necessary, since apparently also aliases (for instance - Administrators or Users) can be members of local groups, even - though MSDN states otherwise. The GUI refuses to put aliases into - local groups, but the CLI interface allows it. However, a normal - logon token does not contain groups, in which the user is only - indirectly a member by being a member of an alias in this group. - So we also should not put them into the token group list. - Note: Allowing those groups in our group list renders external - tokens invalid, so that it becomes impossible to logon with - password and valid logon token. */ - for (int glidx = 0; glidx < grp_list.count (); ++glidx) - if ((buf[bidx].lgrmi1_sidusage == SidTypeGroup - || buf[bidx].lgrmi1_sidusage == SidTypeWellKnownGroup) - && EqualSid (grp_list.sids[glidx], buf[bidx].lgrmi1_sid)) - goto done; - } - - retval = false; - done: - NetApiBufferFree (buf); - return retval; -} - -static bool -get_user_local_groups (cygsidlist &grp_list, PSID pusersid) -{ - LPLOCALGROUP_INFO_0 buf; - DWORD cnt, tot; - NET_API_STATUS ret; - - ret = NetLocalGroupEnum (NULL, 0, (LPBYTE *) &buf, - MAX_PREFERRED_LENGTH, &cnt, &tot, NULL); - if (ret) - { - __seterrno_from_win_error (ret); - return false; - } - - char bgroup[INTERNET_MAX_HOST_NAME_LENGTH + GNLEN + 2]; - char lgroup[INTERNET_MAX_HOST_NAME_LENGTH + GNLEN + 2]; - DWORD blen, llen; - SID_NAME_USE use; - - blen = llen = INTERNET_MAX_HOST_NAME_LENGTH + 1; - if (!LookupAccountSid (NULL, well_known_admins_sid, lgroup, &llen, bgroup, &blen, &use) - || !GetComputerNameA (lgroup, &(llen = INTERNET_MAX_HOST_NAME_LENGTH + 1))) - { - __seterrno (); - return false; - } - bgroup[blen++] = lgroup[llen++] = '\\'; - - for (DWORD i = 0; i < cnt; ++i) - if (is_group_member (buf[i].lgrpi0_name, pusersid, grp_list)) - { - cygsid gsid; - DWORD glen = MAX_SID_LEN; - char domain[INTERNET_MAX_HOST_NAME_LENGTH + 1]; - DWORD dlen = sizeof (domain); - - use = SidTypeInvalid; - sys_wcstombs (bgroup + blen, GNLEN + 1, buf[i].lgrpi0_name); - if (!LookupAccountName (NULL, bgroup, gsid, &glen, domain, &dlen, &use)) - { - if (GetLastError () != ERROR_NONE_MAPPED) - debug_printf ("LookupAccountName(%s), %E", bgroup); - strcpy (lgroup + llen, bgroup + blen); - if (!LookupAccountName (NULL, lgroup, gsid, &glen, - domain, &dlen, &use)) - debug_printf ("LookupAccountName(%s), %E", lgroup); - } - if (!legal_sid_type (use)) - debug_printf ("Rejecting local %s. use: %d", bgroup + blen, use); - grp_list *= gsid; - } - NetApiBufferFree (buf); - return true; -} - -static bool -sid_in_token_groups (PTOKEN_GROUPS grps, cygpsid sid) -{ - if (!grps) - return false; - for (DWORD i = 0; i < grps->GroupCount; ++i) - if (sid == grps->Groups[i].Sid) - return true; - return false; -} - -#if 0 /* Unused */ -static BOOL -get_user_primary_group (WCHAR *wlogonserver, const char *user, - PSID pusersid, cygsid &pgrpsid) -{ - LPUSER_INFO_3 buf; - WCHAR wuser[UNLEN + 1]; - NET_API_STATUS ret; - BOOL retval = FALSE; - UCHAR count = 0; - - if (well_known_system_sid == pusersid) - { - pgrpsid = well_known_system_sid; - return TRUE; - } - - sys_mbstowcs (wuser, user, UNLEN + 1); - ret = NetUserGetInfo (wlogonserver, wuser, 3, (LPBYTE *) &buf); - if (ret) - { - __seterrno_from_win_error (ret); - return FALSE; - } - - pgrpsid = pusersid; - if (IsValidSid (pgrpsid) - && (count = *GetSidSubAuthorityCount (pgrpsid)) > 1) - { - *GetSidSubAuthority (pgrpsid, count - 1) = buf->usri3_primary_group_id; - retval = TRUE; - } - NetApiBufferFree (buf); - return retval; -} -#endif - -static void -get_unix_group_sidlist (struct passwd *pw, cygsidlist &grp_list) -{ - struct __group32 *gr; - cygsid gsid; - - for (int gidx = 0; (gr = internal_getgrent (gidx)); ++gidx) - { - if (gr->gr_gid == (__gid32_t) pw->pw_gid) - goto found; - else if (gr->gr_mem) - for (int gi = 0; gr->gr_mem[gi]; ++gi) - if (strcasematch (pw->pw_name, gr->gr_mem[gi])) - goto found; - continue; - found: - if (gsid.getfromgr (gr)) - grp_list += gsid; - - } -} - -static void -get_token_group_sidlist (cygsidlist &grp_list, PTOKEN_GROUPS my_grps, - LUID auth_luid, int &auth_pos) -{ - auth_pos = -1; - if (my_grps) - { - grp_list += well_known_local_sid; - if (sid_in_token_groups (my_grps, well_known_dialup_sid)) - grp_list *= well_known_dialup_sid; - if (sid_in_token_groups (my_grps, well_known_network_sid)) - grp_list *= well_known_network_sid; - if (sid_in_token_groups (my_grps, well_known_batch_sid)) - grp_list *= well_known_batch_sid; - grp_list *= well_known_interactive_sid; - if (sid_in_token_groups (my_grps, well_known_service_sid)) - grp_list *= well_known_service_sid; - if (sid_in_token_groups (my_grps, well_known_this_org_sid)) - grp_list *= well_known_this_org_sid; - } - else - { - grp_list += well_known_local_sid; - grp_list *= well_known_interactive_sid; - } - if (get_ll (auth_luid) != 999LL) /* != SYSTEM_LUID */ - { - for (DWORD i = 0; i < my_grps->GroupCount; ++i) - if (my_grps->Groups[i].Attributes & SE_GROUP_LOGON_ID) - { - grp_list += my_grps->Groups[i].Sid; - auth_pos = grp_list.count () - 1; - break; - } - } -} - -bool -get_server_groups (cygsidlist &grp_list, PSID usersid, struct passwd *pw) -{ - char user[UNLEN + 1]; - char domain[INTERNET_MAX_HOST_NAME_LENGTH + 1]; - WCHAR wserver[INTERNET_MAX_HOST_NAME_LENGTH + 3]; - char server[INTERNET_MAX_HOST_NAME_LENGTH + 3]; - - if (well_known_system_sid == usersid) - { - grp_list *= well_known_admins_sid; - get_unix_group_sidlist (pw, grp_list); - return true; - } - - grp_list *= well_known_world_sid; - grp_list *= well_known_authenticated_users_sid; - extract_nt_dom_user (pw, domain, user); - if (get_logon_server (domain, server, wserver, false) - && !get_user_groups (wserver, grp_list, user, domain) - && get_logon_server (domain, server, wserver, true)) - get_user_groups (wserver, grp_list, user, domain); - if (get_user_local_groups (grp_list, usersid)) - { - get_unix_group_sidlist (pw, grp_list); - return true; - } - return false; -} - -static bool -get_initgroups_sidlist (cygsidlist &grp_list, - PSID usersid, PSID pgrpsid, struct passwd *pw, - PTOKEN_GROUPS my_grps, LUID auth_luid, int &auth_pos) -{ - grp_list *= well_known_world_sid; - grp_list *= well_known_authenticated_users_sid; - if (well_known_system_sid == usersid) - auth_pos = -1; - else - get_token_group_sidlist (grp_list, my_grps, auth_luid, auth_pos); - if (!get_server_groups (grp_list, usersid, pw)) - return false; - - /* special_pgrp true if pgrpsid is not in normal groups */ - grp_list += pgrpsid; - return true; -} - -static void -get_setgroups_sidlist (cygsidlist &tmp_list, PSID usersid, struct passwd *pw, - PTOKEN_GROUPS my_grps, user_groups &groups, - LUID auth_luid, int &auth_pos) -{ - tmp_list *= well_known_world_sid; - tmp_list *= well_known_authenticated_users_sid; - get_token_group_sidlist (tmp_list, my_grps, auth_luid, auth_pos); - get_server_groups (tmp_list, usersid, pw); - for (int gidx = 0; gidx < groups.sgsids.count (); gidx++) - tmp_list += groups.sgsids.sids[gidx]; - tmp_list += groups.pgsid; -} - -static ULONG sys_privs[] = { - SE_CREATE_TOKEN_PRIVILEGE, - SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, - SE_LOCK_MEMORY_PRIVILEGE, - SE_INCREASE_QUOTA_PRIVILEGE, - SE_TCB_PRIVILEGE, - SE_SECURITY_PRIVILEGE, - SE_TAKE_OWNERSHIP_PRIVILEGE, - SE_LOAD_DRIVER_PRIVILEGE, - SE_SYSTEM_PROFILE_PRIVILEGE, /* Vista ONLY */ - SE_SYSTEMTIME_PRIVILEGE, - SE_PROF_SINGLE_PROCESS_PRIVILEGE, - SE_INC_BASE_PRIORITY_PRIVILEGE, - SE_CREATE_PAGEFILE_PRIVILEGE, - SE_CREATE_PERMANENT_PRIVILEGE, - SE_BACKUP_PRIVILEGE, - SE_RESTORE_PRIVILEGE, - SE_SHUTDOWN_PRIVILEGE, - SE_DEBUG_PRIVILEGE, - SE_AUDIT_PRIVILEGE, - SE_SYSTEM_ENVIRONMENT_PRIVILEGE, - SE_CHANGE_NOTIFY_PRIVILEGE, - SE_UNDOCK_PRIVILEGE, - SE_MANAGE_VOLUME_PRIVILEGE, - SE_IMPERSONATE_PRIVILEGE, - SE_CREATE_GLOBAL_PRIVILEGE, - SE_INCREASE_WORKING_SET_PRIVILEGE, - SE_TIME_ZONE_PRIVILEGE, - SE_CREATE_SYMBOLIC_LINK_PRIVILEGE -}; - -#define SYSTEM_PRIVILEGES_COUNT (sizeof sys_privs / sizeof *sys_privs) - -static PTOKEN_PRIVILEGES -get_system_priv_list (size_t &size) -{ - ULONG max_idx = 0; - while (max_idx < SYSTEM_PRIVILEGES_COUNT - && sys_privs[max_idx] != wincap.max_sys_priv ()) - ++max_idx; - if (max_idx >= SYSTEM_PRIVILEGES_COUNT) - api_fatal ("Coding error: wincap privilege %u doesn't exist in sys_privs", - wincap.max_sys_priv ()); - size = sizeof (ULONG) + (max_idx + 1) * sizeof (LUID_AND_ATTRIBUTES); - PTOKEN_PRIVILEGES privs = (PTOKEN_PRIVILEGES) malloc (size); - if (!privs) - { - debug_printf ("malloc (system_privs) failed."); - return NULL; - } - privs->PrivilegeCount = 0; - for (ULONG i = 0; i <= max_idx; ++i) - { - privs->Privileges[privs->PrivilegeCount].Luid.HighPart = 0L; - privs->Privileges[privs->PrivilegeCount].Luid.LowPart = sys_privs[i]; - privs->Privileges[privs->PrivilegeCount].Attributes = - SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_ENABLED_BY_DEFAULT; - ++privs->PrivilegeCount; - } - return privs; -} - -static PTOKEN_PRIVILEGES -get_priv_list (LSA_HANDLE lsa, cygsid &usersid, cygsidlist &grp_list, - size_t &size) +LONG +get_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd) { - PLSA_UNICODE_STRING privstrs; - ULONG cnt; - PTOKEN_PRIVILEGES privs = NULL; - NTSTATUS ret; - char buf[INTERNET_MAX_HOST_NAME_LENGTH + 1]; - - if (usersid == well_known_system_sid) - return get_system_priv_list (size); + NTSTATUS status = STATUS_SUCCESS; + ULONG len = 0; + int retry = 0; + int res = -1; - for (int grp = -1; grp < grp_list.count (); ++grp) + for (; retry < 2; ++retry) { - if (grp == -1) + if (fh) { - if ((ret = LsaEnumerateAccountRights (lsa, usersid, &privstrs, - &cnt)) != STATUS_SUCCESS) - continue; - } - else if ((ret = LsaEnumerateAccountRights (lsa, grp_list.sids[grp], - &privstrs, &cnt)) - != STATUS_SUCCESS) - continue; - for (ULONG i = 0; i < cnt; ++i) - { - LUID priv; - PTOKEN_PRIVILEGES tmp; - DWORD tmp_count; - - sys_wcstombs (buf, sizeof (buf), - privstrs[i].Buffer, privstrs[i].Length / 2); - if (!privilege_luid (buf, &priv)) - continue; - - if (privs) + status = NtQuerySecurityObject (fh, ALL_SECURITY_INFORMATION, + sd, len, &len); + if (status == STATUS_BUFFER_TOO_SMALL) { - DWORD pcnt = privs->PrivilegeCount; - LUID_AND_ATTRIBUTES *p = privs->Privileges; - for (; pcnt > 0; --pcnt, ++p) - if (priv.HighPart == p->Luid.HighPart - && priv.LowPart == p->Luid.LowPart) - goto next_account_right; + if (!sd.malloc (len)) + { + set_errno (ENOMEM); + break; + } + status = NtQuerySecurityObject (fh, ALL_SECURITY_INFORMATION, + sd, len, &len); } - - tmp_count = privs ? privs->PrivilegeCount : 0; - size = sizeof (DWORD) - + (tmp_count + 1) * sizeof (LUID_AND_ATTRIBUTES); - tmp = (PTOKEN_PRIVILEGES) realloc (privs, size); - if (!tmp) + if (NT_SUCCESS (status)) { - if (privs) - free (privs); - LsaFreeMemory (privstrs); - debug_printf ("realloc (privs) failed."); - return NULL; + res = 0; + break; } - tmp->PrivilegeCount = tmp_count; - privs = tmp; - privs->Privileges[privs->PrivilegeCount].Luid = priv; - privs->Privileges[privs->PrivilegeCount].Attributes = - SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_ENABLED_BY_DEFAULT; - ++privs->PrivilegeCount; - - next_account_right: - ; } - LsaFreeMemory (privstrs); - } - return privs; -} - -/* Accept a token if - - the requested usersid matches the TokenUser and - - if setgroups has been called: - the token groups that are listed in /etc/group match the union of - the requested primary and supplementary groups in gsids. - - else the (unknown) implicitly requested supplementary groups and those - in the token are the groups associated with the usersid. We assume - they match and verify only the primary groups. - The requested primary group must appear in the token. - The primary group in the token is a group associated with the usersid, - except if the token is internal and the group is in the token SD - (see create_token). In that latter case that group must match the - requested primary group. */ -bool -verify_token (HANDLE token, cygsid &usersid, user_groups &groups, bool *pintern) -{ - DWORD size; - bool intern = false; - - if (pintern) - { - TOKEN_SOURCE ts; - if (!GetTokenInformation (token, TokenSource, - &ts, sizeof ts, &size)) - debug_printf ("GetTokenInformation(), %E"); - else - *pintern = intern = !memcmp (ts.SourceName, "Cygwin.1", 8); - } - /* Verify usersid */ - cygsid tok_usersid = NO_SID; - if (!GetTokenInformation (token, TokenUser, - &tok_usersid, sizeof tok_usersid, &size)) - debug_printf ("GetTokenInformation(), %E"); - if (usersid != tok_usersid) - return false; - - /* For an internal token, if setgroups was not called and if the sd group - is not well_known_null_sid, it must match pgrpsid */ - if (intern && !groups.issetgroups ()) - { - const DWORD sd_buf_siz = MAX_SID_LEN + sizeof (SECURITY_DESCRIPTOR); - PSECURITY_DESCRIPTOR sd_buf = (PSECURITY_DESCRIPTOR) alloca (sd_buf_siz); - cygpsid gsid (NO_SID); - if (!GetKernelObjectSecurity (token, GROUP_SECURITY_INFORMATION, - sd_buf, sd_buf_siz, &size)) - debug_printf ("GetKernelObjectSecurity(), %E"); - else if (!GetSecurityDescriptorGroup (sd_buf, (PSID *) &gsid, - (BOOL *) &size)) - debug_printf ("GetSecurityDescriptorGroup(), %E"); - if (well_known_null_sid != gsid) - return gsid == groups.pgsid; - } - - PTOKEN_GROUPS my_grps; - bool sawpg = false, ret = false; - - if (!GetTokenInformation (token, TokenGroups, NULL, 0, &size) && - GetLastError () != ERROR_INSUFFICIENT_BUFFER) - debug_printf ("GetTokenInformation(token, TokenGroups), %E"); - else if (!(my_grps = (PTOKEN_GROUPS) alloca (size))) - debug_printf ("alloca (my_grps) failed."); - else if (!GetTokenInformation (token, TokenGroups, my_grps, size, &size)) - debug_printf ("GetTokenInformation(my_token, TokenGroups), %E"); - else - { - if (groups.issetgroups ()) /* setgroups was called */ - { - cygsid gsid; - struct __group32 *gr; - bool saw[groups.sgsids.count ()]; - memset (saw, 0, sizeof(saw)); - - /* token groups found in /etc/group match the user.gsids ? */ - for (int gidx = 0; (gr = internal_getgrent (gidx)); ++gidx) - if (gsid.getfromgr (gr) && sid_in_token_groups (my_grps, gsid)) - { - int pos = groups.sgsids.position (gsid); - if (pos >= 0) - saw[pos] = true; - else if (groups.pgsid == gsid) - sawpg = true; - else if (gsid != well_known_world_sid - && gsid != usersid) - goto done; - } - /* user.sgsids groups must be in the token */ - for (int gidx = 0; gidx < groups.sgsids.count (); gidx++) - if (!saw[gidx] && !sid_in_token_groups (my_grps, groups.sgsids.sids[gidx])) - goto done; - } - /* The primary group must be in the token */ - ret = sawpg - || sid_in_token_groups (my_grps, groups.pgsid) - || groups.pgsid == usersid; - } -done: - return ret; -} - -HANDLE -create_token (cygsid &usersid, user_groups &new_groups, struct passwd *pw) -{ - NTSTATUS ret; - LSA_HANDLE lsa = INVALID_HANDLE_VALUE; - - cygsidlist tmp_gsids (cygsidlist_auto, 12); - - SECURITY_QUALITY_OF_SERVICE sqos = - { sizeof sqos, SecurityImpersonation, SECURITY_STATIC_TRACKING, FALSE }; - OBJECT_ATTRIBUTES oa = { sizeof oa, 0, 0, 0, 0, &sqos }; - LUID auth_luid = SYSTEM_LUID; - LARGE_INTEGER exp = { QuadPart:INT64_MAX }; - - TOKEN_USER user; - PTOKEN_GROUPS new_tok_gsids = NULL; - PTOKEN_PRIVILEGES privs = NULL; - TOKEN_OWNER owner; - TOKEN_PRIMARY_GROUP pgrp; - TOKEN_DEFAULT_DACL dacl = {}; - TOKEN_SOURCE source; - TOKEN_STATISTICS stats; - memcpy (source.SourceName, "Cygwin.1", 8); - source.SourceIdentifier.HighPart = 0; - source.SourceIdentifier.LowPart = 0x0101; - - HANDLE token = INVALID_HANDLE_VALUE; - HANDLE primary_token = INVALID_HANDLE_VALUE; - - PTOKEN_GROUPS my_tok_gsids = NULL; - DWORD size; - size_t psize = 0; - - /* SE_CREATE_TOKEN_NAME privilege needed to call NtCreateToken. */ - push_self_privilege (SE_CREATE_TOKEN_PRIVILEGE, true); - - /* Open policy object. */ - if ((lsa = open_local_policy ()) == INVALID_HANDLE_VALUE) - goto out; - - /* User, owner, primary group. */ - user.User.Sid = usersid; - user.User.Attributes = 0; - owner.Owner = usersid; - - /* Retrieve authentication id and group list from own process. */ - if (hProcToken) - { - /* Switching user context to SYSTEM doesn't inherit the authentication - id of the user account running current process. */ - if (usersid != well_known_system_sid) - if (!GetTokenInformation (hProcToken, TokenStatistics, - &stats, sizeof stats, &size)) - debug_printf - ("GetTokenInformation(hProcToken, TokenStatistics), %E"); - else - auth_luid = stats.AuthenticationId; - - /* Retrieving current processes group list to be able to inherit - some important well known group sids. */ - if (!GetTokenInformation (hProcToken, TokenGroups, NULL, 0, &size) - && GetLastError () != ERROR_INSUFFICIENT_BUFFER) - debug_printf ("GetTokenInformation(hProcToken, TokenGroups), %E"); - else if (!(my_tok_gsids = (PTOKEN_GROUPS) malloc (size))) - debug_printf ("malloc (my_tok_gsids) failed."); - else if (!GetTokenInformation (hProcToken, TokenGroups, my_tok_gsids, - size, &size)) - { - debug_printf ("GetTokenInformation(hProcToken, TokenGroups), %E"); - free (my_tok_gsids); - my_tok_gsids = NULL; - } - } - - /* Create list of groups, the user is member in. */ - int auth_pos; - if (new_groups.issetgroups ()) - get_setgroups_sidlist (tmp_gsids, usersid, pw, my_tok_gsids, new_groups, - auth_luid, auth_pos); - else if (!get_initgroups_sidlist (tmp_gsids, usersid, new_groups.pgsid, pw, - my_tok_gsids, auth_luid, auth_pos)) - goto out; - - /* Primary group. */ - pgrp.PrimaryGroup = new_groups.pgsid; - - /* Create a TOKEN_GROUPS list from the above retrieved list of sids. */ - new_tok_gsids = (PTOKEN_GROUPS) - alloca (sizeof (DWORD) + tmp_gsids.count () - * sizeof (SID_AND_ATTRIBUTES)); - new_tok_gsids->GroupCount = tmp_gsids.count (); - for (DWORD i = 0; i < new_tok_gsids->GroupCount; ++i) - { - new_tok_gsids->Groups[i].Sid = tmp_gsids.sids[i]; - new_tok_gsids->Groups[i].Attributes = SE_GROUP_MANDATORY - | SE_GROUP_ENABLED_BY_DEFAULT - | SE_GROUP_ENABLED; - } - if (auth_pos >= 0) - new_tok_gsids->Groups[auth_pos].Attributes |= SE_GROUP_LOGON_ID; - /* Retrieve list of privileges of that user. */ - if (!(privs = get_priv_list (lsa, usersid, tmp_gsids, psize))) - goto out; - - /* Let's be heroic... */ - ret = NtCreateToken (&token, TOKEN_ALL_ACCESS, &oa, TokenImpersonation, - &auth_luid, &exp, &user, new_tok_gsids, privs, &owner, - &pgrp, &dacl, &source); - if (ret) - __seterrno_from_nt_status (ret); - else if (GetLastError () == ERROR_PROC_NOT_FOUND) - { - __seterrno (); - debug_printf ("Loading NtCreateToken failed."); - } - else - { - /* Convert to primary token. */ - if (!DuplicateTokenEx (token, MAXIMUM_ALLOWED, &sec_none, - SecurityImpersonation, TokenPrimary, - &primary_token)) - { - __seterrno (); - debug_printf ("DuplicateTokenEx %E"); - } - } - -out: - pop_self_privilege (); - if (token != INVALID_HANDLE_VALUE) - CloseHandle (token); - if (privs) - free (privs); - if (my_tok_gsids) - free (my_tok_gsids); - close_local_policy (lsa); - - debug_printf ("0x%x = create_token ()", primary_token); - return primary_token; -} - -HANDLE -lsaauth (cygsid &usersid, user_groups &new_groups, struct passwd *pw) -{ - cygsidlist tmp_gsids (cygsidlist_auto, 12); - cygpsid pgrpsid; - LSA_STRING name; - HANDLE lsa_hdl = NULL, lsa = INVALID_HANDLE_VALUE; - LSA_OPERATIONAL_MODE sec_mode; - NTSTATUS ret, ret2; - ULONG package_id, size; - LUID auth_luid = SYSTEM_LUID; - struct { - LSA_STRING str; - CHAR buf[16]; - } origin; - cyglsa_t *authinf = NULL; - ULONG authinf_size; - TOKEN_SOURCE ts; - PCYG_TOKEN_GROUPS gsids = NULL; - PTOKEN_PRIVILEGES privs = NULL; - PACL dacl = NULL; - PVOID profile = NULL; - LUID luid; - QUOTA_LIMITS quota; - size_t psize = 0, gsize = 0, dsize = 0; - OFFSET offset, sids_offset; - int tmpidx, non_well_known_cnt; - - HANDLE user_token = NULL; - - push_self_privilege (SE_TCB_PRIVILEGE, true); - - /* Register as logon process. */ - str2lsa (name, "Cygwin"); - SetLastError (0); - ret = LsaRegisterLogonProcess (&name, &lsa_hdl, &sec_mode); - if (ret != STATUS_SUCCESS) - { - debug_printf ("LsaRegisterLogonProcess: %p", ret); - __seterrno_from_win_error (LsaNtStatusToWinError (ret)); - goto out; - } - else if (GetLastError () == ERROR_PROC_NOT_FOUND) - { - debug_printf ("Couldn't load Secur32.dll"); - goto out; - } - /* Get handle to our own LSA package. */ - str2lsa (name, CYG_LSA_PKGNAME); - ret = LsaLookupAuthenticationPackage (lsa_hdl, &name, &package_id); - if (ret != STATUS_SUCCESS) - { - debug_printf ("LsaLookupAuthenticationPackage: %p", ret); - __seterrno_from_win_error (LsaNtStatusToWinError (ret)); - goto out; - } - - /* Open policy object. */ - if ((lsa = open_local_policy ()) == INVALID_HANDLE_VALUE) - goto out; - - /* Create origin. */ - str2buf2lsa (origin.str, origin.buf, "Cygwin"); - /* Create token source. */ - memcpy (ts.SourceName, "Cygwin.1", 8); - ts.SourceIdentifier.HighPart = 0; - ts.SourceIdentifier.LowPart = 0x0103; - - /* Create list of groups, the user is member in. */ - int auth_pos; - if (new_groups.issetgroups ()) - get_setgroups_sidlist (tmp_gsids, usersid, pw, NULL, new_groups, auth_luid, - auth_pos); - else if (!get_initgroups_sidlist (tmp_gsids, usersid, new_groups.pgsid, pw, - NULL, auth_luid, auth_pos)) - goto out; - /* The logon SID entry is not generated automatically on Windows 2000 - and earlier for some reason. So add fake logon sid here, which is - filled with logon id values in the authentication package. */ - if (wincap.needs_logon_sid_in_sid_list ()) - tmp_gsids += fake_logon_sid; - - tmp_gsids.debug_print ("tmp_gsids"); - - /* Evaluate size of TOKEN_GROUPS list */ - non_well_known_cnt = tmp_gsids.non_well_known_count (); - gsize = sizeof (DWORD) + non_well_known_cnt * sizeof (SID_AND_ATTRIBUTES); - tmpidx = -1; - for (int i = 0; i < non_well_known_cnt; ++i) - if ((tmpidx = tmp_gsids.next_non_well_known_sid (tmpidx)) >= 0) - gsize += GetLengthSid (tmp_gsids.sids[tmpidx]); - - /* Retrieve list of privileges of that user. */ - if (!(privs = get_priv_list (lsa, usersid, tmp_gsids, psize))) - goto out; - - /* Create DefaultDacl. */ - dsize = sizeof (ACL) + 3 * sizeof (ACCESS_ALLOWED_ACE) - + GetLengthSid (usersid) - + GetLengthSid (well_known_admins_sid) - + GetLengthSid (well_known_system_sid); - dacl = (PACL) alloca (dsize); - if (!InitializeAcl (dacl, dsize, ACL_REVISION)) - goto out; - if (!AddAccessAllowedAce (dacl, ACL_REVISION, GENERIC_ALL, usersid)) - goto out; - if (!AddAccessAllowedAce (dacl, ACL_REVISION, GENERIC_ALL, - well_known_admins_sid)) - goto out; - if (!AddAccessAllowedAce (dacl, ACL_REVISION, GENERIC_ALL, - well_known_system_sid)) - goto out; - - /* Evaluate authinf size and allocate authinf. */ - authinf_size = (authinf->data - (PBYTE) authinf); - authinf_size += GetLengthSid (usersid); /* User SID */ - authinf_size += gsize; /* Groups + Group SIDs */ - /* When trying to define the admins group as primary group on Vista, - LsaLogonUser fails with error STATUS_INVALID_OWNER. As workaround - we define "Local" as primary group here. First, this adds the otherwise - missing "Local" group to the group list and second, seteuid32 - sets the primary group to the group set in /etc/passwd anyway. */ - pgrpsid = well_known_local_sid; - authinf_size += GetLengthSid (pgrpsid); /* Primary Group SID */ - - authinf_size += psize; /* Privileges */ - authinf_size += 0; /* Owner SID */ - authinf_size += dsize; /* Default DACL */ - - authinf = (cyglsa_t *) alloca (authinf_size); - authinf->inf_size = authinf_size - ((PBYTE) &authinf->inf - (PBYTE) authinf); - - authinf->magic = CYG_LSA_MAGIC; - - extract_nt_dom_user (pw, authinf->domain, authinf->username); - - /* Store stuff in authinf with offset relative to start of "inf" member, - instead of using pointers. */ - offset = authinf->data - (PBYTE) &authinf->inf; - - authinf->inf.ExpirationTime.LowPart = 0xffffffffL; - authinf->inf.ExpirationTime.HighPart = 0x7fffffffL; - /* User SID */ - authinf->inf.User.User.Sid = offset; - authinf->inf.User.User.Attributes = 0; - CopySid (GetLengthSid (usersid), (PSID) ((PBYTE) &authinf->inf + offset), - usersid); - offset += GetLengthSid (usersid); - /* Groups */ - authinf->inf.Groups = offset; - gsids = (PCYG_TOKEN_GROUPS) ((PBYTE) &authinf->inf + offset); - sids_offset = offset + sizeof (ULONG) + non_well_known_cnt - * sizeof (SID_AND_ATTRIBUTES); - gsids->GroupCount = non_well_known_cnt; - /* Group SIDs */ - tmpidx = -1; - for (int i = 0; i < non_well_known_cnt; ++i) - { - if ((tmpidx = tmp_gsids.next_non_well_known_sid (tmpidx)) < 0) - break; - gsids->Groups[i].Sid = sids_offset; - gsids->Groups[i].Attributes = SE_GROUP_MANDATORY - | SE_GROUP_ENABLED_BY_DEFAULT - | SE_GROUP_ENABLED; - /* Mark logon SID as logon SID :) */ - if (wincap.needs_logon_sid_in_sid_list () - && tmp_gsids.sids[tmpidx] == fake_logon_sid) - gsids->Groups[i].Attributes += SE_GROUP_LOGON_ID; - CopySid (GetLengthSid (tmp_gsids.sids[tmpidx]), - (PSID) ((PBYTE) &authinf->inf + sids_offset), - tmp_gsids.sids[tmpidx]); - sids_offset += GetLengthSid (tmp_gsids.sids[tmpidx]); - } - offset += gsize; - /* Primary Group SID */ - authinf->inf.PrimaryGroup.PrimaryGroup = offset; - CopySid (GetLengthSid (pgrpsid), (PSID) ((PBYTE) &authinf->inf + offset), - pgrpsid); - offset += GetLengthSid (pgrpsid); - /* Privileges */ - authinf->inf.Privileges = offset; - memcpy ((PBYTE) &authinf->inf + offset, privs, psize); - offset += psize; - /* Owner */ - authinf->inf.Owner.Owner = 0; - /* Default DACL */ - authinf->inf.DefaultDacl.DefaultDacl = offset; - memcpy ((PBYTE) &authinf->inf + offset, dacl, dsize); - - authinf->checksum = CYGWIN_VERSION_MAGIC (CYGWIN_VERSION_DLL_MAJOR, - CYGWIN_VERSION_DLL_MINOR); - PDWORD csp = (PDWORD) &authinf->username; - PDWORD csp_end = (PDWORD) ((PBYTE) authinf + authinf_size); - while (csp < csp_end) - authinf->checksum += *csp++; - - /* Try to logon... */ - ret = LsaLogonUser (lsa_hdl, (PLSA_STRING) &origin, Interactive, package_id, - authinf, authinf_size, NULL, &ts, &profile, &size, &luid, - &user_token, "a, &ret2); - if (ret != STATUS_SUCCESS) - { - debug_printf ("LsaLogonUser: %p", ret); - __seterrno_from_win_error (LsaNtStatusToWinError (ret)); - goto out; - } - if (profile) - LsaFreeReturnBuffer (profile); - - if (wincap.has_mandatory_integrity_control ()) - { - typedef struct _TOKEN_LINKED_TOKEN - { - HANDLE LinkedToken; - } TOKEN_LINKED_TOKEN, *PTOKEN_LINKED_TOKEN; -# define TokenLinkedToken ((TOKEN_INFORMATION_CLASS) 19) - - TOKEN_LINKED_TOKEN linked; - - if (GetTokenInformation (user_token, TokenLinkedToken, - (PVOID) &linked, sizeof linked, &size)) - { - debug_printf ("Linked Token: %lu", linked.LinkedToken); - if (linked.LinkedToken) - user_token = linked.LinkedToken; + if (!retry) + { + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + + status = NtOpenFile (&fh, READ_CONTROL, + pc.get_object_attr (attr, sec_none_nih), + &io, FILE_SHARE_VALID_FLAGS, + FILE_OPEN_FOR_BACKUP_INTENT); + if (!NT_SUCCESS (status)) + { + fh = NULL; + break; + } } } - -out: - if (privs) - free (privs); - close_local_policy (lsa); - if (lsa_hdl) - LsaDeregisterLogonProcess (lsa_hdl); - pop_self_privilege (); - - debug_printf ("0x%x = lsaauth ()", user_token); - return user_token; -} - -/* read_sd reads a security descriptor from a file. - In case of error, -1 is returned and errno is set. - If sd_buf is too small, 0 is returned and sd_size - is set to the needed buffer size. - On success, 1 is returned. - - GetFileSecurity() is used instead of BackupRead() - to avoid access denied errors if the caller has - not the permission to open that file for read. - - Originally the function should return the size - of the SD on success. Unfortunately NT returns - 0 in `len' on success, while W2K returns the - correct size! - - 2003-11-26: Now the function allocates the space needed by itself so - it knows the real size and returns it in the security_descriptor object. -*/ - -LONG -read_sd (const char *file, security_descriptor &sd) -{ - - DWORD len = 0; - const char *pfile = file; - char fbuf[CYG_MAX_PATH]; - if (current_codepage == oem_cp) - { - DWORD fname_len = min (sizeof (fbuf) - 1, strlen (file)); - bzero (fbuf, sizeof (fbuf)); - OemToCharBuff (file, fbuf, fname_len); - pfile = fbuf; - } - - if (!GetFileSecurity (pfile, - OWNER_SECURITY_INFORMATION - | GROUP_SECURITY_INFORMATION - | DACL_SECURITY_INFORMATION, - NULL, 0, &len) - && GetLastError () != ERROR_INSUFFICIENT_BUFFER) - { - debug_printf ("file %s", file); - __seterrno (); - return -1; - } - debug_printf ("file %s: len %d", file, len); - if (!sd.malloc (len)) - { - set_errno (ENOMEM); - return -1; - } - if (!GetFileSecurity (pfile, - OWNER_SECURITY_INFORMATION - | GROUP_SECURITY_INFORMATION - | DACL_SECURITY_INFORMATION, - sd, len, &len)) - { - __seterrno (); - return -1; - } - return sd.size (); + if (retry && fh) + NtClose (fh); + if (!NT_SUCCESS (status)) + __seterrno_from_nt_status (status); + return res; } LONG -write_sd (HANDLE fh, const char *file, security_descriptor &sd) +set_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd) { - NTSTATUS ret = STATUS_SUCCESS; + NTSTATUS status = STATUS_SUCCESS; int retry = 0; int res = -1; + for (; retry < 2; ++retry) { - if (retry && (fh = CreateFile (file, WRITE_OWNER | WRITE_DAC, - FILE_SHARE_READ | FILE_SHARE_WRITE, - &sec_none_nih, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL - | FILE_FLAG_BACKUP_SEMANTICS, - NULL)) == INVALID_HANDLE_VALUE) - break; - if (fh && (ret = NtSetSecurityObject (fh, - DACL_SECURITY_INFORMATION - | GROUP_SECURITY_INFORMATION - | OWNER_SECURITY_INFORMATION, - sd)) == STATUS_SUCCESS) - break; + if (fh) + { + status = NtSetSecurityObject (fh, ALL_SECURITY_INFORMATION, sd); + if (NT_SUCCESS (status)) + { + res = 0; + break; + } + } + if (!retry) + { + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + + status = NtOpenFile (&fh, WRITE_OWNER | WRITE_DAC, + pc.get_object_attr (attr, sec_none_nih), + &io, FILE_SHARE_VALID_FLAGS, + FILE_OPEN_FOR_RECOVERY); + if (!NT_SUCCESS (status)) + { + fh = NULL; + break; + } + } } - if (retry && fh != INVALID_HANDLE_VALUE) - CloseHandle (fh); - if (fh == INVALID_HANDLE_VALUE) /* CreateFile failed */ - __seterrno (); - else if (ret != STATUS_SUCCESS) /* NtSetSecurityObject failed */ - __seterrno_from_nt_status (ret); - else /* Everything's fine. */ - res = 0; + if (retry && fh) + NtClose (fh); + if (!NT_SUCCESS (status)) + __seterrno_from_nt_status (status); return res; } @@ -1426,25 +282,19 @@ get_info_from_sd (PSECURITY_DESCRIPTOR psd, mode_t *attribute, } static int -get_reg_security (HANDLE handle, security_descriptor &sd_ret) +get_reg_sd (HANDLE handle, security_descriptor &sd_ret) { LONG ret; DWORD len = 0; - ret = RegGetKeySecurity ((HKEY) handle, - DACL_SECURITY_INFORMATION - | GROUP_SECURITY_INFORMATION - | OWNER_SECURITY_INFORMATION, + ret = RegGetKeySecurity ((HKEY) handle, ALL_SECURITY_INFORMATION, sd_ret, &len); if (ret == ERROR_INSUFFICIENT_BUFFER) { if (!sd_ret.malloc (len)) set_errno (ENOMEM); else - ret = RegGetKeySecurity ((HKEY) handle, - DACL_SECURITY_INFORMATION - | GROUP_SECURITY_INFORMATION - | OWNER_SECURITY_INFORMATION, + ret = RegGetKeySecurity ((HKEY) handle, ALL_SECURITY_INFORMATION, sd_ret, &len); } if (ret != ERROR_SUCCESS) @@ -1456,87 +306,36 @@ get_reg_security (HANDLE handle, security_descriptor &sd_ret) } int -get_nt_object_security (HANDLE handle, SE_OBJECT_TYPE object_type, - security_descriptor &sd_ret) -{ - NTSTATUS ret; - ULONG len = 0; - - /* Do not try to use GetSecurityInfo (again), unless we drop NT4 support. - GetSecurityInfo returns the wrong user information when running in - a user session using a token created with NtCreateToken under NT4. - Works fine in 2K and above, but that doesn't help a lot. */ - - /* Unfortunately, NtQuerySecurityObject doesn't work on predefined registry - keys like HKEY_LOCAL_MACHINE. It fails with "Invalid Handle". So we - have to retreat to the Win32 registry functions for registry keys. - What bugs me is that RegGetKeySecurity is obviously just a wrapper - around NtQuerySecurityObject, but there seems to be no function to - convert pseudo HKEY values to real handles. */ - if (object_type == SE_REGISTRY_KEY) - return get_reg_security (handle, sd_ret); - - ret = NtQuerySecurityObject (handle, - DACL_SECURITY_INFORMATION - | GROUP_SECURITY_INFORMATION - | OWNER_SECURITY_INFORMATION, - sd_ret, len, &len); - if (ret == STATUS_BUFFER_TOO_SMALL) - { - if (!sd_ret.malloc (len)) - set_errno (ENOMEM); - else - ret = NtQuerySecurityObject (handle, - DACL_SECURITY_INFORMATION - | GROUP_SECURITY_INFORMATION - | OWNER_SECURITY_INFORMATION, - sd_ret, len, &len); - } - if (ret != STATUS_SUCCESS) - { - __seterrno_from_nt_status (ret); - return -1; - } - return 0; -} - -int -get_object_attribute (HANDLE handle, SE_OBJECT_TYPE object_type, - mode_t *attribute, __uid32_t *uidret, __gid32_t *gidret) +get_reg_attribute (HKEY hkey, mode_t *attribute, __uid32_t *uidret, + __gid32_t *gidret) { if (allow_ntsec) { security_descriptor sd; - PSECURITY_DESCRIPTOR psd = NULL; - if (get_nt_object_security (handle, object_type, sd)) + if (!get_reg_sd (hkey, sd)) { - if (object_type == SE_FILE_OBJECT) - return -1; + get_info_from_sd (sd, attribute, uidret, gidret); + return 0; } - else - psd = sd; - get_info_from_sd (psd, attribute, uidret, gidret); - return 0; } /* The entries are already set to default values */ return -1; } int -get_file_attribute (int use_ntsec, HANDLE handle, const char *file, +get_file_attribute (HANDLE handle, path_conv &pc, mode_t *attribute, __uid32_t *uidret, __gid32_t *gidret) { - syscall_printf ("file: %s", file); - - if (use_ntsec && allow_ntsec) + if (pc.has_acls () && allow_ntsec) { security_descriptor sd; - if (!handle || get_nt_object_security (handle, SE_FILE_OBJECT, sd)) - read_sd (file, sd); - get_info_from_sd (sd, attribute, uidret, gidret); - return 0; + if (!get_file_sd (handle, pc, sd)) + { + get_info_from_sd (sd, attribute, uidret, gidret); + return 0; + } } if (uidret) @@ -1893,35 +692,25 @@ set_security_attribute (int attribute, PSECURITY_ATTRIBUTES psa, } int -set_file_attribute (bool use_ntsec, HANDLE handle, const char *file, +set_file_attribute (HANDLE handle, path_conv &pc, __uid32_t uid, __gid32_t gid, int attribute) { int ret = -1; - if (use_ntsec && allow_ntsec) + if (pc.has_acls () && allow_ntsec) { security_descriptor sd; - if (((handle && !get_nt_object_security (handle, SE_FILE_OBJECT, sd)) - || read_sd (file, sd) > 0) - && alloc_sd (uid, gid, attribute, sd)) - ret = write_sd (handle, file, sd); + if (!get_file_sd (handle, pc, sd) && alloc_sd (uid, gid, attribute, sd)) + ret = set_file_sd (handle, pc, sd); } else ret = 0; syscall_printf ("%d = set_file_attribute (%s, %d, %d, %p)", - ret, file, uid, gid, attribute); + ret, pc.get_win32 (), uid, gid, attribute); return ret; } -int -set_file_attribute (bool use_ntsec, HANDLE handle, const char *file, - int attribute) -{ - return set_file_attribute (use_ntsec, handle, file, - myself->uid, myself->gid, attribute); -} - static int check_access (security_descriptor &sd, GENERIC_MAPPING &mapping, DWORD desired, int flags) @@ -1993,7 +782,7 @@ check_access (security_descriptor &sd, GENERIC_MAPPING &mapping, } int -check_file_access (const char *fn, int flags) +check_file_access (path_conv &pc, int flags) { security_descriptor sd; int ret = -1; @@ -2008,7 +797,7 @@ check_file_access (const char *fn, int flags) desired |= FILE_WRITE_DATA; if (flags & X_OK) desired |= FILE_EXECUTE; - if (read_sd (fn, sd) > 0) + if (!get_file_sd (NULL, pc, sd)) ret = check_access (sd, mapping, desired, flags); debug_printf ("flags %x, ret %d", flags, ret); return ret; @@ -2030,7 +819,7 @@ check_registry_access (HANDLE hdl, int flags) desired |= KEY_SET_VALUE; if (flags & X_OK) desired |= KEY_QUERY_VALUE; - if (!get_nt_object_security (hdl, SE_REGISTRY_KEY, sd)) + if (!get_reg_sd (hdl, sd)) ret = check_access (sd, mapping, desired, flags); /* As long as we can't write the registry... */ if (flags & W_OK) diff --git a/winsup/cygwin/security.h b/winsup/cygwin/security.h index 9aaaadb7d..c69785563 100644 --- a/winsup/cygwin/security.h +++ b/winsup/cygwin/security.h @@ -335,19 +335,16 @@ extern bool allow_ntsec; extern bool allow_smbntsec; /* File manipulation */ -int __stdcall get_file_attribute (int, HANDLE, const char *, mode_t *, - __uid32_t * = NULL, __gid32_t * = NULL); -int __stdcall set_file_attribute (bool, HANDLE, const char *, int); -int __stdcall set_file_attribute (bool, HANDLE, const char *, __uid32_t, __gid32_t, int); -int __stdcall get_nt_object_security (HANDLE, SE_OBJECT_TYPE, - security_descriptor &); -int __stdcall get_object_attribute (HANDLE handle, SE_OBJECT_TYPE object_type, mode_t *, - __uid32_t * = NULL, __gid32_t * = NULL); -LONG __stdcall read_sd (const char *file, security_descriptor &sd); -LONG __stdcall write_sd (HANDLE fh, const char *file, security_descriptor &sd); +int __stdcall get_file_attribute (HANDLE, path_conv &, mode_t *, + __uid32_t *, __gid32_t *); +int __stdcall set_file_attribute (HANDLE, path_conv &, + __uid32_t, __gid32_t, int); +int __stdcall get_reg_attribute (HKEY hkey, mode_t *, __uid32_t *, __gid32_t *); +LONG __stdcall get_file_sd (HANDLE fh, path_conv &, security_descriptor &sd); +LONG __stdcall set_file_sd (HANDLE fh, path_conv &, security_descriptor &sd); bool __stdcall add_access_allowed_ace (PACL acl, int offset, DWORD attributes, PSID sid, size_t &len_add, DWORD inherit); bool __stdcall add_access_denied_ace (PACL acl, int offset, DWORD attributes, PSID sid, size_t &len_add, DWORD inherit); -int __stdcall check_file_access (const char *, int); +int __stdcall check_file_access (path_conv &, int); int __stdcall check_registry_access (HANDLE, int); void set_security_attribute (int attribute, PSECURITY_ATTRIBUTES psa, @@ -359,8 +356,8 @@ bool get_sids_info (cygpsid, cygpsid, __uid32_t * , __gid32_t *); struct __acl32; extern "C" int aclsort32 (int, int, __acl32 *); extern "C" int acl32 (const char *, int, int, __acl32 *); -int getacl (HANDLE, const char *, DWORD, int, __acl32 *); -int setacl (HANDLE, const char *, int, __acl32 *, bool &); +int getacl (HANDLE, path_conv &, int, __acl32 *); +int setacl (HANDLE, path_conv &, int, __acl32 *, bool &); struct _UNICODE_STRING; void __stdcall str2buf2uni (_UNICODE_STRING &, WCHAR *, const char *) __attribute__ ((regparm (3))); -- 2.43.5