* Makefile.in (DLL_OFILES): Add setlsapwd.o.

* cygserver.h (CYGWIN_SERVER_VERSION_API): Bump.
	(request_code_t): Define CYGSERVER_REQUEST_SETPWD request type.
	* cygserver_msg.h (client_request_msg::retval): Use default value of -1
	for retval if msglen is 0.
	* cygserver_sem.h (client_request_sem::retval): Ditto.
	* cygserver_shm.h (client_request_shm::retval): Ditto.
	* cygserver_setpwd.h: New file.
	* external.cc (cygwin_internal): Implement new CW_SET_PRIV_KEY type.
	* sec_auth.cc (open_local_policy): Make externally available.
	Get ACCESS_MASK as argument.
	(create_token): Accommodate change to open_local_policy.
	(lsaauth): Ditto.
	(lsaprivkeyauth): New function fetching token by retrieving
	password stored in Cygwin or Interix LSA private data area and
	calling LogonUser with it.
	* security.h (lsaprivkeyauth): Declare.
	(open_local_policy): Declare.
	* setlsapwd.cc: New file implementing setting LSA private data password
	using LsaStorePrivateData or by calling cygserver if available.
	* syscalls.cc (seteuid32): Add workaround to get the original token
	when switching back to the original privileged user, even if
	setgroups group list is still active.  Add long comment to explain why.
	Call lsaprivkeyauth first, only if that fails call lsaauth or
	create_token.
	* include/cygwin/version.h: Bump API minor number.
	* include/sys/cygwin.h (cygwin_getinfo_types): Add CW_SET_PRIV_KEY.
This commit is contained in:
Corinna Vinschen 2008-11-26 10:18:10 +00:00
parent 4163e9fbdb
commit 51303cbd0c
14 changed files with 323 additions and 32 deletions

View File

@ -1,3 +1,33 @@
2008-11-26 Corinna Vinschen <corinna@vinschen.de>
* Makefile.in (DLL_OFILES): Add setlsapwd.o.
* cygserver.h (CYGWIN_SERVER_VERSION_API): Bump.
(request_code_t): Define CYGSERVER_REQUEST_SETPWD request type.
* cygserver_msg.h (client_request_msg::retval): Use default value of -1
for retval if msglen is 0.
* cygserver_sem.h (client_request_sem::retval): Ditto.
* cygserver_shm.h (client_request_shm::retval): Ditto.
* cygserver_setpwd.h: New file.
* external.cc (cygwin_internal): Implement new CW_SET_PRIV_KEY type.
* sec_auth.cc (open_local_policy): Make externally available.
Get ACCESS_MASK as argument.
(create_token): Accommodate change to open_local_policy.
(lsaauth): Ditto.
(lsaprivkeyauth): New function fetching token by retrieving
password stored in Cygwin or Interix LSA private data area and
calling LogonUser with it.
* security.h (lsaprivkeyauth): Declare.
(open_local_policy): Declare.
* setlsapwd.cc: New file implementing setting LSA private data password
using LsaStorePrivateData or by calling cygserver if available.
* syscalls.cc (seteuid32): Add workaround to get the original token
when switching back to the original privileged user, even if
setgroups group list is still active. Add long comment to explain why.
Call lsaprivkeyauth first, only if that fails call lsaauth or
create_token.
* include/cygwin/version.h: Bump API minor number.
* include/sys/cygwin.h (cygwin_getinfo_types): Add CW_SET_PRIV_KEY.
2008-11-21 Corinna Vinschen <corinna@vinschen.de>
* fhandler_floppy.cc (fhandler_dev_floppy::raw_read): Drop

View File

@ -141,11 +141,11 @@ DLL_OFILES:=assert.o autoload.o bsdlib.o ctype.o cxx.o cygheap.o cygthread.o \
nftw.o ntea.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_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 strfuncs.o strptime.o strsep.o \
strsig.o sync.o syscalls.o sysconf.o syslog.o termios.o thread.o \
timer.o times.o tls_pbuf.o tty.o uinfo.o uname.o wait.o wincap.o \
window.o winf.o xsique.o \
sec_helper.o security.o select.o sem.o setlsapwd.o shared.o shm.o \
sigfe.o signal.o sigproc.o smallprint.o spawn.o strace.o strfuncs.o \
strptime.o strsep.o strsig.o sync.o syscalls.o sysconf.o syslog.o \
termios.o thread.o timer.o times.o tls_pbuf.o tty.o uinfo.o uname.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

