Quick Tip: Validate current user by enforcing him to enter his password

by Thomas Bahn,
assono GmbH, Standort Kiel,

Quick-TippIBM Notes I had a "small" request from a customer last week: He'd be interested in the extended commercial version of our assono Password-Safe. But his internal audit team had the requirement that a user should have to re-enter his Notes client password when opening a document or copying it directly to the clipboard from a view.

There is a @Command for this: ToolsUserLogoff. It just logs the user off, thus you have to combine it with some kind of server access to get the password input dialog to appear, like described here: Forcing user re-entry of passwords for electronic signatures in script.

This approach has three drawbacks:
1. It doesn't work offline, i.e. on a local replica, because it depends on the server contact for the  password input dialog to be opened.
2. I need it to protect the opening of existing documents. When the user just cancels the password input dialog, the document continues to be opened.
3. The formula code must be placed in the QueryOpen event of the form. But I really need to put some LotusScript there.

Thus I had to search further for a solution working in LotusScript. And I found one by Eknori from 2004: @Command(ToolsUserLogoff) in Lotus Script.
It had to be adopted for the current versions of Notes, because the log out key had been changed from F5 to Ctrl-F5.

I still wasn't convinced this would be the best possible solution. Its strictly Windows only and the user keeps logged out, when he cancels the dialog, I don't know, it would work locally etc.

I looked further and found a great idea: Use the Notes C api to access privat portions of the user's ID file, namely the REGGetIDInfoString function with REGIDGetPrivateKey as infoType.

  • This would bring up the password input dialog, but doesn't log off the user.
  • It would work locally without any server contact.
  • I even could determine, if the user cancelled the dialog box, and react, if necessary.

Thank you, Davy Vanherbergen, for
your OpenNTF Code Bin post (from 2003!): Call
notes password prompt from lotusscript
 

I took the idea and implemented it "my
way", using my C api helper functions and letting the user choose
another ID file, if the configured ID file is not his own.


Function
ValidateCurrentUser As
Boolean

        '/**

        '
* validates current user by letting him enter his password


        '
*


        '
* @return  True, if user has successfully entered his password


        '
*


        '
* @author  Thomas Bahn/assono <tbahn@assono.de>


        '
* @version 2014-09-30


        '
*/



        Const
MAXOUTBUFRLEN% = 4096

       

        Dim
idFileName As
String

        Dim
returnCode As
Integer

        Dim
userNameBuffer As
String*MAXUSERNAME

        Dim
actualLen As
Long

        Dim
currentUserName As
String
               

        Dim
outBufrLen As
String*MAXOUTBUFRLEN

       

        If
Not
IsDebugMode() Then
On
Error
GoTo
errorHandler

       

        ValidateCurrentUser
= False

       

        idFileName
= session.GetEnvironmentString("KeyFileName",
True)

        returnCode
= REGGetIDInfoString(idFileName, REGIDGetName, _

         
      userNameBuffer, MAXUSERNAME, actualLen)

        Call
ShowCAPIErrorIfAnyAndEnd(returnCode, "REGGetIDInfo",
_

         
      NULLHANDLE)

       

        currentUserName
= Left(userNameBuffer,
actualLen - 1)
       

        Do
While
session.UserName <> currentUserName

         
      '
ID file configured in notes.ini is not the ID file of the


         
      '
current user


         
      idFileName = uiws.OpenFileDialog(False,
_

         
              "Choose
your ID file:"
, "*.ID|",
_

         
              GetNotesDataDirectory(),
idFileName)(0)

         
      returnCode = REGGetIDInfoString(idFileName,
REGIDGetName, _

         
              userNameBuffer,
MAXUSERNAME, actualLen)

         
      Call
ShowCAPIErrorIfAnyAndEnd(returnCode, "REGGetIDInfo",
_

         
              NULLHANDLE)

         
     

         
      currentUserName = Left(userNameBuffer,
actualLen - 1)

        Loop

       

        returnCode
= REGGetIDInfoString(idFileName, REGIDGetPrivateKey, _

         
              outBufrLen,
MAXOUTBUFRLEN, actualLen)

        If
returnCode = -32355
Then

         
      Exit
Function
' user cancelled
dialog


        Else

         
      Call
ShowCAPIErrorIfAnyAndEnd(returnCode, "REGGetIDInfo",
_

         
              NULLHANDLE)

        End
If


        '
when we get here, the user must have entered his password


        '
successfully


        ValidateCurrentUser
= True

        Exit
Function

       

errorHandler:

        If
HandleError() = RESUME_NEXT_LINE Then
Resume
Next

        Exit
Function

End
Function


GetNotesDataDirectory()
is a helper function to get the name of the Notes data directory. Replace
it by your own function or a String constant.

And adopt the error handling code (IsDebugMode()
and HandleError())
to your standard.

It needs some (Declarations):

