[ This goes to wine-devel too - demonstrates PAM interface ] Patch: LogonUser.diff Against: CVS 2002-09-18 Modified files: wine: configure configure.ac wine/dlls/advapi32: Makefile.in advapi.c advapi32.spec wine/include: config.h.in winbase.h Log Message: Attempt to code 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. It is even possible for system administrators to setup a special authorization scheme for wine by editing "/etc/pam.d/wine". Status: Compiles, simple (non-unicode) test program authenticates sucessfully against Unix and Windows PDC accounts (and fails with wrong password). Fixmes: ** LogonUserW() is untested, and I have little experience with ** Unicode transformations in wine. Be prepared for bugs there. The LICENSE of PAM is rather unclear - on http://sourceforge.net/projects/pam it says "BSD License, GNU General Public License (GPL)". But I expect no problem with Wine linking against it. The flags to LogonUser() are ignored, and the resulting Token is meaningless because wine currently has no concept of security tokens. To make this really useful, more support for security issues needs to be implemented - but this function is (almost) sufficient for me :-) There are no security checks on the parameters (are they null-terminated?), because I'm not sure how to do that (enforce maximum length? If yes, which?). Perhaps the code should have gone into security.c rather than advapi.c. Or does the whole stuff belong somewhere in ntdll? Index: configure =================================================================== RCS file: /home/wine/wine/configure,v [ *** Deleted for better readability *** ] diff -u -r1.340 configure Index: configure.ac =================================================================== RCS file: /home/wine/wine/configure.ac,v retrieving revision 1.77 diff -u -r1.77 configure.ac --- configure.ac 13 Sep 2002 17:54:27 -0000 1.77 +++ configure.ac 18 Sep 2002 19:00:29 -0000 @@ -613,6 +613,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, Index: dlls/advapi32/Makefile.in =================================================================== RCS file: /home/wine/wine/dlls/advapi32/Makefile.in,v retrieving revision 1.15 diff -u -r1.15 Makefile.in --- dlls/advapi32/Makefile.in 9 Aug 2002 01:22:40 -0000 1.15 +++ dlls/advapi32/Makefile.in 18 Sep 2002 19:00:29 -0000 @@ -5,6 +5,7 @@ VPATH = @srcdir@ MODULE = advapi32.dll IMPORTS = kernel32 ntdll +EXTRALIBS = @PAMLIBS@ LDDLLFLAGS = @LDDLLFLAGS@ SYMBOLFILE = $(MODULE).tmp.o Index: dlls/advapi32/advapi.c =================================================================== RCS file: /home/wine/wine/dlls/advapi32/advapi.c,v retrieving revision 1.20 diff -u -r1.20 advapi.c --- dlls/advapi32/advapi.c 27 Aug 2002 18:30:53 -0000 1.20 +++ dlls/advapi32/advapi.c 18 Sep 2002 19:00:29 -0000 @@ -24,6 +24,10 @@ #include <errno.h> #include <stdio.h> #include <string.h> +#include <stdlib.h> +#if HAVE_PAM +#include <security/pam_appl.h> +#endif #include "winbase.h" #include "windef.h" @@ -120,4 +124,240 @@ { TRACE("stub %s (harmless)\n", debugstr_w(lpMachineName)); return TRUE; +} + +/****************************************************************************** + * Init function for advapi32 - needed to properly load the pam library. + */ +BOOL WINAPI advapi32_init ( HINSTANCE hinst, DWORD reason, LPVOID reserved ) +{ +#if HAVE_PAM +#define SONAME_LIBPAM "libpam.so" + switch(reason) + { + case DLL_PROCESS_ATTACH: + if (!wine_dlopen (SONAME_LIBPAM, RTLD_NOW|RTLD_GLOBAL, NULL, 0)) + ERR ( "error opening %s\n", SONAME_LIBPAM ); + break; + } +#endif + 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) + +/* Placeholder for future reasonable error handling */ +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; + } +} + +/* 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; +} +/******************************************************************************/ +#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, IGNORED + * + * 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 + + int err = ERROR_NOT_SUPPORTED, ret; + char *user = NULL; + struct pam_conv conv = { PAM_logon_user_conv, NULL }; + pam_handle_t *pamh = NULL; + + TRACE( "user %s, domain %s\n", lpUserName, lpDomain ); + FIXME( "ignoring type (%lx) and provider (%lx), returned token handle will be bogus!\n", + dwLogonType, dwLogonProvider ); + + if ( !lpUserName || !lpPassword || !phToken ) + { + err = ERROR_INVALID_PARAMETER; + 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] = WINE_WINBIND_SEPARATOR; + strcpy ( user + ldom + 1, lpUserName ); + } + TRACE( "PAM user name: %s\n", user ); + + conv.appdata_ptr = lpPassword; + if ( PAM_FAIL( ret = pam_start ( WINE_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 = 0xcafe; + free ( user ); + + TRACE( "-> logon successful\n" ); + return TRUE; + +pam_error: + err = PAM_error_to_dos ( pamh, ret ); + goto abort; + +outofmem: + err = ERROR_NOT_ENOUGH_MEMORY; + +abort: + TRACE( "-> error %x\n", err ); + if ( pamh ) pam_end ( pamh, PAM_SUCCESS ); + if ( user ) free ( user ); + SetLastError ( err ); + 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 = malloc ( 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 + dlen ); + free ( buf ); + return ret; + +#endif /* HAVE_PAM */ } Index: dlls/advapi32/advapi32.spec =================================================================== RCS file: /home/wine/wine/dlls/advapi32/advapi32.spec,v retrieving revision 1.28 diff -u -r1.28 advapi32.spec --- dlls/advapi32/advapi32.spec 6 Sep 2002 19:36:37 -0000 1.28 +++ dlls/advapi32/advapi32.spec 18 Sep 2002 19:00:29 -0000 @@ -1,3 +1,5 @@ +init advapi32_init + @ stdcall AbortSystemShutdownA(ptr) AbortSystemShutdownA @ stdcall AbortSystemShutdownW(ptr) AbortSystemShutdownW @ stdcall AccessCheck(ptr long long ptr ptr ptr ptr ptr) AccessCheck @@ -127,8 +129,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 Index: include/config.h.in =================================================================== RCS file: /home/wine/wine/include/config.h.in,v retrieving revision 1.129 diff -u -r1.129 config.h.in --- include/config.h.in 29 Aug 2002 01:51:32 -0000 1.129 +++ include/config.h.in 18 Sep 2002 19:00:33 -0000 @@ -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 Index: include/winbase.h =================================================================== RCS file: /home/wine/wine/include/winbase.h,v retrieving revision 1.158 diff -u -r1.158 winbase.h --- include/winbase.h 17 Sep 2002 18:54:42 -0000 1.158 +++ include/winbase.h 18 Sep 2002 19:00:33 -0000 @@ -1123,6 +1123,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) @@ -1364,6 +1374,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) -- Martin Wilck Phone: +49 5251 8 15113 Fujitsu Siemens Computers Fax: +49 5251 8 20409 Heinz-Nixdorf-Ring 1 mailto:Martin.Wilck@Fujitsu-Siemens.com D-33106 Paderborn http://www.fujitsu-siemens.com/primergy