Re: PATCH: login under privileged user != SYSTEM

2008-04-21 Thread Corinna Vinschen
On Apr 19 01:52, Charles Wilson wrote:
 Corinna Vinschen wrote:
 On Apr 18 04:32, Charles Wilson wrote: 
 So, I'm still not checking that the uid specified is a member of the 
 local Administrators group.

 Now I am. See below.

 As for an account being Administrator, and apart from special accounts
 like SYSTEM or LOCAL_SERVICE...
 What about just checking the value of PUSER_INFO_3-usri3_priv?  It may
 contain the value USER_PRIV_ADMIN.  That should be sufficient, afaics.

 That works, even on Vista. I've uploaded a new login-1.9-8 as a test 
 release. Please take a look (I'll make a more formal 'avail for test' 
 announcement later).

I saw you did already.


Thanks,
Corinna

-- 
Corinna Vinschen  Please, send mails regarding Cygwin to
Cygwin Project Co-Leader  cygwin AT cygwin DOT com
Red Hat

--
Unsubscribe info:  http://cygwin.com/ml/#unsubscribe-simple
Problem reports:   http://cygwin.com/problems.html
Documentation: http://cygwin.com/docs.html
FAQ:   http://cygwin.com/faq/



Re: PATCH: login under privileged user != SYSTEM

2008-04-18 Thread Charles Wilson

Corinna Vinschen wrote:

On Apr 17 01:48, Charles Wilson wrote:


With these changes, I can now get passwordless rlogin when inetd is running 
under a privileged user, and not SYSTEM.


Most of the code was adapted from editrights/main.c...


Cool, thanks!  Would you mind to take over login maintainance, too?  It
was always just the wagging tail of inetutils anyway...


Sure.


Other than that, I'd like to suggest a few minor changes to the patch:

- The SeServiceLogonRight doesn't have to be tested, IMHO.  It doesn't
  have anything to do with user account switching.


Ack.


- I don't understand why NT4 is handled specially by only checking for the
  uid while 2K and XP get the additional account check if necessary.  None
  of the functions are new with 2K, they all exists since NT 3.51.


I initially thought the program flow would be rather awkward, to make NT 
act like 2k/xp -- and I just didn't care much for NT.  But after I 
finished coding I should have noticed that it would really be quite 
simple to do that.  But I didn't.


Fixed now.


- I wouldn't do the automatic yes for uid 18 anymore.  Even for NT/2K/XP
  it would be more correct to check if the current account running the

 ^^^
  process is the one with SID S-1-5-18.  


But that's not exactly what you want, here. Sometimes, login.c does
  isROOTUID(getuid())
which could be replaced as you suggest. But *most* of the time, login.c does
  isROOTUID(pw-pw_uid)
before it has actually switched to that user.

And saying that isROOTUID(uid) ==
  {
setuid(pw-pw_uid);
isCurrentProcessRunningAsROOT();
setuid(saved_uid);
  }
is overkill -- especially as I want isROOTUID(uid) to work even if the 
current user does NOT have the privileges needed for setuid() to work.



  Given that there's already
  so much code for Windows specific privilege checking, I don't think
  it hurts a lot to add something along the lines of

AllocateAndInitializeSid (SECURITY_NT_AUTHORITY, 1, 18, ..., system_sid);
token = OpenProcessToken (GetCurrentProcess ());
user_sid = GetTokenInformation(token, TOKEN_USER);
if (EqualSid (user_sid, system_sid))
  yes
else
  check_privileges


I implemented something along these lines. After creating several 
building block functions, I wrapped it all up into:


extern int currentUserIsLocalSystem();
extern int currentUserIsMemberOfLocalAdministrators();

These two use OpenProcessToken/GetTokenInformation as you suggested. 
However, the one I really use is:


extern int uidIsLocalSystem(uid_t uid);

 aside:
And I wish I could have figured out how to make 
uidIsMemberOfLocalAdmin(uid_t uid), but if uid != current user it's 
really hard to get the either (a) the list of groups a particular user 
is a member of, or (b) the list of users that are members of a 
particular group.  Since I already have a make-SID-from-uid method, if I 
had (a) I could iterate that list trying to match the local 
Administrators SID, or if I had (b) I could iterate through the list and 
compare to my SID-from-uid.


