Thread: PwdFilt

  1. #1
    Registered User
    Join Date
    Dec 2008
    Posts
    5

    PwdFilt

    I have downloaded and have been playing around with the PasswordFilters sample which is found in the Microsoft Platform SDK.

    As a VB.NET developer, I am stumped as to how to modify this to allow for a minimum of 1 letter (a-z) or 1 special character (#,$,@,_) AND 1 number. The letters and numbers are not a problem. It is detecting the 4 special characters which I am having problems with.

    Does anybody have any suggestions on how to do this?

    Here is what has been attempted within the PasswordFilter function:

    Code:
          for (i = 0 ; i < cchPassword ; i++)
          {
    
             //
             // keep track of what type of characters we have encountered
             //
    
             if (CharType[i] & C1_DIGIT)
             {
                dwNum = 1;
                continue;
             }
    
             if (CharType[i] & C1_UPPER | C1_LOWER)
             {
                dwChar = 1;
                continue;
             }
    
             if (!(CharType[i] & (C1_ALPHA | C1_DIGIT) ))
             {
                //
                // any other character types make the password complex
                //
    
                    //Password->Buffer is a Pointer and cannot be used in this manner
                    //This is here to demonstrate what I am trying to achieve.
    	switch(Password->Buffer[i])
    	{
    	case '#': case '$': case '@': case '_':
    		dwChar = 1;
    		break;
    	default:
    		break;
    	}
                continue;
    			
             }
          } // for
    Thanks,
    Jody

  2. #2
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    I'm not sure exactly what you are after, but maybe something like this will be useful to peruse:
    Code:
    int special = 0;
    int lower = 0;
    int upper = 0;
    int numeric = 0;
    int x;
    
    for(x = 0; x < length; x ++) {
        if(isupper(string[x])) upper ++;
        else if(islower(string[x])) lower ++;
        else if(isdigit(string[x])) numeric ++;
        else special ++;
    }
    where isupper() and friends are found in <ctype.h>. http://www.sensi.org/~alec/man/man_h/ctype.html
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  3. #3
    Registered User
    Join Date
    Dec 2008
    Posts
    5
    Sorry, I should have been more specific in my first post.

    As previously mentioned, I have downloaded and have been playing around with the PasswordFilters sample which is found in the Microsoft Platform SDK.

    I would like to modify this function to allow for a minimum of 1 letter (a-zA-Z) or 1 special character (#,$,@,_) AND 1 number. The letters and numbers are not a problem. It is detecting the 4 special characters which I am having problems with.

    GetStringTypeW is used to get the character type from a PUNICODE_STRING. If the type is NOT C1_ALPHA or C1_DIGIT, we would like to look at the actual character value (from PUNICODE_STRING) to determine if it is one of (#,$,@,_). If it is NOT any of these characters, then an invalid character has been entered and we would break out of the loop and return FALSE to indicate an invalid password.

    Any other suggestions?

    The full function (unedited) is:
    Code:
    BOOL
    NTAPI
    PasswordFilter(
                  PUNICODE_STRING UserName,
                  PUNICODE_STRING FullName,
                  PUNICODE_STRING Password,
                  BOOL SetOperation
                  )
    /*++
    
    Routine Description:
    
        This (optional) routine is notified of a password change.
    
    Arguments:
    
        UserName - Name of user whose password changed
    
        FullName - Full name of the user whose password changed
    
        NewPassword - Cleartext new password for the user
    
        SetOperation - TRUE if the password was SET rather than CHANGED
    
    Return Value:
    
        TRUE if the specified Password is suitable (complex, long, etc).
         The system will continue to evaluate the password update request
         through any other installed password change packages.
    
        FALSE if the specified Password is unsuitable. The password change
         on the specified account will fail.
    
    --*/
    {
       BOOL bComplex = FALSE; // assume the password in not complex enough
       DWORD cchPassword;
       PWORD CharType;
       DWORD i;
       DWORD dwNum = 0;
       DWORD dwUpper = 0;
       DWORD dwLower = 0;
    
    
       //
       // check if the password is complex enough for our liking by
       // checking that at least two of the four character types are
       // present.
       //
    
       CharType = HeapAlloc(GetProcessHeap(), 0, Password->Length);
       if (CharType == NULL) return FALSE;
    
       cchPassword = Password->Length / sizeof(WCHAR);
    
       if (GetStringTypeW(
                         CT_CTYPE1,
                         Password->Buffer,
                         cchPassword,
                         CharType
                         ))
       {
    
          for (i = 0 ; i < cchPassword ; i++)
          {
    
             //
             // keep track of what type of characters we have encountered
             //
    
             if (CharType[i] & C1_DIGIT)
             {
                dwNum = 1;
                continue;
             }
    
             if (CharType[i] & C1_UPPER)
             {
                dwUpper = 1;
                continue;
             }
    
             if (CharType[i] & C1_LOWER)
             {
                dwLower = 1;
                continue;
             }
    
             if (!(CharType[i] & (C1_ALPHA | C1_DIGIT) ))
             {
    
                //
                // any other character types make the password complex
                //
    
                dwNum = 2;
    
                break;
             }
          } // for
    
          //
          // Indicate whether we encountered enough password complexity
          //
    
          if ( (dwNum + dwUpper + dwLower) >= 2 )
             bComplex = TRUE;
    
          ZeroMemory( CharType, Password->Length );
       } // if
    
       HeapFree(GetProcessHeap(), 0, CharType);
    
       return bComplex;
    }
    Thanks,
    Jody

  4. #4
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    So I think the question then is, what kind of characters you're using? '#', '$', etc. are just plain chars. If you're using wchar_t or tchar, then you would need to use character literals of the same type.

  5. #5
    Registered User
    Join Date
    Dec 2008
    Posts
    5
    From what I understand (through reading and testing), is that this function gets called whenever a user (or admin) attempts to change an Active Directory password. We would like to validate the newly entered password to ensure that it meets our criteria (mentioned above). As a .NET programmer, I am not sure on how to get, and compare, the character values associated with the Password->Buffer pointer.

  6. #6
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    You access bits of a character array like you access bits of any other array. So Password->Buffer[0] is the first character, Password->Buffer[1] is the next, etc.

  7. #7
    Registered User
    Join Date
    Dec 2008
    Posts
    5
    Inside of the loop, Password->Buffer[i] does not work. This is a pointer and not the actual character value. I am unsure on how to get the character to which the pointer is referencing.

  8. #8
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by jgelowitz View Post
    Inside of the loop, Password->Buffer[i] does not work. This is a pointer and not the actual character value. I am unsure on how to get the character to which the pointer is referencing.
    You keep saying this, but it is not in fact true. Password->Buffer is a pointer, yes. Password->Buffer[i] is a character -- specifically, it's a wide character. So, for instance (excuse the mix of C++ and C):
    Code:
    #include <windows.h>
    #include <ntsecapi.h>
    #include <cstdio>
    int main(void) {
    
        PUNICODE_STRING bob = new UNICODE_STRING;
        bob->Buffer = L"This is a string.";
        bob->Length = 17;
        for (int i = 0; i < bob->Length; i++) {
            printf("%lc\n", bob->Buffer[i]);
        }
        system("PAUSE");
        return 0;
    }
    In other words, I don't get any syntax errors from your original code, and as much as I am able to test it it works correctly. If you are having problems, you will have to break down and actually tell us what they are.

  9. #9
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    One possible pitfall:
    Code:
    if (CharType[i] & C1_UPPER | C1_LOWER)
    Since & has higher priority than |, that is interpreted as
    Code:
    if ( ( CharType[i] & C1_UPPER ) | C1_LOWER)
    which is probably not what you want.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  10. #10
    Registered User
    Join Date
    Dec 2008
    Posts
    5
    Thank you all for your comments. With your suggestions, here is a solution which does what we are looking for:

    Code:
    BOOL
    NTAPI
    PasswordFilter(
                  PUNICODE_STRING UserName,
                  PUNICODE_STRING FullName,
                  PUNICODE_STRING Password,
                  BOOL SetOperation
                  )
    {
       BOOL bComplex = FALSE; // assume the password in not complex enough
       DWORD i;
       DWORD dwNum = 0;
       DWORD dwAlpha = 0;
    
        size_t  count;
        char    *pstrPwd = (char *)malloc( Password->Length / sizeof(WCHAR) );
    
        count = wcstombs(pstrPwd, Password->Buffer, Password->Length / sizeof(WCHAR) );
    
    if (count <= 10)
    {
    	for (i = 0; i < count; i++)
    	{
    		if(isalpha(pstrPwd[i]))
    		{
    			dwAlpha = 1;
    		}
    		else if(isdigit(pstrPwd[i]))
    		{
    			dwNum = 1;
    		}
    		else
    		{
    			if (!(pstrPwd[i] == '@' || pstrPwd[i] == '#' || pstrPwd[i] == '$' || pstrPwd[i] == '_'))
    			{
    				dwAlpha = 0;
    				dwNum = 0;
    				break;
    			}
    
    		}
    	}
    
          if ( (dwNum + dwAlpha) >= 2 )
             bComplex = TRUE;
    }
    
    	ZeroMemory(pstrPwd, count);
    
        free(pstrPwd);
    
       return bComplex;
    }

Popular pages Recent additions subscribe to a feed