View File

@ -1,6 +1,6 @@
/* cygserver.h
Copyright 2001, 2002, 2003, 2004 Red Hat Inc.
Copyright 2001, 2002, 2003, 2004, 2008 Red Hat Inc.
Written by Egor Duda <deo@logos-m.ru>
@ -20,7 +20,7 @@ details. */
#endif
#define CYGWIN_SERVER_VERSION_MAJOR 1
#define CYGWIN_SERVER_VERSION_API 3
#define CYGWIN_SERVER_VERSION_API 4
#define CYGWIN_SERVER_VERSION_MINOR 0
#define CYGWIN_SERVER_VERSION_PATCH 0
@ -51,6 +51,7 @@ protected:
CYGSERVER_REQUEST_MSG,
CYGSERVER_REQUEST_SEM,
CYGSERVER_REQUEST_SHM,
CYGSERVER_REQUEST_SETPWD,
CYGSERVER_REQUEST_LAST
} request_code_t;

View File

@ -1,6 +1,6 @@
/* cygserver_msg.h: Single unix specification IPC interface for Cygwin.
Copyright 2003 Red Hat, Inc.
Copyright 2003, 2008 Red Hat, Inc.
This file is part of Cygwin.
@ -75,7 +75,7 @@ public:
client_request_msg (int, const void *, size_t, int); // msgsnd
#endif
int retval () const { return _parameters.out.ret; }
int retval () const { return msglen () ? _parameters.out.ret : -1; }
ssize_t rcvval () const { return _parameters.out.rcv; }
};

View File

@ -1,6 +1,6 @@
/* cygserver_sem.h: Single unix specification IPC interface for Cygwin.
Copyright 2003 Red Hat, Inc.
Copyright 2003, 2008 Red Hat, Inc.
This file is part of Cygwin.
@ -71,7 +71,7 @@ public:
client_request_sem (int, struct sembuf *, size_t); // semop
#endif
int retval () const { return _parameters.out.ret; }
int retval () const { return msglen () ? _parameters.out.ret : -1; }
};
#ifndef __INSIDE_CYGWIN__

View File

@ -0,0 +1,53 @@
/* cygserver_setpwd.h: Set LSA private data password for current user.
Copyright 2008 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. */
#ifndef __CYGSERVER_SETPWD_H__
#define __CYGSERVER_SETPWD_H__
#include <sys/types.h>
#include "cygserver.h"
#define CYGWIN_LSA_KEY_PREFIX L"L$CYGWIN_"
#ifndef __INSIDE_CYGWIN__
class transport_layer_base;
class process_cache;
#endif
class client_request_setpwd : public client_request
{
friend class client_request;
private:
union
{
struct
{
WCHAR passwd[256];
} in;
} _parameters;
#ifndef __INSIDE_CYGWIN__
client_request_setpwd ();
virtual void serve (transport_layer_base *, process_cache *);
#endif
public:
#ifdef __INSIDE_CYGWIN__
client_request_setpwd (PUNICODE_STRING);
#endif
};
#ifdef __INSIDE_CYGWIN__
unsigned long setlsapwd (const char *passwd);
#endif
#endif /* __CYGSERVER_SETPWD_H__ */

View File

@ -1,6 +1,6 @@
/* cygserver_shm.h: Single unix specification IPC interface for Cygwin.
Copyright 2003 Red Hat, Inc.
Copyright 2003, 2008 Red Hat, Inc.
This file is part of Cygwin.
@ -78,7 +78,7 @@ public:
client_request_shm (proc *); // shmfork
#endif
int retval () const { return _parameters.out.ret; }
int retval () const { return msglen () ? _parameters.out.ret : -1; }
void *ptrval () const { return (void *)_parameters.out.ptr; }
vm_object_t objval () const { return _parameters.out.obj; }
};

View File

@ -25,6 +25,7 @@ details. */
#include "cygtls.h"
#include "child_info.h"
#include "environ.h"
#include "cygserver_setpwd.h"
#include <unistd.h>
#include <stdlib.h>
#include <wchar.h>
@ -357,6 +358,11 @@ cygwin_internal (cygwin_getinfo_types t, ...)
dos_file_warning = va_arg (arg, int);
}
break;
case CW_SET_PRIV_KEY:
{
const char *passwd = va_arg (arg, const char *);
return setlsapwd (passwd);
}
default:
break;