I know there is NetUserGetLocalGroups, but what if the user is a member 
of a global group, and the local security policy makes that global group 
a member of the (local) Administrators group? With the multi-level 
inclusion of groups, it's almost easier to go the other way: get the 
local administrator group, and use (recursively) NetLocalGroupGetMembers 
and NetGroupGetUsers to build a list of all users that are (directly or 
by inclusion) members of the (local) Administrators group -- and THEN 
iterate that to see if any of them match SID-from-uid.


But neither is easy.
 end aside

So, I'm still not checking that the uid specified is a member of the 
local Administrators group.


I did discover one awkward thing: in my make-SID-from-uid function, I do 
the following


1. get struct passwd* for uid
2. cygwin_internal(CW_EXTRACT_DOMAIN_AND_USER, pw, domain, name);
3. get the servername for the domain by using either
   DsGetDcName or NetGetDCName
4. use NetUserGetInfo to get a PUSER_INFO_3 structure
   (if domain user, and call fails, try again locally...)
5. use LookupAccountName to get the SID
   (if basic call fails and returned account type is SidTypeDomain,
   try again after adding domain spec to username)

However, if uid = 18 it turns out that NetUserGetInfo(, 
toUnicode(LocalSystem),...) always fails. I even tested that 
proposition in a quick test app. It just doesn't work.


Which means that my uidIsLocalSystem(uid_t uid) function is broken in 
this case. It's algorithm is:


1) create SID for LocalSystem manually. call this desiredSID
2) get SID corresponding to supplied uid. call this userSID
3) compare desiredSID == userSID

And for LocalSystem, step #2 fails.  So, I had to modify 
make-SID-from-uid, by adding step 1a:


1a. if pw-pw_name == LocalSystem (after canonicalizing from SYSTEM)
manually create SID for 

Re: PATCH: login under privileged user != SYSTEM

2008-04-18 Thread Corinna Vinschen
On Apr 18 04:32, Charles Wilson wrote:
 Corinna Vinschen wrote:
 Cool, thanks!  Would you mind to take over login maintainance, too?  It
 was always just the wagging tail of inetutils anyway...

 Sure.

Thank you!  Igor?  Can we get another gold star for Charles?

 - I wouldn't do the automatic yes for uid 18 anymore.  Even for NT/2K/XP
   it would be more correct to check if the current account running the
  ^^^
   process is the one with SID S-1-5-18.  

 But that's not exactly what you want, here. Sometimes, login.c does
   isROOTUID(getuid())
 which could be replaced as you suggest. But *most* of the time, login.c 
 does
   isROOTUID(pw-pw_uid)
 before it has actually switched to that user.

 And saying that isROOTUID(uid) ==
   {
 setuid(pw-pw_uid);
 isCurrentProcessRunningAsROOT();
 setuid(saved_uid);
   }
 is overkill -- especially as I want isROOTUID(uid) to work even if the 
 current user does NOT have the privileges needed for setuid() to work.

That makes sense.

  aside:
 And I wish I could have figured out how to make 
 uidIsMemberOfLocalAdmin(uid_t uid), but if uid != current user it's really 
 hard to get the either (a) the list of groups a particular user is a member 
 of, or (b) the list of users that are members of a particular group.  Since 
 I already have a make-SID-from-uid method, if I had (a) I could iterate 
 that list trying to match the local Administrators SID, or if I had (b) I 
 could iterate through the list and compare to my SID-from-uid.

 I know there is NetUserGetLocalGroups, but what if the user is a member of 
 a global group, and the local security policy makes that global group a 
 member of the (local) Administrators group? With the multi-level inclusion 
 of groups, it's almost easier to go the other way: get the local 
 administrator group, and use (recursively) NetLocalGroupGetMembers and 
 NetGroupGetUsers to build a list of all users that are (directly or by 
 inclusion) members of the (local) Administrators group -- and THEN iterate 
 that to see if any of them match SID-from-uid.

 But neither is easy.
  end aside