Private Const LIBRARY = "Eintrag utils"

' WORD LNPUBLIC OSLoadString(HMODULE hModule, STATUS StringCode, char far *retBuffer, WORD BufferLength);
Declare Function OSLoadString Lib "nnotes" Alias "OSLoadString" (ByVal hModule As Long, ByVal stringCode As Integer, ByVal retBuffer As LMBCS String, ByVal bufferLength As Integer) As Integer

' STATUS LNPUBLIC REGGetIDInfo(char far *IDFileName, WORD InfoType, void far *OutBufr, WORD OutBufrLen, WORD far *ActualLen);
Declare Function REGGetIDInfoString  Lib "nnotes" Alias "REGGetIDInfo" (ByVal idFileName As String, ByVal infoType As Integer, ByVal outBufr As String, ByVal outBufrLen As Integer, actualLen As Long) As Integer
Declare Function REGGetIDInfoBoolean Lib "nnotes" Alias "REGGetIDInfo" (ByVal idFileName As String, ByVal infoType As Integer, ByVal outBufr As Long,   ByVal outBufrLen As Integer, actualLen As Long) As Integer

Const REGIDGetName = 7 ' Data structure returned Is char xx[MAXUSERNAME]
Const REGIDGetPrivateKey = 9 ' Data structure returned Is char xx[xx]


' STATUS LNPUBLIC NSFDbClose(DBHANDLE hDB);
Declare Function NSFDbClose Lib "nnotes.dll" (ByVal hDB As Long) As Integer


Const NOERROR = 0

Const NULLHANDLE = 0&

Const MAXUSERNAME = 256


And two support functions for the C API error handling:

Sub ShowCAPIErrorIfAnyAndEnd(errorCode As Integer, functionName As String, hDB As Long)
        '/**
        ' * shows user the C API error and aborts execution.
        ' *
        ' * @param   errorCode return code of the function's execution
        ' * @param   functionName name of the C API function called
        ' * @param   hDB handle to the open database
        ' *
        ' * @author  Thomas Bahn/assono <tbahn@assono.de>
        ' * @version 2014-07-17
        ' */                
       
        If errorCode = NOERROR Then Exit Sub ' exit if no error occured
       
        If hDB <> 0 Then
                ' if there is a valid handle, try to close database
                Call NSFDbClose(hDB)
        End If
       
        Error Err, "Error in library '" & LIBRARY & "'" & Chr$(10) & _
        "An error has occurred in the C api function '" & _
        functionName & "': "  & Chr$(10) &_
        "Error code: " & Trim$(Str$(errorCode)) & Chr$(10) & _
        "Error text: " & Chr$(10) & GetCAPIErrorMsg(errorCode)
End Sub

Function GetCAPIErrorMsg(errorCode As Integer) As String
        '/**
        ' * gets error message for the C API error.
        ' *
        ' * @param   errorCode return code of the function's execution
        ' * @return  error message for the C API error
        ' *
        ' * @author  Thomas Bahn/assono <tbahn@assono.de>
        ' * @version 2014-07-17
        ' */                
       
        Dim length As Integer
        Dim buffer As String
       
        ' initialize a buffer of adequate length to accept the error string
        buffer = String$(256, 0)
       
        ' get the API error message from the internal Notes/Domino string
        ' tables
        length = OSLoadString(NULLHANDLE, errorCode, buffer, Len(buffer))
        If length > 0 Then
                ' remove any trailing characters from the string and
                ' return it to the caller
                GetCAPIErrorMsg = Left$(buffer, InStr(1,buffer,Chr$(0))-1)
        Else
                ' couldn?t locate the error message in the string tables
                GetCAPIErrorMsg = "Unknown error"
        End If
End Function


Finally place some code like this into the QueryOpen event handler of your form:

If continue Then
        continue = ValidateCurrentUser()
       
        If continue Then
                ' do some stuff if necessary
        End If
End If

Since this code only uses Notes C api calls, it can easily be extended to other platforms than Windows, but for now it is restricted to this operating system.

Event AdminCamp Technical article IBM Notes Security Development

Sie haben Fragen zu diesem Artikel? Kontaktieren Sie uns gerne: blog@assono.de

Do you want an individual solution? Contact us

More interesting entries

Any questions? Contact us.

If you want to know more about our offers, you can contact us at any time. There are several ways to contact us for a non-binding first consultation.

We don’t sell your data. 100% guaranteed. See: Privacy Policy
assono GmbH

Location Kiel (headquarters)
assono GmbH
Lise-Meitner-Straße 1–7
24223 Schwentinental

Location Hamburg
assono GmbH
Bornkampsweg 58
22761 Hamburg

Phone numbers:
Human resources department: +49 4307 900 407
Marketing department: +49 4307 900 402

E-Mail adresses:
contact@assono.de
bewerbung@assono.de