View File

@ -335,12 +335,13 @@ details. */
186: Remove ancient V8 regexp functions. Also eliminate old crt0 interface
which provided its own user_data structure.
187: Export cfmakeraw.
188: Export CW_SET_PRIV_KEY.
*/
/* Note that we forgot to bump the api for ualarm, strtoll, strtoull */
#define CYGWIN_VERSION_API_MAJOR 0
#define CYGWIN_VERSION_API_MINOR 187
#define CYGWIN_VERSION_API_MINOR 188
/* There is also a compatibity version number associated with the
shared memory regions. It is incremented when incompatible

View File

@ -140,7 +140,8 @@ typedef enum
CW_DEBUG_SELF,
CW_SYNC_WINENV,
CW_CYGTLS_PADSIZE,
CW_SET_DOS_FILE_WARNING
CW_SET_DOS_FILE_WARNING,
CW_SET_PRIV_KEY
} cygwin_getinfo_types;
#define CW_NEXTPID 0x80000000 /* or with pid to get next one */

View File

@ -27,6 +27,7 @@ details. */
#include <iptypes.h>
#include "pwdgrp.h"
#include "cyglsa.h"
#include "cygserver_setpwd.h"
#include <cygwin/version.h>
extern "C" void
@ -150,13 +151,13 @@ str2uni_cat (UNICODE_STRING &tgt, const char *srcstr)
tgt.Length = tgt.MaximumLength = 0;
}
static LSA_HANDLE
open_local_policy ()
HANDLE
open_local_policy (ACCESS_MASK access)
{
LSA_OBJECT_ATTRIBUTES oa = { 0, 0, 0, 0, 0, 0 };
LSA_HANDLE lsa = INVALID_HANDLE_VALUE;
HANDLE lsa = INVALID_HANDLE_VALUE;
NTSTATUS ret = LsaOpenPolicy (NULL, &oa, POLICY_EXECUTE, &lsa);
NTSTATUS ret = LsaOpenPolicy (NULL, &oa, access, &lsa);
if (ret != STATUS_SUCCESS)
__seterrno_from_win_error (LsaNtStatusToWinError (ret));
return lsa;
@ -785,7 +786,7 @@ create_token (cygsid &usersid, user_groups &new_groups, struct passwd *pw)
push_self_privilege (SE_CREATE_TOKEN_PRIVILEGE, true);
/* Open policy object. */
if ((lsa = open_local_policy ()) == INVALID_HANDLE_VALUE)
if ((lsa = open_local_policy (POLICY_EXECUTE)) == INVALID_HANDLE_VALUE)
goto out;
/* User, owner, primary group. */
@ -963,7 +964,7 @@ lsaauth (cygsid &usersid, user_groups &new_groups, struct passwd *pw)
}
/* Open policy object. */
if ((lsa = open_local_policy ()) == INVALID_HANDLE_VALUE)
if ((lsa = open_local_policy (POLICY_EXECUTE)) == INVALID_HANDLE_VALUE)
goto out;
/* Create origin. */
@ -1172,3 +1173,75 @@ out:
debug_printf ("%p = lsaauth ()", user_token);
return user_token;
}
#define SFU_LSA_KEY_SUFFIX L"_microsoft_sfu_utility"
HANDLE
lsaprivkeyauth (struct passwd *pw)
{
NTSTATUS status;
HANDLE lsa = INVALID_HANDLE_VALUE;
HANDLE token = NULL;
WCHAR sid[256];
WCHAR domain[MAX_DOMAIN_NAME_LEN + 1];
WCHAR user[UNLEN + 1];
WCHAR key_name[MAX_DOMAIN_NAME_LEN + UNLEN + wcslen (SFU_LSA_KEY_SUFFIX) + 2];
UNICODE_STRING key;
PUNICODE_STRING data;
cygsid psid;
push_self_privilege (SE_TCB_PRIVILEGE, true);
/* Open policy object. */
if ((lsa = open_local_policy (POLICY_GET_PRIVATE_INFORMATION))
== INVALID_HANDLE_VALUE)
goto out;
/* Needed for Interix key and LogonUser. */
extract_nt_dom_user (pw, domain, user);
/* First test for a Cygwin entry. */
if (psid.getfrompw (pw) && psid.string (sid))
{
wcpcpy (wcpcpy (key_name, CYGWIN_LSA_KEY_PREFIX), sid);
RtlInitUnicodeString (&key, key_name);
status = LsaRetrievePrivateData (lsa, &key, &data);
if (!NT_SUCCESS (status))
{
/* No Cygwin key, try Interix key. */
if (!*domain)
goto out;
__small_swprintf (key_name, L"%W_%W%W",
domain, user, SFU_LSA_KEY_SUFFIX);
RtlInitUnicodeString (&key, key_name);
status = LsaRetrievePrivateData (lsa, &key, &data);
if (!NT_SUCCESS (status))
goto out;
}
}
/* The key is not 0-terminated. */
PWCHAR passwd = (PWCHAR) alloca (data->Length + sizeof (WCHAR));
*wcpncpy (passwd, data->Buffer, data->Length / sizeof (WCHAR)) = L'\0';
LsaFreeMemory (data);
debug_printf ("Try logon for %W\\%W", domain, user);
if (!LogonUserW (user, domain, passwd, LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, &token))
{
__seterrno ();
token = NULL;
}
else if (!SetHandleInformation (token,
HANDLE_FLAG_INHERIT,
HANDLE_FLAG_INHERIT))
{
__seterrno ();
CloseHandle (token);
token = NULL;
}
out:
close_local_policy (lsa);
pop_self_privilege ();
return token;
}