Yes, I agree wholeheartedly.  The handling of users and groups is
really complicated and you're coding your brain out of your head just
to *get* the information and tyhen you still have to test.  It's
really not funny how much code you need to fetch certain types of
information.

 So, I'm still not checking that the uid specified is a member of the local 
 Administrators group.

 I did discover one awkward thing: in my make-SID-from-uid function, I do 
 the following

 1. get struct passwd* for uid
 2. cygwin_internal(CW_EXTRACT_DOMAIN_AND_USER, pw, domain, name);
 3. get the servername for the domain by using either
DsGetDcName or NetGetDCName
 4. use NetUserGetInfo to get a PUSER_INFO_3 structure
(if domain user, and call fails, try again locally...)
 5. use LookupAccountName to get the SID
(if basic call fails and returned account type is SidTypeDomain,
try again after adding domain spec to username)

 However, if uid = 18 it turns out that NetUserGetInfo(, 
 toUnicode(LocalSystem),...) always fails. I even tested that proposition 
 in a quick test app. It just doesn't work.

As for an account being Administrator, and apart from special accounts
like SYSTEM or LOCAL_SERVICE...

What about just checking the value of PUSER_INFO_3-usri3_priv?  It may
contain the value USER_PRIV_ADMIN.  That should be sufficient, afaics.


Corinna

-- 
Corinna Vinschen  Please, send mails regarding Cygwin to
Cygwin Project Co-Leader  cygwin AT cygwin DOT com
Red Hat

--
Unsubscribe info:  http://cygwin.com/ml/#unsubscribe-simple
Problem reports:   http://cygwin.com/problems.html
Documentation: http://cygwin.com/docs.html
FAQ:   http://cygwin.com/faq/



Re: PATCH: login under privileged user != SYSTEM

2008-04-18 Thread Charles Wilson

Corinna Vinschen wrote:
On Apr 18 04:32, Charles Wilson wrote: 
So, I'm still not checking that the uid specified is a member of the local 
Administrators group.


Now I am. See below.


As for an account being Administrator, and apart from special accounts
like SYSTEM or LOCAL_SERVICE...

What about just checking the value of PUSER_INFO_3-usri3_priv?  It may
contain the value USER_PRIV_ADMIN.  That should be sufficient, afaics.


That works, even on Vista. I've uploaded a new login-1.9-8 as a test 
release. Please take a look (I'll make a more formal 'avail for test' 
announcement later).


--
Chuck

--
Unsubscribe info:  http://cygwin.com/ml/#unsubscribe-simple
Problem reports:   http://cygwin.com/problems.html
Documentation: http://cygwin.com/docs.html
FAQ:   http://cygwin.com/faq/



Re: PATCH: login under privileged user != SYSTEM

2008-04-17 Thread Corinna Vinschen
On Apr 17 01:48, Charles Wilson wrote:
 I've been trying to get all the bugs in inetutils-1.5 squashed, and I ran 
 into an issue with rlogin when rlogind was running under a privileged user 
 (that is, not SYSTEM), as is required for Windows Server 2003, 2008, and 
 Vista.

 The problem was, although rsh would honor my .rhosts and allow passwordless 
 operation, rlogin would not. It always asked for my password.

 Internally, rlogind *knew* that the incoming connection was authenticated 
 via .rhosts, so it invoked login thus:

 login -p -h incoming hostname -f -- username

 where the '-f' SHOULD mean this is already authenticated, don't ask for 
 the password again.  But it wasn't working, because login was hardcoded to 
 compare the current uid to 18 (that is, SYSTEM), before allowing 
 passwordless auth.  But rlogind/login were not running under SYSTEM.


 I don't think you can simply replace the code in login, the way we did in 
 many of the servers, tho:

  #ifdef __CYGWIN__
 -#define  ROOT_UID18
 +#define  ROOT_UIDgetuid()
  #else
  #define  ROOT_UID 0
  #endif

 because then you'd allow passwordless auth no matter what account login was 
 running under. Now, it might fail later, assuming we added code to check 
 whether some future setuid() succeeded or not, but I think that's too late 
 in the process.

 So, for *login*, I changed the code from
if (uid == ROOT_UID)
 to
