Windows 로그온 화면을 사용자 정의 방법으로 만들 수 있는 Credential Provider는 

Windows SDK에 포함되어 샘플로 제공되기 때문에 따로 받을 수는 없었다.

(Vista용 Credential Provider 샘플이 별도 다운로드 가능하게 공개되어 있긴 한데, Vista용이라 좀...)


>> 참조: https://www.microsoft.com/en-us/download/details.aspx?id=8279



그런데, 최근에 다시 보니 동일한 샘플이 아래 GitHub에도 올려져 있다. 누군지 몰라도 멋찜!


>> 참조: https://github.com/pauldotknopf/WindowsSDK7-Samples/tree/master/security/credentialproviders



아무튼,

이 샘플들에는 기존 Password Provider를 wrapping하는 방법, 새로 작성하는 방법,

또 CredUI로 동작하게 하는 방법 등등이 들어있는데, 그대로 복사해서 사용하기에 괜찮다.

샘플은 그저 샘플일 뿐인데, 이건 나름 완성도 있게 잘 만들어져 있다.


실제로 Windows 7과 Windows 10에서 동작하는 구조와 순서가 조금 다르긴 하지만

살짝 코드를 손보는 정도로 두 플랫폼에서 거의 동일하게 구현되기 때문에 사용하기에도 좋다.

(예를 들면, Windows 7에서는 Advice와 Unadvice 함수가 아이디 입력란에 타이핑할 때마다

계속 반복 호출되는데, Windows 10에서는 최초 한번만 호출된다. 자세한 차이점은 아래 링크 참조.)


>> 참조: C# WinForm: Windows Credential Provider 개발 관련...



그런데, 이것을 활용해서 OTP용 CP를 만들어 사용하다가 최근에 큰 난관에 봉착하게 되었다.


무슨 난관이냐면...

Windows 로그온 시나리오 중에, 생각치도 않았던 시나리오가 하나 더 있었으니...

바로, Active Directory 관리자 설정에 의해 강제로 암호가 초기화되고 

다음 로그온 시 사용자가 반드시 암호를 변경해야 하는 시나리오가 있었던 것이다!

로그온 이후에 Ctrl+Alt+Del 키를 눌러 암호 변경을 하는 시나리오와는 전혀 다른 상황.




원래 암호 변경 시나리오(CPUS_CHANGE_PASSWORD)는 CP에서 구현 제외되었었고,

해당 부분은 Credential Provider Filter를 통해 기본 Password Provider에서 처리되도록 했었는데,

로그온 시나리오 중에 암호 변경을 해야 하는 상황이 생겨 버려서 Filter로 처리할 수도 없게 된 것.

(Filter는 인증 시나리오 별로 어떤 CP를 사용하게 할 것인지 선택하는 역할을 한다.

그런데 로그온 시나리오에서 암호 변경을 해야 하니 Filter로는 처리가 안될 수밖에...)




세상에 공개된 온갖 자료를 다시 찾기 시작했다.




그런데, 아무리 찾아 헤매도 없었다. 암호 변경 시나리오를 구현한 샘플이나 가이드 자체가...

그나마 쓸모 있는 코드가 포함된 프로젝트는 아래 GitHub의 multiOTP CP.


>> 참조: https://github.com/multiOTP/multiOTPCredentialProvider


helpers.cpp 코드에 KERB_CHANGEPASSWORD_REQUEST 구조체를 사용하는 예제가 들어 있었다.


아싸, 심봤다!


그런데...

해당 프로젝트에서는 그걸 실제 사용하는 암호 변경 시나리오 코드가 없어서 더 이상의 참조는 불가능.

아니, 암호 변경 코드는 만들어 놓고 그걸 사용하는 코드가 없다니... 뭐 이런!!!




또 찾아 헤맸다.

코드 샘플은 없으니, 어떻게 작성하면 되는지 실마리라도 찾아 보려고 여러 검색어로 검색을 시도했다.


그 중에 발견한 하나.


>> 참조: https://social.msdn.microsoft.com/Forums/sqlserver/en-US/ec2986f0-624f-4db5-b4bd-2f79ffff4fdb/how-to-change-password-using-credential-provider?forum=windowssecurity


불행히도 암호 변경 시나리오에 대한 설명이라 로그온 시 강제 암호 변경 시나리오와는 차이가 있지만, 

그나마 좀 도움이 되었다.


그리고 또 하나.


>> 참조: https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/867de829-e031-4f14-8b3e-85b9da07e472/must-change-password-in-icredentialprovider?forum=windowssdk


여기에 핵심적인 설명이 적혀 있었다.

즉, 암호 변경을 처리하고 나면 다시 로그온 화면이 나타나게 되는데, 이때 자동 로그온 처리를 해서 

다시 암호를 입력하지 않고 넘어갈 수 있게 하는 방법이 잘 설명되어 있었다.

(기본 Password Provider에서 처리하는 방식이 그렇다. 그걸 CP로 똑같이 구현하려니...)



위에서 찾은 자료와 도움말을 바탕으로 로그온 시 강제 암호 변경 시나리오를 코드로 작성해 봤다.






결과는... 성공.

히힛.






혼자 만든 것이긴 하지만 우리 회사의 자산이기도 하기 때문에 여기에다 소스코드를 올릴 수는 없고, 

작성한 코드 핵심 부분에 대한 설명만 간략히 덧붙여 본다.


  1. ReportResult(...) 함수에서 로그온 에러 코드를 뒤져서 ntsStatusSTATUS_PASSWORD_MUST_CHANGE이거나, ntsSubStatusSTATUS_PASSWORD_EXPIRED인 경우 암호 변경 화면을 보여 준다. 이 때 전역 플래그 변수 _changepwd_logging_on를 만들어 이후 GetSerialization(...) 함수에서 암호 변경 처리 중인지 판단하는 용도로 활용한다.

  2. 추가로, "암호 복잡도 정책" 때문에 암호 변경이 실패한 경우, 다시 암호 변경 화면을 보여 주어야 하기 때문에 ReportResult(...) 함수에서 해당 처리도 해야 하는데, 이 때는 ntsStatus가 STATUS_PASSWORD_RESTRICTION인지의 여부를 체크하면 된다.

  3. 암호 변경이 성공한 경우에는 다시 로그온 화면으로 돌아가게 되는데 이 때 SetSelected(...) 함수에서 자동 로그온 처리를 해 주어야 한다. 전역 플래그 변수 _changepwd_succeeded를 만들어 활용하며, 로그온 화면으로 가기 전에 새 암호를 암호 입력란에 넣어주는 센스는 기본. 안 그러면 자동 로그온이 될 수 없으니까.

  4. GetSerialization(...) 함수에서는 암호 변경 화면에서 입력값을 받은 후 *pcpgsr을 CPGSR_RETURN_CREDENTIAL_FINISHED로 리턴해야 한다. 그래야 암호 변경이 처리되고 난 다음 다시 로그온 화면으로 진행된다.


끝.





Posted by 떼르미
,


자바스크립트를 허용해주세요!
Please Enable JavaScript![ Enable JavaScript ]