An attempt at LogonUser()

Martin Wilck Martin.Wilck at Fujitsu-Siemens.com
Wed Sep 18 14:35:08 CDT 2002


[ 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 at 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 at Fujitsu-Siemens.com
D-33106 Paderborn           http://www.fujitsu-siemens.com/primergy








More information about the wine-devel mailing list