Skip to content

Instantly share code, notes, and snippets.

@AudriusButkevicius
Created May 11, 2020 23:09
Show Gist options
  • Save AudriusButkevicius/59d94abaec217714569fb74a1f3857cc to your computer and use it in GitHub Desktop.
Save AudriusButkevicius/59d94abaec217714569fb74a1f3857cc to your computer and use it in GitHub Desktop.
package dummy
import (
"errors"
"runtime"
"syscall"
"unsafe"
)
type CredentialsPromptFlag uint32
const (
// Specifies that a user interface will be shown even if the credentials can be returned from an existing credential
// in credential manager. This flag is permitted only if GenericCredentials is also specified.
AlwaysShowUI CredentialsPromptFlag = 0x00080
// Populate the combo box with the prompt for a user name.
CompleteUsername CredentialsPromptFlag = 0x00800
// Do not store credentials or display check boxes. You can pass CREDUI_FLAGS_SHOW_SAVE_CHECK_BOX with this flag to
// display the Save check box only, and the result is returned in the pfSave output parameter.
DoNotPersist CredentialsPromptFlag = 0x00002
// Populate the combo box with user name/password only. Do not display certificates or smart cards in the combo box.
ExcludeCertificates CredentialsPromptFlag = 0x00008
// Specifies that the caller will call CredUIConfirmCredentials after checking to determine whether the returned
// credentials are actually valid. This mechanism ensures that credentials that are not valid are not saved to the
// credential manager. Specify this flag in all cases unless CREDUI_FLAGS_DO_NOT_PERSIST is specified.
ExpectConfirmation CredentialsPromptFlag = 0x20000
// Consider the credentials entered by the user to be generic credentials.
GenericCredentials CredentialsPromptFlag = 0x40000
// Notify the user of insufficient credentials by displaying the "Logon unsuccessful" balloon tip.
IncorrectPassword CredentialsPromptFlag = 0x00001
// Do not allow the user to change the supplied user name.
KeepUsername CredentialsPromptFlag = 0x100000
// Populate the combo box with the password only. Do not allow a user name to be entered.
PasswordOnlyOk CredentialsPromptFlag = 0x00200
// Do not show the Save check box, but the credential is saved as though the box were shown and selected.
Persist CredentialsPromptFlag = 0x01000
// Populate the combo box with local administrators only.Windows XP Home Edition: This flag will filter out the
// well-known Administrator account.
RequestAdministrator CredentialsPromptFlag = 0x00004
// Populate the combo box with certificates and smart cards only. Do not allow a user name to be entered.
RequireCertificate CredentialsPromptFlag = 0x00010
// Populate the combo box with certificates or smart cards only. Do not allow a user name to be entered.
RequireSmartcard CredentialsPromptFlag = 0x00100
// This flag is meaningful only in locating a matching credential to prefill the dialog box, should authentication
// fail. When this flag is specified, wildcard credentials will not be matched. It has no effect when writing a
// credential. CredUI does not create credentials that contain wildcard characters. Any found were either created
// explicitly by the user or created programmatically, as happens when a RAS connection is made.
ServerCredential CredentialsPromptFlag = 0x04000
// If the check box is selected, show the Save check box and return TRUE in the pfSave output parameter, otherwise,
// return FALSE. Check box uses the value in pfSave by default.
ShowSaveCheckBox CredentialsPromptFlag = 0x00040
// The credential is a "runas" credential. The TargetName parameter specifies the name of the command or program
// being run. It is used for prompting purposes only.
UsernameTargetCredentials CredentialsPromptFlag = 0x80000
// Check that the user name is valid.
ValidateUsername CredentialsPromptFlag = 0x00400
sys_ERROR_CANCELLED = 1223
sys_ERROR_INVALID_FLAGS = 1004
sys_ERROR_INVALID_PARAMETER = 87
sys_NO_ERROR = 0
sys_ERROR_NO_SUCH_LOGON_SESSION = 1312
sys_CREDUI_MAX_MESSAGE_LENGTH = 32767
sys_CREDUI_MAX_CAPTION_LENGTH = 128
sys_CREDUI_MAX_GENERIC_TARGET_LENGTH = 32767
sys_CREDUI_MAX_DOMAIN_TARGET_LENGTH = 337
sys_CREDUI_MAX_USERNAME_LENGTH = 513
sys_CREDUI_MAX_PASSWORD_LENGTH = 256
)
var (
procCredUIPromptForCredentials proc = credui.NewProc("CredUIPromptForCredentialsW")
credui = syscall.NewLazyDLL("credui.dll")
ErrCancelled = errors.New("cancelled")
ErrInvalidFlags = errors.New("invalid flags")
ErrInvalidParameter = errors.New("invalid parameter")
ErrNoSuchLogonSession = errors.New("no such logon session")
)
type sys_CREDUI_INFO struct {
Size uint32
ParentWindow syscall.Handle
MessageText *uint16
CaptionText *uint16
Banner syscall.Handle
}
type PromptSettings struct {
Target string
Errno syscall.Errno
Flags CredentialsPromptFlag
Username string
Password string
Save bool
Message string
Caption string
ParentWindow syscall.Handle
BannerImage syscall.Handle
}
type PromptResponse struct {
Username string
Password string
Save bool
}
func createUiInfo(settings PromptSettings) (*sys_CREDUI_INFO, error) {
uiInfo := new(sys_CREDUI_INFO)
uiInfo.ParentWindow = settings.ParentWindow
var err error
if len(settings.Message) > 0 {
if uiInfo.MessageText, err = syscall.UTF16PtrFromString(settings.Message); err != nil {
return nil, err
}
}
if len(settings.Caption) > 0 {
if uiInfo.CaptionText, err = syscall.UTF16PtrFromString(settings.Caption); err != nil {
return nil, err
}
}
uiInfo.Banner = settings.BannerImage
uiInfo.Size = uint32(unsafe.Sizeof(*uiInfo))
return uiInfo, nil
}
func validateSettings(settings PromptSettings) error {
targetMaxLength := sys_CREDUI_MAX_DOMAIN_TARGET_LENGTH
if settings.Flags&GenericCredentials == GenericCredentials {
targetMaxLength = sys_CREDUI_MAX_GENERIC_TARGET_LENGTH
}
if len(settings.Target) > targetMaxLength {
return errors.New("target too long")
}
if len(settings.Username) > sys_CREDUI_MAX_USERNAME_LENGTH {
return errors.New("username too long")
}
if len(settings.Password) > sys_CREDUI_MAX_PASSWORD_LENGTH {
return errors.New("password too long")
}
if len(settings.Message) > sys_CREDUI_MAX_MESSAGE_LENGTH {
return errors.New("message too long")
}
if len(settings.Caption) > sys_CREDUI_MAX_CAPTION_LENGTH {
return errors.New("caption too long")
}
return nil
}
func Prompt(settings PromptSettings) (PromptResponse, error) {
if err := validateSettings(settings); err != nil {
return PromptResponse{}, err
}
targetPtr, err := syscall.UTF16PtrFromString(settings.Target)
if err != nil {
return PromptResponse{}, err
}
uiInfo, err := createUiInfo(settings)
if err != nil {
return PromptResponse{}, err
}
usernameBuf := make([]uint16, sys_CREDUI_MAX_USERNAME_LENGTH, sys_CREDUI_MAX_USERNAME_LENGTH)
if usernameUtf16, err := syscall.UTF16FromString(settings.Username); err != nil {
return PromptResponse{}, err
} else {
copy(usernameBuf, usernameUtf16)
}
passwordBuf := make([]uint16, sys_CREDUI_MAX_PASSWORD_LENGTH, sys_CREDUI_MAX_PASSWORD_LENGTH)
if passwordUtf16, err := syscall.UTF16FromString(settings.Password); err != nil {
return PromptResponse{}, err
} else {
copy(passwordBuf, passwordUtf16)
}
save := settings.Save
response := PromptResponse{}
ret, _, _ := procCredUIPromptForCredentials.Call(
uintptr(unsafe.Pointer(uiInfo)),
uintptr(unsafe.Pointer(targetPtr)),
0,
uintptr(settings.Errno),
uintptr(unsafe.Pointer(&usernameBuf[0])),
uintptr(cap(usernameBuf)),
uintptr(unsafe.Pointer(&passwordBuf[0])),
uintptr(cap(passwordBuf)),
uintptr(unsafe.Pointer(&save)),
uintptr(settings.Flags),
)
response.Username = syscall.UTF16ToString(usernameBuf)
response.Password = syscall.UTF16ToString(passwordBuf)
response.Save = save
switch ret {
case sys_ERROR_CANCELLED:
err = ErrCancelled
case sys_ERROR_INVALID_FLAGS:
err = ErrInvalidFlags
case sys_ERROR_INVALID_PARAMETER:
err = ErrInvalidParameter
case sys_ERROR_NO_SUCH_LOGON_SESSION:
err = ErrNoSuchLogonSession
case sys_NO_ERROR:
err = nil
}
return response, err
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment