05/11/2007

HEROES: ovvero quando gli agenti LotusScript hanno i superpoteri...

In seguito ad una chiacchierata con un amico di DominoBaloney, nonchè redattore un pò pigro... ;-) , mi sono ricordato di una cosa buffa che però non è raro vedere presso i clienti:
il server Domino avviato NON come servizio di Windows, ma semplicemente come utente Administrator di rete del SO Windows!

Segue una chiacchierata fittizia per rendere meglio la situazione:
P1 - "Non sarebbe più comodo/corretto lanciare il server Domino come un servizio Windows?"

P2 - "Certo, ma poi abbiamo alcuni agenti LotusScript che non potrebbero accedere a risorse di rete per leggere e/o copiare files , Domino 'gira' come utente SYSTEM e quindi non riesce a vedere le risorse..."

P1 - "Ma per lanciare Domino come Administrator devi loggarti prima con quell'utente e POI lanciare il server! Oppure puoi adottare la tecnica dell'auto-logon di Windows, ma non piace molto..."

P2 - "Non abbiamo trovato altre strade possibile: non è una soluzione "bella" ma funziona! E poi chi vuoi che acceda alla sala server..."

Sorpresa! Mesi fa ho scovato la soluzione presso uno dei tanti forum tecnici.

In poche parole, un agent LotusScript deve richiamare alcune API del sistema operativo per far in modo che temporaneamente il codice "giri" sotto le mentite spoglie di uno specifico utente Windows. Si realizza quindi una sorta di "swith ID del mondo Notes" o un "comando su del mondo unix/linux", per poi tornare "indietro" prima che l'agent termini l'esecuzione...

N.B.
Ho utilizzato queste API di Windows presso alcuni clienti e tutto procede bene da diversi mesi.
Nella parte critica del codice, ovvero fra il cambio utente e il suo ripristino, ho sempre preferito inserire codice semplice e breve, per es. FileCopy(.., ...), e riferirmi alle risorse di rete nel formato UNC ( es. \\nomeserver\condivisione\miofile.est).

Per correttezza vi riporto il commento dell'autore (Paul Ray?) del post e un esempio Lotusscript di utilizzo.

"By default, you run under the security context of the LocalSystem account when you setup Domino to run as a service (I assume this is what you are doing). If you still want to see the server console window, and keep yourself from doing any registry hacks, there are some Win32 API functions you can call to get around this."

"Basically, all you need to do is use these functions in your agent to get a temporary security token for a particular NT account, and use that token to impersonate a user. In doing so, you can tell your agent to perform some function (such as writing to a file on a remote machine) under the security context of another user.

The code is actually rather simple. Below is a sample scheduled agent that you can modify to suit your needs. In the call to LogonUser(), simply fill in the:

1. NT account name you wish to impersonate,
2. The NT domain to login to, and
3. The password for the account you are logging in under.

(Note: this agent assumes there is a mapped "P" drive.) "
' (Declarations) -- see MSDN for more details on these functions
Declare Function LogonUser Lib "advapi32.dll" Alias "LogonUserA" (_
Byval lpszUsername As String, _
Byval lpszDomain As String, _
Byval lpszPassword As String, _
Byval dwLogonType As Long, _
Byval dwLogonProvider As Long, _
phToken As Long _
) As Long

Declare Function ImpersonateLoggedOnUser Lib "advapi32.dll" Alias "ImpersonateLoggedOnUser" ( _
Byval hToken As Long _
) As Long

Declare Function RevertToSelf Lib "advapi32.dll" Alias "RevertToSelf" () As Long

Declare Function CloseHandle Lib "kernel32.dll" Alias "CloseHandle" ( _
Byval hObject As Long _
) As Long

Const LOGON32_PROVIDER_DEFAULT = 0
Const LOGON32_LOGON_INTERACTIVE = 2

Sub Initialize
%REM
This agent shows how to fetch a security token for a particular NT account, and impersonate that account. In
doing so, the agent can access files on remote machines under the security context of another NT account. Note
that impersonation only occurs in the current thread, and concludes when the current thread terminates.

Copyright 2001 Paul Ray. Use at your own risk.
%END REM


Dim hToken&
Dim file%

' get a security token for the specified NT account
If LogonUser("", "", "", LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, hToken&) = 0 Then
Msgbox "==>LogonUser failed."
Exit Sub
End If

' impersonate the NT user (only in the current thread)
If ImpersonateLoggedOnUser(hToken&) = 0 Then
Msgbox "==>ImpersonateLoggedOnUser failed."
If hToken& <> 0 Then CloseHandle(hToken&)
Exit Sub
End If

' close the handle to the token
If hToken& <> 0 Then CloseHandle(hToken&)

' -------------- INIZIO PARTE "CRITICA"
' create a file on another NT machine and write some text to it
file% = Freefile()
Open "P:\COMMPROF.TXT" For Output As file%
Write #file%, "Hello World!"
Close file%
' -------------- FINE PARTE "CRITICA"

' revert back to system account (not really required, as it should automatically revert back when thread terminates)

Call RevertToSelf()

End Sub
This LotusScript was converted to HTML using the ls2html routine,
provided by Julian Robichaux at nsftools.com.

5 comments:

xxx said...

Interessante ... Veramente interessante. Proverò !

Iarin Fabbri said...

Testato e funziona, ma c'è un "ma".
Se l'agente è troppo lungo e Domino durante l'esecuzione dello script a qualche attività, potrebbe dare errori di accesso (perchè in quel momento l'utente è un altro).
Io ho risolto mettendo l'utente switchato come amministratore della macchina.
Qualcuno di voi ha avuto lo stesso problema ?

Piter said...

Io ho provato lo script direttamente sul mio pc tentando di accedere ad una condivisione di rete (\\server\condivisione) con l'utente di dominio NT che ha accesso a tale risorsa. Peccato , NON funziona.
Appena riesco lo provo direttamente sul server visto che Iarin dice che funziona.

Cristian D'Aloisio said...

Se ti può servire, oltre che a me, funziona anche a Vincenzo.

Ritenta, sarai più fortunato ;-)

Vincenzo Capponcelli said...

ho l'ho provato utilizzando il semplice:

filecopy "c:\fattura.pdf" "\\server\dir\fattura.pdf" e funziona.

Ora lo stò testando anche per stampare tramite API di windows su stampanti di rete:

ShellExecute(hwnd, "Print", fname, "", "", 0)

funziona!

Ora ho solo problemi con i file TIF, ma il problema stà nel programma kodak imaging del server W2000 su cui gira.