Patch: LogonUser.diff This patch is incremental wrt my ComuterName (take 2) patch (http://www.winehq.com/hypermail/wine-patches/2002/11/0080.html) in the documentation subdirectory. Also fixes an error I made in that patch in wine.conf.man. Modified files: wine: configure.ac configure (not included in the patch) wine/dlls/advapi32: Makefile.in advapi.c security.c advapi32.spec wine/include: config.h.in winbase.h wine/documentation: configuring.sgml wine.conf.man wine/documentation/samples: config Log Message: Martin Wilck <martin.wilck@fujitsu-siemens.com> Implement LogonUser() using an interface between Wine and the PAM library. Normally, this will validate Unix username/password pairs. If PAM is suitably configured (for using winbind), usernames and passwords can be obtained from a Windows Domain controller. Introduce PAM related configuration options. Differences to my previous Logonuser patch: - configuration options instead of hard-coded defaults - documentation update - LogonUser() now in security.c diff -ruNX ignore CVS/wine/configure.ac TMP/wine/configure.ac --- CVS/wine/configure.ac Fri Nov 8 10:45:07 2002 +++ TMP/wine/configure.ac Fri Nov 8 10:45:59 2002 @@ -621,6 +621,13 @@ [AUDIOIOLIBS="-laudioio" AC_DEFINE(HAVE_LIBAUDIOIO, 1, [Define if you have libaudioIO])])]) +dnl **** Check for PAM **** +AC_SUBST(PAMLIBS,"") +AC_CHECK_HEADERS(security/pam_appl.h, + [AC_CHECK_LIB(pam,pam_start, + [AC_DEFINE(HAVE_PAM,1,[Define if you have PAM including devel headers]) + PAMLIBS="-lpam"],,)]) + dnl **** Check for broken glibc mmap64 **** AC_CACHE_CHECK( [whether mmap64 works defined as mmap], ac_cv_mmap64_works, diff -ruNX ignore CVS/wine/dlls/advapi32/Makefile.in TMP/wine/dlls/advapi32/Makefile.in --- CVS/wine/dlls/advapi32/Makefile.in Mon Oct 28 09:35:53 2002 +++ TMP/wine/dlls/advapi32/Makefile.in Fri Nov 8 10:45:59 2002 @@ -5,6 +5,7 @@ VPATH = @srcdir@ MODULE = advapi32.dll IMPORTS = kernel32 ntdll +EXTRALIBS = @PAMLIBS@ LDDLLFLAGS = @LDDLLFLAGS@ SYMBOLFILE = $(MODULE).tmp.o diff -ruNX ignore CVS/wine/dlls/advapi32/advapi.c TMP/wine/dlls/advapi32/advapi.c --- CVS/wine/dlls/advapi32/advapi.c Wed Aug 28 13:49:07 2002 +++ TMP/wine/dlls/advapi32/advapi.c Fri Nov 8 14:44:29 2002 @@ -121,3 +121,17 @@ TRACE("stub %s (harmless)\n", debugstr_w(lpMachineName)); return TRUE; } + +/****************************************************************************** + * Init function for advapi32 - needed to properly load the pam library. + */ +BOOL WINAPI DllMain ( HINSTANCE hinst, DWORD reason, LPVOID reserved ) +{ +#if HAVE_PAM + const static char soname[] = "libpam.so"; + if ( reason == DLL_PROCESS_ATTACH + && ! wine_dlopen( soname, RTLD_NOW|RTLD_GLOBAL, NULL, 0 ) ) + ERR ( "error opening %s\n", soname ); +#endif + return TRUE; +} diff -ruNX ignore CVS/wine/dlls/advapi32/advapi32.spec TMP/wine/dlls/advapi32/advapi32.spec --- CVS/wine/dlls/advapi32/advapi32.spec Mon Sep 9 09:28:27 2002 +++ TMP/wine/dlls/advapi32/advapi32.spec Fri Nov 8 14:44:41 2002 @@ -127,8 +127,8 @@ @ stdcall IsValidSecurityDescriptor(ptr) IsValidSecurityDescriptor @ stdcall IsValidSid(ptr) IsValidSid @ stub LockServiceDatabase -@ stub LogonUserA -@ stub LogonUserW +@ stdcall LogonUserA(ptr ptr ptr long long ptr) LogonUserA +@ stdcall LogonUserW(ptr ptr ptr long long ptr) LogonUserW @ stdcall LookupAccountNameA(str str ptr ptr ptr ptr ptr) LookupAccountNameA @ stub LookupAccountNameW @ stdcall LookupAccountSidA(ptr ptr ptr ptr ptr ptr ptr) LookupAccountSidA diff -ruNX ignore CVS/wine/dlls/advapi32/security.c TMP/wine/dlls/advapi32/security.c --- CVS/wine/dlls/advapi32/security.c Mon Oct 28 09:35:53 2002 +++ TMP/wine/dlls/advapi32/security.c Fri Nov 8 15:38:32 2002 @@ -17,8 +17,13 @@ * * FIXME: for all functions thunking down to Rtl* functions: implement SetLastError() */ +#include "config.h" #include <string.h> +#include <stdlib.h> +#if HAVE_PAM +#include <security/pam_appl.h> +#endif #include "windef.h" #include "winerror.h" @@ -1093,3 +1098,288 @@ *pfResult=TRUE; return TRUE; } + +#if HAVE_PAM +/****************************************************************************** + * Helper functions for the PAM interface. + */ + +/* FIXME: These should be made configurable options, at least the separator. */ +#define WINE_PAM_SERVICE "wine" +/* This corresponds to the "winbind separator" setting in smb.conf. */ +#define WINE_WINBIND_SEPARATOR '\\' +#define PAM_FAIL(x) ((x) != PAM_SUCCESS) + +/****************************************************************************** + * PAM_error_to_dos [INTERNAL] + */ +static int PAM_error_to_dos ( pam_handle_t *pamh, int retcode ) +{ + if ( pamh ) + TRACE( "PAM error: %s\n", pam_strerror ( pamh, retcode ) ); + switch ( retcode ) + { + case PAM_SUCCESS: return ERROR_SUCCESS; + case PAM_AUTH_ERR: return ERROR_ACCESS_DENIED; + default: return ERROR_GEN_FAILURE; + } +} + +/****************************************************************************** + * PAM_logon_user_conv [INTERNAL] + * The "PAM conversation callback" for LogonUser(). + */ +static int PAM_logon_user_conv ( int nmsg, const struct pam_message **msg, + struct pam_response **resp, void *ptr ) +{ + int i; + struct pam_response *myresp; + + /* PAM will free myresp and the response buffers when done. */ + myresp = malloc ( nmsg * sizeof (struct pam_response) ); + if (!myresp) return PAM_BUF_ERR; + memset ( myresp, 0, nmsg * sizeof (struct pam_response) ); + + /* Fill in our password as "response" to the prompt we never show. */ + for (i = 0; i < nmsg; i++) + { + if ( msg[i]->msg_style == PAM_PROMPT_ECHO_OFF ) + { + myresp[i].resp = strdup (ptr); + break; + } + } + *resp = myresp; + return PAM_SUCCESS; +} + +/****************************************************************************** + * PAM_get_config [INTERNAL] + * - Read the PAM configuration from Wine config. + * - Servicename is a newly allocated string, must be freed by caller. + */ +static BOOL PAM_get_config ( char **servicename, char *separator ) +{ + const static char keyname[] = "Software\\Wine\\Wine\\Config\\Logon"; + const static char pamname[] = "PamServiceName"; + const static char sepname[] = "WinbindSeparator"; + const static char default_pam[] = "other"; + const static char default_sep = '\\'; + HKEY hkey; + DWORD ret = ERROR_NOT_ENOUGH_MEMORY; + LONG len = 32, slen; + char *buf; + BOOL res = FALSE; + + if ( RegOpenKeyA ( HKEY_LOCAL_MACHINE, keyname, &hkey ) != ERROR_SUCCESS ) + return FALSE; + buf = HeapAlloc ( GetProcessHeap(), 0, len ); + + while ( buf ) + { + ret = RegQueryValueExA ( hkey, pamname, NULL, NULL, buf, &len ); + if ( ret != ERROR_MORE_DATA ) break; + buf = HeapReAlloc ( GetProcessHeap(), 0, buf, len ); + } + if ( !buf ) { + SetLastError ( ERROR_NOT_ENOUGH_MEMORY ); + goto out; + } + else if ( ret == ERROR_FILE_NOT_FOUND ) + strcpy ( buf, default_pam ); + else if ( ret != ERROR_SUCCESS ) goto out; + + slen = strlen ( buf ) + 1; + *servicename = HeapAlloc ( GetProcessHeap(), 0 , slen ); + if ( !*servicename ) { + SetLastError ( ERROR_NOT_ENOUGH_MEMORY ); + goto out; + } + memcpy ( *servicename, buf, slen ); + + ret = RegQueryValueExA ( hkey, sepname, NULL, NULL, buf, &len ); + if ( ret == ERROR_SUCCESS ) + *separator = *buf; + else + *separator = default_sep; + res = TRUE; + +out: + if (buf) HeapFree ( GetProcessHeap(), 0, buf ); + RegCloseKey ( hkey ); + return res; +} + +/******************************************************************************/ +#endif /* HAVE_PAM */ + +/****************************************************************************** + * LogonUserA [ADVAPI32.@] + * + * PARAMS + * lpUserName : (IN) user name + * lpDomain : (IN) domain name (will only work with PAM winbind support) + * lpPassword : (IN) Cleartext password + * dwLogonType : (IN) logon type, IGNORED + * dwLogonProvider : (IN) login provider, IGNORED + * phToken : (OUT) handle to user token, BOGUS + * + * RETURNS + * TRUE on success. + */ +BOOL WINAPI LogonUserA( LPSTR lpUserName, LPSTR lpDomain, LPSTR lpPassword, + DWORD dwLogonType, DWORD dwLogonProvider, LPHANDLE phToken ) +{ +#if ! HAVE_PAM + ERR( "stub - wine needs to be compiled against PAM library\n" ); + SetLastError ( ERROR_NOT_SUPPORTED ); + return FALSE; +#else + + char *pam_service = NULL; + char winbind_separator; + int ret; + char *user = NULL; + struct pam_conv conv = { PAM_logon_user_conv, NULL }; + pam_handle_t *pamh = NULL; + + FIXME( "returned token handle will be bogus!\n" ); + FIXME( "ignoring type (%lx) and provider (%lx)\n", dwLogonType, dwLogonProvider ); + TRACE( "user %s, domain %s\n", lpUserName, lpDomain ); + + if ( !lpUserName || !lpPassword || !phToken ) + { + SetLastError ( ERROR_INVALID_PARAMETER ); + goto abort; + } + + if ( !PAM_get_config ( &pam_service, &winbind_separator ) ) + goto abort; + + if ( !lpDomain ) + /* User name is "user@DNS-name" (UPN format), + * but we don't know how to convert DNS to NT domain name. */ + { + char *p; + FIXME( "UPN format unsupported - trying unqualified name\n" ); + + user = strdup ( lpUserName ); + if ( ! user ) goto outofmem; + p = strchr ( user, '@' ); + if ( p ) *p = '\0'; + } + else if ( *lpDomain == '\0' || ! strcmp ( lpDomain, "." ) ) + { + user = strdup ( lpUserName ); + if ( ! user ) goto outofmem; + } + else + { + int ldom = strlen ( lpDomain ); + int lusr = strlen ( lpUserName ); + user = malloc ( ldom + lusr + 2 ); + if ( ! user ) goto outofmem; + strcpy ( user, lpDomain ); + user[ldom] = winbind_separator; + strcpy ( user + ldom + 1, lpUserName ); + } + TRACE( "PAM sevice %s, user name: %s\n", debugstr_a (pam_service), debugstr_a (user) ); + + conv.appdata_ptr = lpPassword; + if ( PAM_FAIL( ret = pam_start ( pam_service, user, &conv, &pamh )) ) + goto pam_error; + if ( PAM_FAIL( ret = pam_authenticate ( pamh, PAM_SILENT ) ) ) + goto pam_error; + pam_end ( pamh, PAM_SUCCESS ); + + *phToken = (HANDLE) 0xcafe; + HeapFree ( GetProcessHeap(), 0, pam_service ); + free ( user ); + TRACE( "-> successful\n" ); + return TRUE; + +pam_error: + SetLastError ( PAM_error_to_dos ( pamh, ret ) ); + goto abort; + +outofmem: + SetLastError ( ERROR_NOT_ENOUGH_MEMORY ); + +abort: + TRACE( "-> error %lu\n", GetLastError() ); + if ( pam_service ) HeapFree ( GetProcessHeap(), 0, pam_service ); + if ( pamh ) pam_end ( pamh, PAM_SUCCESS ); + if ( user ) free ( user ); + return FALSE; + +#endif /* HAVE_PAM */ +} + +/****************************************************************************** + * LogonUserW [ADVAPI32.@] + * + * PARAMS + * lpUserNameW : (IN) user name + * lpDomainW : (IN) domain name (will only work with PAM winbind support) + * lpPasswordW : (IN) Cleartext password + * dwLogonType : (IN) logon type, IGNORED + * dwLogonProvider : (IN) login provider, IGNORED + * phToken : (OUT) handle to user token, IGNORED + * + * RETURNS + * TRUE on success. + */ +BOOL WINAPI LogonUserW( LPWSTR lpUserNameW, LPWSTR lpDomainW, LPWSTR lpPasswordW, + DWORD dwLogonType, DWORD dwLogonProvider, LPHANDLE phToken ) +{ +#if ! HAVE_PAM + ERR( "stub - wine needs to be compiled against PAM library\n" ); + SetLastError ( ERROR_NOT_SUPPORTED ); + return FALSE; +#else + + int ulen = 0, dlen = 0, plen = 0, ret; + LPSTR buf = NULL; + LPSTR lpUserName, lpPassword, lpDomain; + + if ( !lpUserNameW || !lpPasswordW || !phToken ) + { + SetLastError ( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + ulen = WideCharToMultiByte( CP_ACP, 0, lpUserNameW, -1, NULL, 0, NULL, NULL ); + plen = WideCharToMultiByte( CP_ACP, 0, lpPasswordW, -1, NULL, 0, NULL, NULL ); + if ( lpDomainW ) + dlen = WideCharToMultiByte( CP_ACP, 0, lpDomainW, -1, NULL, 0, NULL, NULL ); + + buf = HeapAlloc ( GetProcessHeap(), 0, ulen + plen + dlen ); + if ( !buf ) + { + SetLastError ( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + + lpUserName = buf; + WideCharToMultiByte( CP_ACP, 0, lpUserNameW, -1, lpUserName, ulen, NULL, NULL ); + + lpPassword = buf + ulen; + WideCharToMultiByte( CP_ACP, 0, lpPasswordW, -1, lpPassword, plen, NULL, NULL ); + + if ( lpDomainW ) + { + lpDomain = lpPassword + plen; + WideCharToMultiByte( CP_ACP, 0, lpDomainW, -1, lpDomain, dlen, NULL, NULL ); + } + else lpDomain = NULL; + + ret = LogonUserA( lpUserName, lpDomain, lpPassword, + dwLogonType, dwLogonProvider, phToken ); + + memset ( buf, 0, ulen + plen ); /* erase password in memory */ + HeapFree ( GetProcessHeap(), 0, buf ); + return ret; + +#endif /* HAVE_PAM */ +} + diff -ruNX ignore CVS/wine/documentation/configuring.sgml TMP/wine/documentation/configuring.sgml --- CVS/wine/documentation/configuring.sgml Fri Nov 8 16:37:45 2002 +++ TMP/wine/documentation/configuring.sgml Fri Nov 8 16:34:56 2002 @@ -968,6 +968,73 @@ </variablelist> </sect3> + <sect3 id="logon-section"> + <title>The [Logon] Section</title> + <para> + [Logon] contains settings related to user authentification. + <emphasis>Note that Wine does not support Windows NT security + concepts.</emphasis> In particular, Wine processes are not allowed to + "change personality", and Wine relies almost exclusively + on its Unix environment for determining file access rights, etc. + </para> + <para> + The logon functionality only enables Windows + programs to check for a valid login name and password from within Wine. + It is up to the Windows or Winelib application to do + something useful with this information. It is up to the system + administrator installing Wine (that is, YOU) to make sure the + application can't harm anything on your Unix system. + <emphasis>NEVER RUN WINE AS ROOT!!</emphasis> + </para> + <para> + The Logon functionality is only available if Wine was compiled against + the PAM (Pluggable Authetification Modules) library. This library is + part of all recent Linux distributions. + </para> + + <variablelist> + <varlistentry> + <term>PamServiceName</term> + <listitem> + <para> + This is the name of the "PAM service" associated with Wine. This name + corresponds to the name of a file in the PAM configuration directory + <filename>/etc/pam.d</filename>. It defaults to the service + <literal>other</literal>, which normally has very restrictive access + rights. You can set <literal>PamServiceName</literal> e.g. to + <literal>login</literal> to use the same settings as for Unix + authentification, or you can define a special PAM service for Wine and + call it e.g. <literal>wine</literal>. See the PAM documentation for details. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>WinbindSeparator</term> + <listitem> + <para> + This setting is useful if the Unix system setup uses the + <literal>pam_winbind</literal> module to authenticate + against a Windows Primary Domain Controller (PDC). + It makes only sense to set this up if your Unix + machine is configured to use <literal>pam_winbind</literal>. + </para> + <para> + When a user + authenticates with a Windows domain name and user name, these names + are concatenated with this character as separator to form a pseudo Unix user + name. This name is then passed to PAM for authentification against the + PDC. It must be set to the same value as the "winbind separator" in + the Samba configuration file <filename>smb.conf</filename>. See + the <command>winbindd(8)</command> manual page for + details. The default value for + <literal>WinbindSeparator</literal> is "\\" (the + backslash character). + </para> + </listitem> + </varlistentry> + </variablelist> + </sect3> + <sect3 id="appdefaults-section"> <title>The [AppDefaults] Section</title> <para> diff -ruNX ignore CVS/wine/documentation/samples/config TMP/wine/documentation/samples/config --- CVS/wine/documentation/samples/config Fri Nov 8 16:37:45 2002 +++ TMP/wine/documentation/samples/config Fri Nov 8 15:40:12 2002 @@ -263,6 +263,15 @@ ;; HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\ComputerName\ComputerName, too. ;"UseDnsComputerName" = "N" +[Logon] +;; If you have PAM in your system, the PAM service name (file name in /etc/pam.d) +;; to use for Wine authentication. Default: "other". +;"PamServiceName" = "wine" +;; Character to separate Windows domain and user name for PAM_winbind queries. +;; Should be set to the same value as the "winbind separator" in smb.conf(5). +;; See man page of winbindd(8). Default: "\\". +;"WinbindSeparator" = "+" + ;; sample AppDefaults entries ;[AppDefaults\\iexplore.exe\\DllOverrides] ;"shlwapi" = "native" diff -ruNX ignore CVS/wine/documentation/wine.conf.man TMP/wine/documentation/wine.conf.man --- CVS/wine/documentation/wine.conf.man Fri Nov 8 16:37:45 2002 +++ TMP/wine/documentation/wine.conf.man Fri Nov 8 16:46:36 2002 @@ -303,15 +303,29 @@ .br Defaults are read all, write to home files. .PP -.B [AppDefaults\\\\\\\\<appname>\\\\\\\\...] -.PP .B [Network] -.br +.PP .I format: """UseDnsComputerName""=""<boolean>""" .br If Y, always override the registry setting for ComputerName with the Unix hostname. .PP +.B [Logon] +.PP +.I format: """PamServiceName""=""<string>""" +.br +default: "other". +.br +Name of the PAM service (file in /etc/pam.d) determining +authentification rules for Wine. +.br +.PP +.I format: """WinbindSeparator""=""<character>""" +.br +default: "\\\\". +.br +Separator between Windows domain and user name. See winbindd(8). +.PP .B [AppDefaults\\\\\\\\<appname>\\\\\\\\...] .PP This section allows specifying application-specific values for diff -ruNX ignore CVS/wine/include/config.h.in TMP/wine/include/config.h.in --- CVS/wine/include/config.h.in Fri Nov 8 10:45:17 2002 +++ TMP/wine/include/config.h.in Fri Nov 8 10:45:59 2002 @@ -320,6 +320,9 @@ /* Define if you have NAS including devel headers */ #undef HAVE_NAS +/* Define if you have PAM including devel headers */ +#undef HAVE_PAM + /* Define to 1 if you have the <ncurses.h> header file. */ #undef HAVE_NCURSES_H diff -ruNX ignore CVS/wine/include/winbase.h TMP/wine/include/winbase.h --- CVS/wine/include/winbase.h Fri Nov 8 16:37:45 2002 +++ TMP/wine/include/winbase.h Fri Nov 8 10:45:59 2002 @@ -1128,6 +1128,16 @@ #define SCS_POSIX_BINARY 4 #define SCS_OS216_BINARY 5 +/* LOGON support APIs */ +#define LOGON32_LOGON_INTERACTIVE 2 +#define LOGON32_LOGON_NETWORK 3 +#define LOGON32_LOGON_BATCH 4 +#define LOGON32_LOGON_SERVICE 5 +#define LOGON32_PROVIDER_DEFAULT 0 +#define LOGON32_PROVIDER_WINNT35 1 +#define LOGON32_PROVIDER_WINNT40 2 +#define LOGON32_PROVIDER_WINNT50 3 + BOOL WINAPI GetBinaryTypeA( LPCSTR lpApplicationName, LPDWORD lpBinaryType ); BOOL WINAPI GetBinaryTypeW( LPCWSTR lpApplicationName, LPDWORD lpBinaryType ); #define GetBinaryType WINELIB_NAME_AW(GetBinaryType) @@ -1375,6 +1385,9 @@ BOOL WINAPI ImpersonateLoggedOnUser(HANDLE); BOOL WINAPI ImpersonateSelf(SECURITY_IMPERSONATION_LEVEL); BOOL WINAPI IsProcessorFeaturePresent(DWORD); +BOOL WINAPI LogonUserA(LPSTR,LPSTR,LPSTR,DWORD,DWORD,PHANDLE); +BOOL WINAPI LogonUserW(LPWSTR,LPWSTR,LPWSTR,DWORD,DWORD,PHANDLE); +#define LogonUser WINELIB_NAME_AW(LogonUser) BOOL WINAPI LookupAccountSidA(LPCSTR,PSID,LPSTR,LPDWORD,LPSTR,LPDWORD,PSID_NAME_USE); BOOL WINAPI LookupAccountSidW(LPCWSTR,PSID,LPWSTR,LPDWORD,LPWSTR,LPDWORD,PSID_NAME_USE); #define LookupAccountSid WINELIB_NAME_AW(LookupAccountSid)