if (is_a_ROOT_UID(uid))

 and implemented a function that, depending on the underlying windows 
 version, either
   (1) compares to 18
   (2) checks that the account with the specified uid has the following 
 privileges:
 +SeAssignPrimaryTokenPrivilege
 +SeCreateTokenPrivilege
 +SeTcbPrivilege
 +SeIncreaseQuotaPrivilege
 +SeServiceLogonRight
 (On NT/2k/XP, uid = 18 is an automatic yes, but if uid != 18, then we 
 fall back to the Vista check-privileges procedure)

 With these changes, I can now get passwordless rlogin when inetd is running 
 under a privileged user, and not SYSTEM.

 Most of the code was adapted from editrights/main.c...

Cool, thanks!  Would you mind to take over login maintainance, too?  It
was always just the wagging tail of inetutils anyway...

Other than that, I'd like to suggest a few minor changes to the patch:

- The SeServiceLogonRight doesn't have to be tested, IMHO.  It doesn't
  have anything to do with user account switching.

- I don't understand why NT4 is handled specially by only checking for the
  uid while 2K and XP get the additional account check if necessary.  None
  of the functions are new with 2K, they all exists since NT 3.51.

- I wouldn't do the automatic yes for uid 18 anymore.  Even for NT/2K/XP
  it would be more correct to check if the current account running the
  process is the one with SID S-1-5-18.  Given that there's already
  so much code for Windows specific privilege checking, I don't think
  it hurts a lot to add something along the lines of

AllocateAndInitializeSid (SECURITY_NT_AUTHORITY, 1, 18, ..., system_sid);
token = OpenProcessToken (GetCurrentProcess ());
user_sid = GetTokenInformation(token, TOKEN_USER);
if (EqualSid (user_sid, system_sid))
  yes
else
  check_privileges


Thanks again,
Corinna

-- 
Corinna Vinschen  Please, send mails regarding Cygwin to
Cygwin Project Co-Leader  cygwin AT cygwin DOT com
Red Hat

--
Unsubscribe info:  http://cygwin.com/ml/#unsubscribe-simple
Problem reports:   http://cygwin.com/problems.html
Documentation: http://cygwin.com/docs.html
FAQ:   http://cygwin.com/faq/



PATCH: login under privileged user != SYSTEM

2008-04-16 Thread Charles Wilson
I've been trying to get all the bugs in inetutils-1.5 squashed, and I 
ran into an issue with rlogin when rlogind was running under a 
privileged user (that is, not SYSTEM), as is required for Windows Server 
2003, 2008, and Vista.


The problem was, although rsh would honor my .rhosts and allow 
passwordless operation, rlogin would not. It always asked for my password.


Internally, rlogind *knew* that the incoming connection was 
authenticated via .rhosts, so it invoked login thus:


login -p -h incoming hostname -f -- username

where the '-f' SHOULD mean this is already authenticated, don't ask for 
the password again.  But it wasn't working, because login was hardcoded 
to compare the current uid to 18 (that is, SYSTEM), before allowing 
passwordless auth.  But rlogind/login were not running under SYSTEM.



I don't think you can simply replace the code in login, the way we did 
in many of the servers, tho:


 #ifdef __CYGWIN__
-#define  ROOT_UID18
+#define  ROOT_UIDgetuid()
 #else
 #define  ROOT_UID 0
 #endif

because then you'd allow passwordless auth no matter what account login 
was running under. Now, it might fail later, assuming we added code to 
check whether some future setuid() succeeded or not, but I think that's 
too late in the process.


So, for *login*, I changed the code from
   if (uid == ROOT_UID)
to
   if (is_a_ROOT_UID(uid))

and implemented a function that, depending on the underlying windows 
version, either

  (1) compares to 18
  (2) checks that the account with the specified uid has the following 
privileges:

+SeAssignPrimaryTokenPrivilege
+SeCreateTokenPrivilege
+SeTcbPrivilege
+SeIncreaseQuotaPrivilege
+SeServiceLogonRight
(On NT/2k/XP, uid = 18 is an automatic yes, but if uid != 18, then we 
fall back to the Vista check-privileges procedure)