View File

@ -370,6 +370,8 @@ void __stdcall str2uni_cat (_UNICODE_STRING &, const char *) __attribute__ ((reg
HANDLE create_token (cygsid &usersid, user_groups &groups, struct passwd * pw);
/* LSA authentication function. */
HANDLE lsaauth (cygsid &, user_groups &, struct passwd *);
/* LSA private key storage authentication, same as when using service logons. */
HANDLE lsaprivkeyauth (struct passwd *pw);
/* Verify an existing token */
bool verify_token (HANDLE token, cygsid &usersid, user_groups &groups, bool *pintern = NULL);
/* Get groups of a user */
@ -380,6 +382,8 @@ void extract_nt_dom_user (const struct passwd *pw, PWCHAR domain, PWCHAR user);
/* Get default logonserver for a domain. */
bool get_logon_server (PWCHAR domain, PWCHAR wserver, bool rediscovery);
HANDLE open_local_policy (ACCESS_MASK access);
/* sec_helper.cc: Security helper functions. */
int set_privilege (HANDLE token, DWORD privilege, bool enable);
void set_cygwin_privileges (HANDLE token);

View File

@ -0,0 +1,90 @@
/* setlsapwd.cc: Set LSA private data password for current user.
Copyright 2008 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 "shared_info.h"
#include "cygerrno.h"
#include "path.h"
#include "fhandler.h"
#include "dtable.h"
#include "cygheap.h"
#include "security.h"
#include "cygserver_setpwd.h"
#include "ntdll.h"
#include <ntsecapi.h>
#include <stdlib.h>
#include <wchar.h>
#ifdef USE_SERVER
/*
* client_request_setpwd Constructor
*/
client_request_setpwd::client_request_setpwd (PUNICODE_STRING passwd)
: client_request (CYGSERVER_REQUEST_SETPWD, &_parameters, sizeof (_parameters))
{
memset (_parameters.in.passwd, 0, sizeof _parameters.in.passwd);
if (passwd->Length > 0 && passwd->Length < 256 * sizeof (WCHAR))
wcpncpy (_parameters.in.passwd, passwd->Buffer, 255);
msglen (sizeof (_parameters.in));
}
#endif /* USE_SERVER */
unsigned long
setlsapwd (const char *passwd)
{
unsigned long ret = (unsigned long) -1;
HANDLE lsa = INVALID_HANDLE_VALUE;
WCHAR sid[128];
WCHAR key_name[128 + wcslen (CYGWIN_LSA_KEY_PREFIX)];
PWCHAR data_buf = NULL;
UNICODE_STRING key;
UNICODE_STRING data;
wcpcpy (wcpcpy (key_name, CYGWIN_LSA_KEY_PREFIX),
cygheap->user.get_windows_id (sid));
RtlInitUnicodeString (&key, key_name);
if (!passwd || ! *passwd
|| sys_mbstowcs_alloc (&data_buf, HEAP_NOTHEAP, passwd))
{
NTSTATUS status = STATUS_ACCESS_DENIED;
memset (&data, 0, sizeof data);
if (data_buf)
RtlInitUnicodeString (&data, data_buf);
/* First try it locally. Works for admin accounts. */
if ((lsa = open_local_policy (POLICY_CREATE_SECRET))
!= INVALID_HANDLE_VALUE)
{
status = LsaStorePrivateData (lsa, &key, data.Length ? &data : NULL);
if (NT_SUCCESS (status))
ret = 0;
LsaClose (lsa);
}
if (ret)
#ifdef USE_SERVER
{
/* If that fails, ask cygserver. */
client_request_setpwd request (&data);
if (request.make_request () == -1 || request.error_code ())
set_errno (request.error_code ());
else
ret = 0;
}
#else
__seterrno_from_nt_status (status);
#endif
if (data_buf)
free (data_buf);
}
return ret;
}

View File

@ -2493,7 +2493,23 @@ seteuid32 (__uid32_t uid)
cygheap->user.deimpersonate ();
/* Verify if the process token is suitable. */
if (verify_token (hProcToken, usersid, groups))
/* TODO, CV 2008-11-25: The check against saved_sid is a kludge and a
shortcut. We must check if it's really feasible in the long run.
The reason to add this shortcut is this: sshd switches back to the
privileged user running sshd at least twice in the process of
authentication. It calls seteuid first, then setegid. Due to this
order, the setgroups group list is still active when calling seteuid
and verify_token treats the original token of the privileged user as
insufficient. This in turn results in creating a new user token for
the privileged user instead of using the orignal token. This can have
unfortunate side effects. The created token has different group
memberships, different user rights, and misses possible network
credentials.
Therefore we try this shortcut now. When switching back to the
privileged user, we probably always want a correct (aka original)
user token for this privileged user, not only in sshd. */
if ((uid == cygheap->user.saved_uid && usersid == cygheap->user.saved_sid ())
|| verify_token (hProcToken, usersid, groups))
new_token = hProcToken;
/* Verify if the external token is suitable */
else if (cygheap->user.external_token != NO_IMPERSONATION
@ -2514,19 +2530,35 @@ seteuid32 (__uid32_t uid)
debug_printf ("Found token %d", new_token);
/* If no impersonation token is available, try to
authenticate using NtCreateToken () or LSA authentication. */
/* If no impersonation token is available, try to authenticate using
LSA private data stored password, LSA authentication using our own
LSA module, or, as last chance, NtCreateToken. */
if (new_token == INVALID_HANDLE_VALUE)
{
if (!(new_token = lsaauth (usersid, groups, pw_new)))
{
debug_printf ("lsaauth failed, try create_token.");
new_token = create_token (usersid, groups, pw_new);
if (new_token == INVALID_HANDLE_VALUE)
new_token = lsaprivkeyauth (pw_new);
if (new_token)
{
/* We have to verify this token since settings in /etc/group
might render it unusable im terms of group membership. */
if (!verify_token (new_token, usersid, groups))
{
debug_printf ("create_token failed, bail out of here");
cygheap->user.reimpersonate ();
return -1;
CloseHandle (new_token);
new_token = NULL;
}
}
if (!new_token)
{
debug_printf ("lsaprivkeyauth failed, try lsaauth.");
if (!(new_token = lsaauth (usersid, groups, pw_new)))
{
debug_printf ("lsaauth failed, try create_token.");
new_token = create_token (usersid, groups, pw_new);
if (new_token == INVALID_HANDLE_VALUE)
{
debug_printf ("create_token failed, bail out of here");
cygheap->user.reimpersonate ();
return -1;
}
}
}