With these changes, I can now get passwordless rlogin when inetd is 
running under a privileged user, and not SYSTEM.


Most of the code was adapted from editrights/main.c...

--
Chuck
diff -urN old/login-1.9-7/Makefile new/login-1.9-7/Makefile
--- old/login-1.9-7/Makefile2003-08-11 15:01:41.0 -0400
+++ new/login-1.9-7/Makefile2008-04-17 00:30:15.515625000 -0400
@@ -18,8 +18,8 @@
 #
 
 CFLAGS=-O
-SRCS=  login.c
-OBJS=  login.o
+SRCS=  login.c testWindowsPrivileges.c
+OBJS=  login.o testWindowsPrivileges.o
 MAN=   login.1
 prefix=/usr
 docdir=/usr/share/doc/Cygwin
@@ -27,28 +27,28 @@
 DESTDIR=
 EXEEXT=.exe
 
-all: login${EXEEXT}
+all: login$(EXEEXT)
 
-login${EXEEXT}: login.o
-   ${CC} -o $@ ${CFLAGS} $ -lcrypt
+login$(EXEEXT): login.o testWindowsPrivileges.o
+   $(CC) -o $@ $(CFLAGS) $^ -lcrypt
 
 clean:
-   rm -f ${OBJS} core login${EXEEXT}.stackdump login${EXEEXT}
+   rm -f $(OBJS) core login$(EXEEXT).stackdump login$(EXEEXT)
 
 cleandir: clean
-   rm -f ${MAN} tags .depend
+   rm -f $(MAN) tags .depend
 
-depend: ${SRCS}
-   mkdep -p ${CFLAGS} ${SRCS}
+depend: $(SRCS)
+   mkdep -p $(CFLAGS) $(SRCS)
 
-install: login${EXEEXT}
-   -mkdir -p ${DESTDIR}${prefix}/bin ${DESTDIR}${docdir} 
${DESTDIR}${mandir}
-   install -s -m 4755 login${EXEEXT} ${DESTDIR}${prefix}/bin/login${EXEEXT}
-   install -m 644 login.README ${DESTDIR}${docdir}/login.README
-   install -m 644 login.1 ${DESTDIR}${mandir}/login.1
+install: login$(EXEEXT)
+   -mkdir -p $(DESTDIR)$(prefix)/bin $(DESTDIR)$(docdir) 
$(DESTDIR)$(mandir)
+   install -s -m 4755 login$(EXEEXT) $(DESTDIR)$(prefix)/bin/login$(EXEEXT)
+   install -m 644 login.README $(DESTDIR)$(docdir)/login.README
+   install -m 644 login.1 $(DESTDIR)$(mandir)/login.1
 
-lint: ${SRCS}
-   lint ${CFLAGS} ${SRCS}
+lint: $(SRCS)
+   lint $(CFLAGS) $(SRCS)
 
-tags: ${SRCS}
-   ctags ${SRCS}
+tags: $(SRCS)
+   ctags $(SRCS)
diff -urN old/login-1.9-7/login.README new/login-1.9-7/login.README
--- old/login-1.9-7/login.README2002-08-07 14:14:53.0 -0400
+++ new/login-1.9-7/login.README2008-04-17 01:26:49.96875 -0400
@@ -8,9 +8,29 @@
 ==
 
 This version now supports login w/o password, supported beginning with
-Cygwin version 1.3.6.  Note that this is explicitely allowed for the
-SYSTEM user only!  This change now allows interactive login with rlogin(1)
-and rsh(1) using rhosts authentication.
+Cygwin version 1.3.6.  Note that this is explicitely allowed only in 
+limited cases:
+  (1) NT/2k/XP: SYSTEM user, or a specially privileged user (see below)
+  (2) Windows Server 2003, 2008, and Vista: a specially privileged user
+
+The user account that login is invoked by must have the following 
+privileges:
+SeAssignPrimaryTokenPrivilege
+SeCreateTokenPrivilege
+SeTcbPrivilege
+SeIncreaseQuotaPrivilege
+SeServiceLogonRight
+On NT/2k/XP, the SYSTEM user (aka LocalSystem) has these privileges.
+However, on newer versions of Windows, it