From f72c2fc3e75a13e3736a275df35597ad43f620fa Mon Sep 17 00:00:00 2001 From: Asanka Herath Date: Wed, 11 Oct 2006 05:32:16 +0000 Subject: [PATCH] windows-netidmgr-plugin-20061010 Secure Endpoints Inc. is contributing its AFS plug-in for KFW 3.1's Network Identity Manager to OpenAFS under an MIT style license. --- src/NTMakefile | 9 +- src/WINNT/install/NSIS/OpenAFS.nsi | 22 +- src/WINNT/install/wix/feature.wxi | 78 +- src/WINNT/install/wix/files.wxi | 36 + src/WINNT/install/wix/lang/en_US/strings.wxl | 4 + src/WINNT/install/wix/openafs.wxs | 9 +- src/WINNT/install/wix/platform.wxi | 5 +- src/WINNT/netidmgr_plugin/NTMakefile | 183 ++ src/WINNT/netidmgr_plugin/afsconfig.c | 36 + src/WINNT/netidmgr_plugin/afsconfigdlg.c | 646 +++++ src/WINNT/netidmgr_plugin/afscred.h | 208 ++ src/WINNT/netidmgr_plugin/afsext.c | 454 ++++ src/WINNT/netidmgr_plugin/afsfuncs.c | 1432 ++++++++++ src/WINNT/netidmgr_plugin/afsfuncs.h | 80 + src/WINNT/netidmgr_plugin/afshelp.c | 70 + src/WINNT/netidmgr_plugin/afsnewcreds.c | 2781 ++++++++++++++++++++ src/WINNT/netidmgr_plugin/afsnewcreds.h | 145 + src/WINNT/netidmgr_plugin/afsp_version.h | 37 + src/WINNT/netidmgr_plugin/afsp_version.h.in | 40 + src/WINNT/netidmgr_plugin/afspext.h | 328 +++ src/WINNT/netidmgr_plugin/afsplugin.c | 642 +++++ src/WINNT/netidmgr_plugin/afsplugin_custom.c | 69 + src/WINNT/netidmgr_plugin/dynimport.c | 463 ++++ src/WINNT/netidmgr_plugin/dynimport.h | 350 +++ src/WINNT/netidmgr_plugin/help/Index.hhk | 9 + src/WINNT/netidmgr_plugin/help/NTMakefile | 37 + src/WINNT/netidmgr_plugin/help/afsplhlp.h | 14 + src/WINNT/netidmgr_plugin/help/afsplhlp.hhp | 29 + src/WINNT/netidmgr_plugin/help/html/afsplhlp.css | 73 + src/WINNT/netidmgr_plugin/help/html/bugs.htm | 24 + .../netidmgr_plugin/help/html/config_service.htm | 39 + src/WINNT/netidmgr_plugin/help/html/copyright.htm | 42 + .../help/html/images/openafs-logo.jpg | Bin 0 -> 35661 bytes .../help/html/images/window_cfg_afs_0.bmp | Bin 0 -> 639858 bytes .../help/html/images/window_cfg_afs_main.bmp | Bin 0 -> 639858 bytes .../help/html/images/window_nc_afs_0.bmp | Bin 0 -> 336950 bytes .../help/html/images/window_nc_afs_1.bmp | Bin 0 -> 17534 bytes .../help/html/images/window_nc_afs_2.bmp | Bin 0 -> 82422 bytes .../help/html/images/window_nc_main.bmp | Bin 0 -> 413190 bytes .../help/html/images/window_nc_prob.bmp | Bin 0 -> 161094 bytes src/WINNT/netidmgr_plugin/help/html/template.htm | 11 + .../netidmgr_plugin/help/html/token_methods.htm | 70 + .../netidmgr_plugin/help/html/tokens_per_id.htm | 189 ++ src/WINNT/netidmgr_plugin/help/html/welcome.htm | 61 + src/WINNT/netidmgr_plugin/help/popups_cfg.txt | 30 + src/WINNT/netidmgr_plugin/help/popups_newcred.txt | 31 + src/WINNT/netidmgr_plugin/help/toc.hhc | 56 + src/WINNT/netidmgr_plugin/images/OpenAFS.ico | Bin 0 -> 31942 bytes src/WINNT/netidmgr_plugin/images/afstoken.ico | Bin 0 -> 1406 bytes src/WINNT/netidmgr_plugin/images/nc_exist.ico | Bin 0 -> 2166 bytes src/WINNT/netidmgr_plugin/images/nc_new.ico | Bin 0 -> 1406 bytes src/WINNT/netidmgr_plugin/images/nc_notowned.ico | Bin 0 -> 2166 bytes src/WINNT/netidmgr_plugin/krb5common.c | 449 ++++ src/WINNT/netidmgr_plugin/krb5common.h | 56 + src/WINNT/netidmgr_plugin/lang/en_us/langres.rc | 273 ++ src/WINNT/netidmgr_plugin/lang/en_us/license.rtf | Bin 0 -> 1850 bytes src/WINNT/netidmgr_plugin/lang/en_us/resource.h | 15 + src/WINNT/netidmgr_plugin/langres.h | 101 + src/WINNT/netidmgr_plugin/main.c | 148 ++ src/WINNT/netidmgr_plugin/params.h | 31 + src/WINNT/netidmgr_plugin/version.rc | 87 + src/config/NTMakefile | 3 + 62 files changed, 9977 insertions(+), 28 deletions(-) create mode 100644 src/WINNT/netidmgr_plugin/NTMakefile create mode 100644 src/WINNT/netidmgr_plugin/afsconfig.c create mode 100644 src/WINNT/netidmgr_plugin/afsconfigdlg.c create mode 100644 src/WINNT/netidmgr_plugin/afscred.h create mode 100644 src/WINNT/netidmgr_plugin/afsext.c create mode 100644 src/WINNT/netidmgr_plugin/afsfuncs.c create mode 100644 src/WINNT/netidmgr_plugin/afsfuncs.h create mode 100644 src/WINNT/netidmgr_plugin/afshelp.c create mode 100644 src/WINNT/netidmgr_plugin/afsnewcreds.c create mode 100644 src/WINNT/netidmgr_plugin/afsnewcreds.h create mode 100644 src/WINNT/netidmgr_plugin/afsp_version.h create mode 100644 src/WINNT/netidmgr_plugin/afsp_version.h.in create mode 100644 src/WINNT/netidmgr_plugin/afspext.h create mode 100644 src/WINNT/netidmgr_plugin/afsplugin.c create mode 100644 src/WINNT/netidmgr_plugin/afsplugin_custom.c create mode 100644 src/WINNT/netidmgr_plugin/dynimport.c create mode 100644 src/WINNT/netidmgr_plugin/dynimport.h create mode 100644 src/WINNT/netidmgr_plugin/help/Index.hhk create mode 100644 src/WINNT/netidmgr_plugin/help/NTMakefile create mode 100644 src/WINNT/netidmgr_plugin/help/afsplhlp.h create mode 100644 src/WINNT/netidmgr_plugin/help/afsplhlp.hhp create mode 100644 src/WINNT/netidmgr_plugin/help/html/afsplhlp.css create mode 100644 src/WINNT/netidmgr_plugin/help/html/bugs.htm create mode 100644 src/WINNT/netidmgr_plugin/help/html/config_service.htm create mode 100644 src/WINNT/netidmgr_plugin/help/html/copyright.htm create mode 100644 src/WINNT/netidmgr_plugin/help/html/images/openafs-logo.jpg create mode 100644 src/WINNT/netidmgr_plugin/help/html/images/window_cfg_afs_0.bmp create mode 100644 src/WINNT/netidmgr_plugin/help/html/images/window_cfg_afs_main.bmp create mode 100644 src/WINNT/netidmgr_plugin/help/html/images/window_nc_afs_0.bmp create mode 100644 src/WINNT/netidmgr_plugin/help/html/images/window_nc_afs_1.bmp create mode 100644 src/WINNT/netidmgr_plugin/help/html/images/window_nc_afs_2.bmp create mode 100644 src/WINNT/netidmgr_plugin/help/html/images/window_nc_main.bmp create mode 100644 src/WINNT/netidmgr_plugin/help/html/images/window_nc_prob.bmp create mode 100644 src/WINNT/netidmgr_plugin/help/html/template.htm create mode 100644 src/WINNT/netidmgr_plugin/help/html/token_methods.htm create mode 100644 src/WINNT/netidmgr_plugin/help/html/tokens_per_id.htm create mode 100644 src/WINNT/netidmgr_plugin/help/html/welcome.htm create mode 100644 src/WINNT/netidmgr_plugin/help/popups_cfg.txt create mode 100644 src/WINNT/netidmgr_plugin/help/popups_newcred.txt create mode 100644 src/WINNT/netidmgr_plugin/help/toc.hhc create mode 100644 src/WINNT/netidmgr_plugin/images/OpenAFS.ico create mode 100644 src/WINNT/netidmgr_plugin/images/afstoken.ico create mode 100644 src/WINNT/netidmgr_plugin/images/nc_exist.ico create mode 100644 src/WINNT/netidmgr_plugin/images/nc_new.ico create mode 100644 src/WINNT/netidmgr_plugin/images/nc_notowned.ico create mode 100644 src/WINNT/netidmgr_plugin/krb5common.c create mode 100644 src/WINNT/netidmgr_plugin/krb5common.h create mode 100644 src/WINNT/netidmgr_plugin/lang/en_us/langres.rc create mode 100644 src/WINNT/netidmgr_plugin/lang/en_us/license.rtf create mode 100644 src/WINNT/netidmgr_plugin/lang/en_us/resource.h create mode 100644 src/WINNT/netidmgr_plugin/langres.h create mode 100644 src/WINNT/netidmgr_plugin/main.c create mode 100644 src/WINNT/netidmgr_plugin/params.h create mode 100644 src/WINNT/netidmgr_plugin/version.rc diff --git a/src/NTMakefile b/src/NTMakefile index 42a6306..f6b59fb 100644 --- a/src/NTMakefile +++ b/src/NTMakefile @@ -563,7 +563,14 @@ aklog: client_creds $(NTMAKE) $(CD) ..\..\.. -extra: aklog +netidmgr_plugin: aklog + echo ***** $@ + $(DOCD) $(SRC)\WINNT\$@ + $(CD) $(SRC)\WINNT\$@ + $(NTMAKE) + $(CD) ..\..\.. + +extra: netidmgr_plugin ! IF EXIST($(SRC)\WINNT\extra) && EXIST($(SRC)\WINNT\extra\NTMakefile) echo ***** $@ $(DOCD) $(SRC)\WINNT\$@ diff --git a/src/WINNT/install/NSIS/OpenAFS.nsi b/src/WINNT/install/NSIS/OpenAFS.nsi index a8823ec..2126146 100644 --- a/src/WINNT/install/NSIS/OpenAFS.nsi +++ b/src/WINNT/install/NSIS/OpenAFS.nsi @@ -85,6 +85,7 @@ VIAddVersionKey "PrivateBuild" "Checked/Debug" !define AFS_COMPANY_NAME "OpenAFS" !define AFS_PRODUCT_NAME "OpenAFS" !define AFS_REGKEY_ROOT "Software\TransarcCorporation" + !define NID_PLUGIN_MGR "Software\MIT\NetIDMgr\PluginManager" CRCCheck force ;Folder selection page @@ -539,7 +540,7 @@ Section "!AFS Client" secClient File "${AFS_CLIENT_BUILDDIR}\afsdacl.exe" File "${AFS_CLIENT_BUILDDIR}\cmdebug.exe" File "${AFS_CLIENT_BUILDDIR}\aklog.exe" - File "${AFS_CLIENT_BUILDDIR}\afscreds.exe" + !insertmacro ReplaceDLL "${AFS_CLIENT_BUILDDIR}\afscreds.exe" "$INSTDIR\Client\Program\afscreds.exe" "$INSTDIR" !insertmacro ReplaceDLL "${AFS_CLIENT_BUILDDIR}\afs_shl_ext.dll" "$INSTDIR\Client\Program\afs_shl_ext.dll" "$INSTDIR" File "${AFS_CLIENT_BUILDDIR}\afsd_service.exe" File "${AFS_CLIENT_BUILDDIR}\symlink.exe" @@ -553,6 +554,9 @@ Section "!AFS Client" secClient File "${AFS_DESTDIR}\etc\rxdebug.exe" File "${AFS_DESTDIR}\etc\backup.exe" !insertmacro ReplaceDLL "${AFS_CLIENT_BUILDDIR}\afs_cpa.cpl" "$INSTDIR\Client\Program\afs_cpa.cpl" "$INSTDIR" + !insertmacro ReplaceDLL "${AFS_CLIENT_BUILDDIR}\afscred.dll" "$INSTDIR\Client\Program\afscred.dll" "$INSTDIR" + !insertmacro ReplaceDLL "${AFS_CLIENT_BUILDDIR}\afscred_en_us.dll" "$INSTDIR\Client\Program\afscred_en_us.dll" "$INSTDIR" + File "${AFS_CLIENT_BUILDDIR}\afsplhlp.chm" SetOutPath "$SYSDIR" !insertmacro ReplaceDLL "${AFS_CLIENT_BUILDDIR}\afslogon.dll" "$SYSDIR\afslogon.dll" "$INSTDIR" @@ -626,6 +630,18 @@ Section "!AFS Client" secClient DeleteRegValue HKLM "${AFS_REGKEY_ROOT}\AFS Client\${AFS_VERSION}" "Debug" !endif + ;NetIDMgr Plug-in Reg Entries + WriteRegStr HKLM "${NID_PLUGIN_MGR}\Modules\OpenAFS" "ImagePath" "$INSTDIR\Client\Program\afscred.dll" + WriteRegStr HKLM "${NID_PLUGIN_MGR}\Modules\OpenAFS" "Description" "OpenAFS Module" + WriteRegStr HKLM "${NID_PLUGIN_MGR}\Modules\OpenAFS" "Vendor" "Secure Endpoints Inc." + WriteRegStr HKLM "${NID_PLUGIN_MGR}\Modules\OpenAFS" "PluginList" "AfsCred" + WriteRegDWORD HKLM "${NID_PLUGIN_MGR}\Modules\OpenAFS" "NoUnload" "1" + + WriteRegStr HKLM "${NID_PLUGIN_MGR}\Plugins\AfsCred" "Module" "OpenAFS" + WriteRegStr HKLM "${NID_PLUGIN_MGR}\Plugins\AfsCred" "Description" "AFS Credentials Provider" + WriteRegStr HKLM "${NID_PLUGIN_MGR}\Plugins\AfsCred" "Dependencies" "Krb5Cred" + WriteRegDWORD HKLM "${NID_PLUGIN_MGR}\Plugins\AfsCred" "Type" "1" + ; On Windows 2000 work around KB301673. This is fixed in Windows XP and 2003 Call GetWindowsVersion Pop $R1 @@ -1133,6 +1149,7 @@ Section /o "Debug symbols" secDebug File "${AFS_DESTDIR}\etc\rxdebug.pdb" File "${AFS_DESTDIR}\etc\backup.pdb" File "${AFS_CLIENT_BUILDDIR}\afs_cpa.pdb" + File "${AFS_CLIENT_BUILDDIR}\afscred.pdb" SetOutPath "$SYSDIR" File "${AFS_CLIENT_BUILDDIR}\afslogon.pdb" @@ -1998,6 +2015,9 @@ StartRemove: DeleteRegKey HKLM "${AFS_REGKEY_ROOT}\AFS Server\CurrentVersion" DeleteRegKey HKLM "${AFS_REGKEY_ROOT}\AFS Server" DeleteRegKey /ifempty HKLM "${AFS_REGKEY_ROOT}" + DeleteRegKey HKLM "${NID_PLUGIN_MGR}\Modules\OpenAFS" + DeleteRegKey HKLM "${NID_PLUGIN_MGR}\Plugins\AfsCred" + DeleteRegKey /ifempty HKLM "Software\MIT\NetIDMgr" DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenAFS" DeleteRegValue HKLM "SYSTEM\CurrentControlSet\Services\NetBT\Parameters" "SmbDeviceEnabled" diff --git a/src/WINNT/install/wix/feature.wxi b/src/WINNT/install/wix/feature.wxi index bb36097..684b68e 100644 --- a/src/WINNT/install/wix/feature.wxi +++ b/src/WINNT/install/wix/feature.wxi @@ -24,33 +24,48 @@ - - - VersionNT = 500 And ServicePackLevel < 3 - + + + + + + + + + + + + + + + + VersionNT = 500 And ServicePackLevel < 3 + - - - CREDSSTARTUP = 0 - + + + CREDSSTARTUP = 0 + - - - - - - - - - + + + + + + + + + - + - + @@ -263,16 +278,31 @@ - - + + - + + + + + + + + + + + + + + diff --git a/src/WINNT/install/wix/files.wxi b/src/WINNT/install/wix/files.wxi index 3d34d2a..9959d17 100644 --- a/src/WINNT/install/wix/files.wxi +++ b/src/WINNT/install/wix/files.wxi @@ -851,6 +851,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/WINNT/install/wix/lang/en_US/strings.wxl b/src/WINNT/install/wix/lang/en_US/strings.wxl index 72a2843..5737a82 100644 --- a/src/WINNT/install/wix/lang/en_US/strings.wxl +++ b/src/WINNT/install/wix/lang/en_US/strings.wxl @@ -27,6 +27,10 @@ Loopback adapter for AFS. It is recommended that you install the loopback adapter if you are installing the client components. OpenAFS command-line utilities and libraries. Tools and libraries + NetIDMgr Plug-in + OpenAFS plug-in for Network Identity Manager. Adds AFS token management capabilities to Network Identity Manager. + Debug symbols for NetIDMgr plug-in + Debugging symbols for the OpenAFS NetIDMgr plug-in. AFS Context Menu Shell Extension Authentication for AFS diff --git a/src/WINNT/install/wix/openafs.wxs b/src/WINNT/install/wix/openafs.wxs index 933f2be..3fde40a 100644 --- a/src/WINNT/install/wix/openafs.wxs +++ b/src/WINNT/install/wix/openafs.wxs @@ -207,7 +207,7 @@ - IBMAFS_UPGRADE OR OPENAFS_UPGRADE + IBMAFS_UPGRADE OR OPENAFS_UPGRADE OR AFSPLUGIN_UPGRADE NSISUNINSTALL <> "" AND UILevel >= 4 NSISUNINSTALL <> "" AND UILevel < 4 @@ -243,6 +243,13 @@ + + + + + + + diff --git a/src/WINNT/install/wix/platform.wxi b/src/WINNT/install/wix/platform.wxi index e8b1d8f..53ccc92 100644 --- a/src/WINNT/install/wix/platform.wxi +++ b/src/WINNT/install/wix/platform.wxi @@ -198,7 +198,10 @@ - + + + + diff --git a/src/WINNT/netidmgr_plugin/NTMakefile b/src/WINNT/netidmgr_plugin/NTMakefile new file mode 100644 index 0000000..ba84673 --- /dev/null +++ b/src/WINNT/netidmgr_plugin/NTMakefile @@ -0,0 +1,183 @@ +# +# Copyright (c) 2005,2006 Secure Endpoints Inc. +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +AFSDEV_AUXRCFLAGS=/i ..\kfw\inc\netidmgr +RELDIR=WINNT\netidmgr_plugin +!INCLUDE ..\..\config\NTMakefile.$(SYS_NAME) +!INCLUDE ..\..\config\NTMakefile.version + +AFSPLUGIN_VERSION_MAJOR=$(AFSPRODUCT_VER_MAJOR) +AFSPLUGIN_VERSION_MINOR=$(AFSPRODUCT_VER_MINOR) +AFSPLUGIN_VERSION_PATCH=$(AFSPRODUCT_VER_PATCH) +AFSPLUGIN_VERSION_AUX =$(AFSPRODUCT_VER_BUILD) + +AFSPLUGIN_VERSION=$(AFSPLUGIN_VERSION_MAJOR).$(AFSPLUGIN_VERSION_MINOR).$(AFSPLUGIN_VERSION_PATCH).$(AFSPLUGIN_VERSION_AUX) +AFSPLUGIN_VERLIST=$(AFSPLUGIN_VERSION_MAJOR),$(AFSPLUGIN_VERSION_MINOR),$(AFSPLUGIN_VERSION_PATCH),$(AFSPLUGIN_VERSION_AUX) + +# short form version numbers +TAFSPLUGIN_VERSION=$(AFSPLUGIN_VERSION_MAJOR).$(AFSPLUGIN_VERSION_MINOR).$(AFSPLUGIN_VERSION_PATCH) + +# Directories + +EXEDIR = $(DESTDIR)\root.client\usr\vice\etc + +INCFILEDIR = $(DESTDIR)\include\WINNT + +LIBFILEDIR = $(DESTDIR)\lib + +KFWINCDIR = ..\kfw\inc + +KFWLIBDIR = ..\kfw\lib\$(CPU) + +# Build environment + +kfwincflags=-I$(KFWINCDIR)\krb5\KerberosIV -I$(KFWINCDIR)\loadfuncs -I$(KFWINCDIR)\krb5 -I$(KFWINCDIR)\netidmgr -I$(KFWINCDIR) +afsincflags=-I$(DESTDIR)\include + +incflags=$(kfwincflags) $(afsincflags) -I. +pldefines=-DUNICODE -D_UNICODE +cdebug=$(cdebug) -Os -Zi +cwarn=$(cwarn) /Wp64 /WX + +plcflags=$(cdebug) $(cflags) $(incflags) $(pldefines) $(cwarn) $(cvarsmt) + +PC2OBJ=$(CC) $(plcflags) /Fo"$@" /c $** + +{}.c{$(OBJ)}.obj: + $(PC2OBJ) + +# Targets + +CHMFILE=$(EXEDIR)\afsplhlp.chm + +DLLFILE=$(EXEDIR)\afscred.dll + +LIBFILE=$(LIBFILEDIR)\afscred.lib + +INCFILES= \ + $(INCFILEDIR)\afspext.h \ + $(INCFILEDIR)\afsp_version.h + +OBJFILES= \ + $(OUT)\afsfuncs.obj \ + $(OUT)\afsplugin.obj \ + $(OUT)\main.obj \ + $(OUT)\afsconfig.obj \ + $(OUT)\afsconfigdlg.obj \ + $(OUT)\afsnewcreds.obj \ + $(OUT)\afsext.obj \ + $(OUT)\afshelp.obj \ + $(OUT)\dynimport.obj \ + $(OUT)\krb5common.obj + +LIBFILES= \ + $(KFWLIBDIR)\nidmgr32.lib + +SDKLIBFILES= \ + $(DESTDIR)\lib\afsauthent.lib \ + $(DESTDIR)\lib\libafsconf.lib \ + $(KFWLIBDIR)\loadfuncs.lib \ + htmlhelp.lib \ + shell32.lib \ + comctl32.lib \ + version.lib \ + shlwapi.lib \ + psapi.lib + +VERRESFILE=$(OUT)\version.res + +$(OUT)\afsfuncs.obj: afsfuncs.c + $(PC2OBJ) + +$(OUT)\afsplugin.obj: afsplugin.c + $(PC2OBJ) + +$(OUT)\main.obj: main.c + $(PC2OBJ) + +$(OUT)\afsconfig.obj: afsconfig.c + $(PC2OBJ) + +$(OUT)\afsconfigdlg.obj: afsconfigdlg.c + $(PC2OBJ) + +$(OUT)\afsnewcreds.obj: afsnewcreds.c + $(PC2OBJ) + +$(OUT)\afsext.obj: afsext.c + $(PC2OBJ) + +$(OUT)\afshelp.obj: afshelp.c + $(PC2OBJ) + +$(OUT)\dynimport.obj: dynimport.c + $(PC2OBJ) + +$(OUT)\krb5common.obj: krb5common.c + $(PC2OBJ) + +!include afsp_version.h.in + +$(DLLFILE): $(OBJFILES) $(VERRESFILE) + $(DLLGUILINK) $(LIBFILES) $(SDKLIBFILES) + $(_VC_MANIFEST_EMBED_DLL) + $(_VC_MANIFEST_CLEAN) + +dummy: + +$(CHMFILE): dummy + $(CD) help + nmake /f NTMakefile install + $(CD) .. + +install: $(INCFILES) $(CHMFILE) $(DLLFILE) lang + +lang:: + +# Repeat this block as necessary redefining LANG for additional +# languages. + +# Begin language block +LANG=en_us + +LANGDLL=$(EXEDIR)\afscred_$(LANG).dll + +lang:: $(LANGDLL) + +$(LANGDLL): $(OUT)\langres_$(LANG).res $(OUT)\langres_ver_$(LANG).res + $(DLLRESLINK) + +$(OUT)\langres_ver_$(LANG).res: version.rc + $(RC) /d LANGVER /d LANG_$(LANG) /fo $@ $** + +$(OUT)\langres_$(LANG).res: lang\$(LANG)\langres.rc + $(RC) /fo $@ $** +# End language block + +clean:: +!ifdef INCFILES + $(DEL) $(INCFILES) +!endif + $(CD) help + $(MAKECMD) /f NTMakefile clean + $(CD) .. diff --git a/src/WINNT/netidmgr_plugin/afsconfig.c b/src/WINNT/netidmgr_plugin/afsconfig.c new file mode 100644 index 0000000..512e2a0 --- /dev/null +++ b/src/WINNT/netidmgr_plugin/afsconfig.c @@ -0,0 +1,36 @@ +/* +This file was autogenerated from: + c:\work\pismere\athena\auth\krb5\src\windows\identity\config\csvschema.cfg + afsconfig.csv + +Do not modify directly. +*/ +#include + +kconf_schema schema_afsconfig[] = { +{L"AfsCred",KC_SPACE,0,L"AFS Credentials Provider"}, +{L"Module",KC_STRING,(khm_int64) L"OpenAFS",L""}, +{L"Description",KC_STRING,(khm_int64) L"AFS Credentials Provider",L""}, +{L"Dependencies",KC_STRING,(khm_int64) L"Krb5Cred",L""}, +{L"Type",KC_INT32,1,L""}, +{L"Flags",KC_INT32,0,L""}, +{L"Cells",KC_STRING,(khm_int64) L"",L"Multi string giving list of cells to authenticate to"}, +{L"Disableafscreds",KC_INT32,0,L"Disable afscreds.exe"}, +{L"Parameters",KC_SPACE,0,L"Parameters for AfsCred"}, +{L"AFSEnabled",KC_INT32,1,L"Is AFS enabled?"}, +{L"LRUCells",KC_STRING,(khm_int64) L"",L"List of recently used cells"}, +{L"LRURealms",KC_STRING,(khm_int64) L"",L"List of recently used realms"}, +{L"DefaultCells",KC_STRING,(khm_int64) L"",L"List of default cells to authenticate to."}, +{L"Parameters",KC_ENDSPACE,0,L""}, +{L"Cells",KC_SPACE,0,L"Per identity cells list and plugin settings"}, +{L"cellname",KC_STRING,(khm_int64) L"identity",L"Cell-identity mapping kept in the plugin configuration for the user."}, +{L"_Schema",KC_SPACE,0,L"Schema is named for each cell to authenticate"}, +{L"MethodName",KC_STRING,(khm_int64) L"",L"Name of method"}, +{L"Method",KC_INT32,0,L"(Deprecated) Method identifier"}, +{L"Realm",KC_STRING,(khm_int64) L"",L"Kerberos realm to use for authentication"}, +{L"_Schema",KC_ENDSPACE,0,L""}, +{L"Cells",KC_ENDSPACE,0,L""}, +{L"AfsCred",KC_ENDSPACE,0,L""} +}; + + diff --git a/src/WINNT/netidmgr_plugin/afsconfigdlg.c b/src/WINNT/netidmgr_plugin/afsconfigdlg.c new file mode 100644 index 0000000..1fbf84b --- /dev/null +++ b/src/WINNT/netidmgr_plugin/afsconfigdlg.c @@ -0,0 +1,646 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include +#include +#include +#include +#include + +/* disable the 'name was marked as deprecated' warnings. These get + issued for Str?Cat? and Str?Cpy? functions. We don't use those + anyway. */ +#pragma warning(push) +#pragma warning(disable: 4995) +#include +#include +#pragma warning(pop) + +#include + +typedef struct tag_afs_ids_dlg_data { + khui_config_init_data cfg; + + khm_boolean afs_enabled; +} afs_ids_dlg_data; + +INT_PTR CALLBACK +afs_cfg_ids_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) { + + afs_ids_dlg_data * d = NULL; + + switch(uMsg) { + case WM_INITDIALOG: + { + khm_int32 t = 1; + + d = PMALLOC(sizeof(*d)); + ZeroMemory(d, sizeof(*d)); + +#pragma warning(push) +#pragma warning(disable: 4244) + SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d); +#pragma warning(pop) + + d->cfg = *((khui_config_init_data *) lParam); + + khc_read_int32(csp_params, L"AFSEnabled", &t); + + d->afs_enabled = !!t; + + CheckDlgButton(hwnd, IDC_CFG_OBTAIN, + (d->afs_enabled)? BST_CHECKED: BST_UNCHECKED); + } + return FALSE; + + case WM_DESTROY: + { + d = (afs_ids_dlg_data *) (LONG_PTR) + GetWindowLongPtr(hwnd, DWLP_USER); + + PFREE(d); + } + return TRUE; + + case WM_COMMAND: + { + d = (afs_ids_dlg_data *) (LONG_PTR) + GetWindowLongPtr(hwnd, DWLP_USER); + + if (wParam == MAKEWPARAM(IDC_CFG_OBTAIN, BN_CLICKED)) { + d->afs_enabled = + (IsDlgButtonChecked(hwnd, IDC_CFG_OBTAIN) == + BST_CHECKED); + khui_cfg_set_flags_inst(&d->cfg, KHUI_CNFLAG_MODIFIED, + KHUI_CNFLAG_MODIFIED); + return TRUE; + } + } + return FALSE; + + case KHUI_WM_CFG_NOTIFY: + { + d = (afs_ids_dlg_data *) (LONG_PTR) + GetWindowLongPtr(hwnd, DWLP_USER); + + if (HIWORD(wParam) == WMCFG_APPLY) { + khm_int32 t; + + if (KHM_FAILED(khc_read_int32(csp_params, + L"AFSEnabled", &t)) || + !!t != !!d->afs_enabled) { + khc_write_int32(csp_params, L"AFSEnabled", + !!d->afs_enabled); + + khui_cfg_set_flags_inst(&d->cfg, + KHUI_CNFLAG_APPLIED, + KHUI_CNFLAG_APPLIED | + KHUI_CNFLAG_MODIFIED); + } else { + khui_cfg_set_flags_inst(&d->cfg, + 0, + KHUI_CNFLAG_MODIFIED); + } + } + } + return TRUE; + } + + return FALSE; +} + +INT_PTR CALLBACK +afs_cfg_id_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) { + + switch(uMsg) { + + case WM_INITDIALOG: + { + INT_PTR rv; + afs_dlg_data * d; + wchar_t idname[KCDB_IDENT_MAXCCH_NAME]; + khm_size cb; + + rv = afs_dlg_proc(hwnd, uMsg, wParam, 0); + + d = (afs_dlg_data *) (LONG_PTR) GetWindowLongPtr(hwnd, DWLP_USER); + + d->cfg = *((khui_config_init_data *) lParam); + + idname[0] = L'\0'; + cb = sizeof(idname); + khui_cfg_get_name(d->cfg.ctx_node, idname, &cb); + + d->ident = NULL; + kcdb_identity_create(idname, 0, &d->ident); + +#ifdef DEBUG + assert(d->ident); +#endif + + d->config_dlg = TRUE; + + afs_cred_get_identity_creds(&d->creds, d->ident, &d->afs_enabled); + + afs_dlg_proc(hwnd, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_DIALOG_SETUP), 0); + + return rv; + } + break; /* not reached */ + + case WM_DESTROY: + { + afs_dlg_data * d; + + d = (afs_dlg_data *) (LONG_PTR) GetWindowLongPtr(hwnd, DWLP_USER); +#ifdef DEBUG + assert(d && d->ident); +#endif + kcdb_identity_release(d->ident); + + return afs_dlg_proc(hwnd, uMsg, wParam, lParam); + } + break; /* not reached */ + + case KHUI_WM_CFG_NOTIFY: + { + afs_dlg_data * d; + + d = (afs_dlg_data *) (LONG_PTR) GetWindowLongPtr(hwnd, DWLP_USER); + + if (HIWORD(wParam) == WMCFG_APPLY) { + afs_cred_write_ident_data(d); + } + } + return TRUE; + + default: + return afs_dlg_proc(hwnd, uMsg, wParam, lParam); + } +} + +static void +set_service_status(HWND hwnd) { + static DWORD wait_start = 0; + DWORD status = 0; + DWORD wait_hint = 0; + int i; + wchar_t status_strings_csv[1024]; + wchar_t status_strings_ms[1024]; + khm_size cb; + wchar_t *t; + + GetServiceStatus(NULL, + TRANSARCAFSDAEMON, + &status, &wait_hint); + + LoadString(hResModule, IDS_CFG_SVCSTATUS, + status_strings_csv, ARRAYLENGTH(status_strings_csv)); + + cb = sizeof(status_strings_ms); + csv_to_multi_string(status_strings_ms, &cb, status_strings_csv); + + for(i=0, t = status_strings_ms; t && *t && *t != L' '; + t = multi_string_next(t), i++) { + if (i == status) + break; + } + + if (!t || !*t) + t = status_strings_ms; /* the first one is "unknown". */ + + SetDlgItemText(hwnd, IDC_CFG_STATUS, t); + + if (status != SERVICE_RUNNING) { + HWND hw; + + hw = GetDlgItem(hwnd, IDC_CFG_STOP); + if (hw == GetFocus()) + SetFocus(GetNextDlgTabItem(hwnd, hw, FALSE)); + + EnableWindow(hw, FALSE); + } else { + EnableWindow(GetDlgItem(hwnd, IDC_CFG_STOP), TRUE); + } + + if (status != SERVICE_STOPPED && + status != SERVICE_PAUSED) { + HWND hw; + + hw = GetDlgItem(hwnd, IDC_CFG_START); + if (hw == GetFocus()) + SetFocus(GetNextDlgTabItem(hwnd, hw, FALSE)); + + EnableWindow(hw, FALSE); + } else { + EnableWindow(GetDlgItem(hwnd, IDC_CFG_START), TRUE); + } + + if (status == SERVICE_START_PENDING || + status == SERVICE_STOP_PENDING) { + HWND hw; + DWORD now; + int progress; + + hw = GetDlgItem(hwnd, IDC_CFG_PROGRESS); +#ifdef DEBUG + assert(hw); +#endif + if (!IsWindowVisible(hw)) + ShowWindow(hw, SW_SHOW); + + if (wait_start == 0) + wait_start = GetTickCount(); + + now = GetTickCount(); + + if (now + wait_hint != wait_start) + progress = (now - wait_start) * 100 / + (now + wait_hint - wait_start); + else + progress = 0; + + SendMessage(hw, PBM_SETPOS, progress, 0); + + SetTimer(hwnd, 1, 500, NULL); + } else { + HWND hw; + + hw = GetDlgItem(hwnd, IDC_CFG_PROGRESS); +#ifdef DEBUG + assert(hw); +#endif + wait_start = 0; + if (IsWindowVisible(hw)) + ShowWindow(hw, SW_HIDE); + } +} + +void +afs_cfg_show_last_error(HWND hwnd, wchar_t * prefix, DWORD code) { + DWORD r; + wchar_t * err_desc = NULL; + wchar_t title[64]; + wchar_t msg[1024]; + wchar_t tmp[128]; + + r = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + code, + 0, + (LPWSTR) &err_desc, + 0, + NULL); + + if (r == 0 || err_desc == NULL) + return; + + LoadString(hResModule, IDS_PLUGIN_DESC, + title, ARRAYLENGTH(title)); + if (prefix == NULL) + tmp[0] = L'\0'; + else if (IS_INTRESOURCE(prefix)) + LoadString(hResModule, (UINT)(UINT_PTR) prefix, + tmp, ARRAYLENGTH(tmp)); + else + StringCbCopy(tmp, sizeof(tmp), prefix); + + StringCbPrintf(msg, sizeof(msg), L"%s%s", + tmp, err_desc); + + MessageBox(hwnd, msg, title, MB_OK | MB_APPLMODAL); + + LocalFree(err_desc); +} + +#define SCNAME_AFSCREDS L"AFS Credentials.lnk" + +BOOL +afs_cfg_get_afscreds_shortcut(wchar_t * wpath) { + HRESULT hr; + BOOL shortcut_found = FALSE; + + hr = SHGetFolderPath(NULL, CSIDL_COMMON_STARTUP, + NULL, SHGFP_TYPE_CURRENT, + wpath); + if (FAILED(hr)) + goto _noshortcut; + + if (!PathAppend(wpath, SCNAME_AFSCREDS)) { + goto _noshortcut; + } + + if (PathFileExists(wpath)) { + shortcut_found = TRUE; + } + + _noshortcut: + + return shortcut_found; +} + +INT_PTR CALLBACK +afs_cfg_main_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) { + switch(uMsg) { + case WM_INITDIALOG: + { + wchar_t imagepath[MAX_PATH]; + wchar_t blockname[MAX_PATH]; + HKEY service_key; + LONG l; + DWORD cb; + DWORD dummy; + LPVOID ver_info; + wchar_t * value; + + struct LANGANDCODEPATH { + WORD wLanguage; + WORD wCodePage; + } *translations; + +#pragma warning(push) +#pragma warning(disable: 4244) + SetWindowLongPtr(hwnd, DWLP_USER, (DWORD_PTR) lParam); +#pragma warning(pop) + + /* Try to figure out if afscreds.exe is on the startup + group for all users. */ + { + khm_handle csp_afscred = NULL; + khm_int32 disable = FALSE; + + if (KHM_SUCCEEDED(kmm_get_plugin_config(AFS_PLUGIN_NAME, + 0, + &csp_afscred))) { + + khc_read_int32(csp_afscred, L"Disableafscreds", + &disable); + + khc_close_space(csp_afscred); + } + + if (!disable) { + CheckDlgButton(hwnd, IDC_CFG_STARTAFSCREDS, + BST_UNCHECKED); + } else { + CheckDlgButton(hwnd, IDC_CFG_STARTAFSCREDS, + BST_CHECKED); + } + } + + l = RegOpenKeyEx(HKEY_LOCAL_MACHINE, + L"SYSTEM\\CurrentControlSet\\Services\\TransarcAFSDaemon", + 0, + KEY_READ, + &service_key); + + if (l != ERROR_SUCCESS) + goto _set_status; + + cb = sizeof(imagepath); + l = RegQueryValueEx(service_key, + L"ImagePath", + NULL, NULL, + (LPBYTE) imagepath, + &cb); + if (l != ERROR_SUCCESS) + goto _close_key; + + PathUnquoteSpaces(imagepath); + + dummy = 1; + cb = GetFileVersionInfoSize(imagepath, &dummy); + if (cb == 0 || dummy) + goto _close_key; + + ver_info = malloc(cb); +#ifdef DEBUG + assert(ver_info); +#endif + if (!ver_info) + goto _close_key; + + if (!GetFileVersionInfo(imagepath, + 0, cb, ver_info)) + goto _free_buffer; + + cb = 0; + if (!VerQueryValue(ver_info, + L"\\VarFileInfo\\Translation", + (LPVOID*) &translations, + &cb) || + cb == 0) + goto _free_buffer; + + StringCbPrintf(blockname, sizeof(blockname), + L"\\StringFileInfo\\%04x%04x\\FileVersion", + translations[0].wLanguage, + translations[0].wCodePage); + + if (!VerQueryValue(ver_info, + blockname, + (LPVOID*) &value, + &cb) || + cb == 0) + goto _free_buffer; + + SetDlgItemText(hwnd, IDC_CFG_VERSION, value); + + StringCbPrintf(blockname, sizeof(blockname), + L"\\StringFileInfo\\%04x%04x\\CompanyName", + translations[0].wLanguage, + translations[0].wCodePage); + + if (!VerQueryValue(ver_info, + blockname, + (LPVOID*) &value, + &cb) || + cb == 0) + goto _free_buffer; + + SetDlgItemText(hwnd, IDC_CFG_COMPANY, value); + + _free_buffer: + free(ver_info); + _close_key: + RegCloseKey(service_key); + _set_status: + set_service_status(hwnd); + } + return FALSE; + + case WM_COMMAND: + switch(wParam) { + case MAKEWPARAM(IDC_CFG_STOP, BN_CLICKED): + { + DWORD r; + + r = ServiceControl(NULL, TRANSARCAFSDAEMON, SERVICE_STOPPED); + + if (r) + afs_cfg_show_last_error(hwnd, + MAKEINTRESOURCE(IDS_CFG_CANTSTOP), + r); + else + set_service_status(hwnd); + } + break; + + case MAKEWPARAM(IDC_CFG_START,BN_CLICKED): + { + DWORD r; + r = ServiceControl(NULL, TRANSARCAFSDAEMON, SERVICE_RUNNING); + + if (r) + afs_cfg_show_last_error(hwnd, + MAKEINTRESOURCE(IDS_CFG_CANTSTART), + r); + else + set_service_status(hwnd); + } + break; + + case MAKEWPARAM(IDC_CFG_CPL, BN_CLICKED): + if (32 >= (LRESULT) ShellExecute (NULL, NULL, + L"AFS_CONFIG.EXE", NULL, + NULL, SW_SHOW)) { + MessageBox(NULL, + L"Can't find file AFS_CONFIG.EXE", + L"Error", MB_OK); + } + break; + + case MAKEWPARAM(IDC_CFG_STARTAFSCREDS, BN_CLICKED): + { + khui_config_node node; + + node = (khui_config_node) (DWORD_PTR) + GetWindowLongPtr(hwnd, DWLP_USER); + + khui_cfg_set_flags(node, + KHUI_CNFLAG_MODIFIED, + KHUI_CNFLAG_MODIFIED); + } + break; + } + return TRUE; + + case KHUI_WM_CFG_NOTIFY: + { + if (HIWORD(wParam) == WMCFG_APPLY) { + wchar_t wpath[MAX_PATH]; + int dlg_state; + khui_config_node node; + khm_handle csp_afscred = NULL; + khm_int32 disable = FALSE; + + node = (khui_config_node) (DWORD_PTR) + GetWindowLongPtr(hwnd, DWLP_USER); + + kmm_get_plugin_config(AFS_PLUGIN_NAME, KHM_PERM_WRITE, + &csp_afscred); + + if (csp_afscred) + khc_read_int32(csp_afscred, L"Disableafscreds", + &disable); + + dlg_state = IsDlgButtonChecked(hwnd, IDC_CFG_STARTAFSCREDS); + + if (!!disable != + (dlg_state == BST_CHECKED)) { + if (csp_afscred) + khc_write_int32(csp_afscred, + L"Disableafscreds", + (dlg_state == BST_CHECKED)); + + khui_cfg_set_flags(node, + KHUI_CNFLAG_APPLIED, + KHUI_CNFLAG_MODIFIED | + KHUI_CNFLAG_APPLIED); + } else { + khui_cfg_set_flags(node, 0, + KHUI_CNFLAG_MODIFIED); + } + + if (dlg_state == BST_CHECKED && + afs_cfg_get_afscreds_shortcut(wpath)) { + + DeleteFile(wpath); + } + } + } + return TRUE; + + case WM_TIMER: + if (wParam == 1) { + KillTimer(hwnd, 1); + set_service_status(hwnd); + } + break; + + case WM_DESTROY: + return FALSE; + + case WM_HELP: + { + static const DWORD ctx_help[] = { + IDC_CFG_STATUS, IDH_SVCSTATUS, + IDC_CFG_STOP, IDH_SVCSTOP, + IDC_CFG_START, IDH_SVCSTART, + IDC_CFG_VERSION, IDH_SVCVERSION, + IDC_CFG_COMPANY, IDH_SVCCOMPANY, + IDC_CFG_CPL, IDH_SVCCPL, + IDC_CFG_STARTAFSCREDS, IDH_STARTAFSCREDS, + 0 + }; + + LPHELPINFO hlp; + + hlp = (LPHELPINFO) lParam; + + if (hlp->iContextType != HELPINFO_WINDOW) + break; + + afs_html_help(hlp->hItemHandle, L"::/popups_cfg.txt", + HH_TP_HELP_WM_HELP, (DWORD_PTR) ctx_help); + } + return TRUE; + } + return FALSE; +} diff --git a/src/WINNT/netidmgr_plugin/afscred.h b/src/WINNT/netidmgr_plugin/afscred.h new file mode 100644 index 0000000..3dad7ab --- /dev/null +++ b/src/WINNT/netidmgr_plugin/afscred.h @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_AFSCRED_H +#define __KHIMAIRA_AFSCRED_H + +#define _WINSOCKAPI_ +#include +#include + +#define KHERR_FACILITY L"AfsCred" +#define KHERR_HMODULE hResModule +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#ifndef NOSTRSAFE +#include +#endif + +#define AFS_PLUGIN_NAME L"AfsCred" +#define AFS_CREDTYPE_NAME L"AfsCred" + +#define AFS_PLUGIN_DEPS L"Krb5Cred\0" + +#define KRB5_CREDTYPE_NAME L"Krb5Cred" +#define KRB4_CREDTYPE_NAME L"Krb4Cred" + +#define AFS_TYPENAME_PRINCIPAL L"AFSPrincipal" +#define AFS_TYPENAME_METHOD L"AFSTokenMethod" +#define AFS_ATTRNAME_CLIENT_PRINC L"AFSClientPrinc" +#define AFS_ATTRNAME_SERVER_PRINC L"AFSServerPrinc" +#define AFS_ATTRNAME_CELL L"AFSCell" +#define AFS_ATTRNAME_METHOD L"AFSMethod" +#define AFS_ATTRNAME_REALM L"AFSRealm" + +#define AFS_VALID_CELL_CHARS L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-" +#define AFS_VALID_REALM_CHARS AFS_VALID_CELL_CHARS + +#define AFS_CONFIG_NODE_IDS L"AfsIdentities" +#define AFS_CONFIG_NODE_ID L"AfsIdentity" +#define AFS_CONFIG_NODE_MAIN L"AfsOptions" + +#define AFS_HELPFILE L"afsplhlp.chm" + +/* token acquisition methods provided by extensions begin with this + ID */ +#define AFS_TOKEN_USER 8 + +void init_afs(); +void exit_afs(); +KHMEXP khm_int32 KHMAPI init_module(kmm_module h_module); +KHMEXP khm_int32 KHMAPI exit_module(kmm_module h_module); + +/* globals */ +extern kmm_module h_khModule; +extern HMODULE hResModule; +extern HINSTANCE hInstance; + +extern khm_int32 afs_credtype_id; +extern khm_int32 krb5_credtype_id; +extern khm_int32 krb4_credtype_id; +extern khm_int32 afs_msg_type_id; +extern khm_handle afs_credset; + +extern khm_int32 afs_type_principal; +extern khm_int32 afs_attr_client_princ; +extern khm_int32 afs_attr_server_princ; +extern khm_int32 afs_attr_cell; +extern khm_int32 afs_attr_method; +extern khm_int32 afs_attr_realm; + +/* Configuration spaces */ +#define CSNAME_PLUGINS L"Plugins" +#define CSNAME_AFSCRED L"AfsCred" +#define CSNAME_PARAMS L"Parameters" + +extern khm_handle csp_plugins; +extern khm_handle csp_afscred; +extern khm_handle csp_params; + +extern khm_handle afs_sub; + +/* defined in afsconfig.c which is generated from afsconfig.csv */ +extern kconf_schema schema_afsconfig[]; + + +/* plugin callback procedure */ +khm_int32 KHMAPI +afs_plugin_cb(khm_int32 msg_type, + khm_int32 msg_subtype, + khm_ui_4 uparam, + void * vparam); + +INT_PTR CALLBACK +afs_cfg_ids_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam); + +INT_PTR CALLBACK +afs_cfg_id_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam); + +INT_PTR CALLBACK +afs_cfg_main_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam); + +HWND +afs_html_help(HWND caller, + wchar_t * postfix, + UINT cmd, + DWORD_PTR data); + +/* extensions */ +typedef afs_msg_announce afs_extension; + +/* not thread safe. only call from the plugin thread */ +afs_extension * +afs_find_extension(const wchar_t * name); + +/* not thread safe. only call from the plugin thread */ +afs_extension * +afs_get_extension(khm_size i); + +/* not thread safe. only call from the plugin thread */ +afs_extension * +afs_get_next_token_acq(afs_extension * f); + +/* not thread safe. only call from the plugin thread */ +khm_boolean +afs_is_valid_method_id(afs_tk_method method); + +afs_tk_method +afs_get_next_method_id(afs_tk_method method); + +afs_tk_method +afs_get_method_id(wchar_t * name); + +khm_boolean +afs_get_method_name(afs_tk_method method, wchar_t * buf, khm_size cbbuf); + +afs_extension * +afs_get_method_ext(afs_tk_method method); + +khm_boolean +afs_method_describe(afs_tk_method method, khm_int32 flags, + wchar_t * wbuf, khm_size cbbuf); + +khm_boolean +afs_ext_resolve_token(const wchar_t * cell, + const struct ktc_token * token, + const struct ktc_principal * serverp, + const struct ktc_principal * clientp, + khm_handle * pident, + afs_tk_method * pmethod); + +khm_boolean +afs_ext_klog(afs_tk_method method, + khm_handle identity, + const char * service, + const char * cell, + const char * realm, + const afs_conf_cell * cell_config, + khm_int32 lifetime); + +BOOL +afs_cfg_get_afscreds_shortcut(wchar_t * wpath); + +#endif diff --git a/src/WINNT/netidmgr_plugin/afsext.c b/src/WINNT/netidmgr_plugin/afsext.c new file mode 100644 index 0000000..5a3e5d3 --- /dev/null +++ b/src/WINNT/netidmgr_plugin/afsext.c @@ -0,0 +1,454 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include +#include + +/* supported API versions */ +#define AFS_PLUGIN_VERSION_MIN 0x00000001 +#define AFS_PLUGIN_VERSION_MAX AFS_PLUGIN_VERSION + +#define MAX_EXTENSIONS 8 + +afs_extension extensions[MAX_EXTENSIONS]; +khm_size n_extensions = 0; +khm_int32 next_method_id = AFS_TOKEN_USER; + +/* not threadsafe. should only be called from the plugin thread */ +khm_int32 +afs_add_extension(afs_msg_announce * ann) { + size_t cbname = 0; + size_t cbtashort = 0; + size_t cbtalong = 0; + afs_extension * ext; + wchar_t * tmp; + + if (ann->cbsize != sizeof(afs_msg_announce) || + FAILED(StringCbLength(ann->name, KHUI_MAXCB_NAME, &cbname)) || + ann->sub == NULL || + (ann->provide_token_acq && + ((FAILED(StringCbLength(ann->token_acq.short_desc, + KHUI_MAXCB_SHORT_DESC, + &cbtashort))) || + (ann->token_acq.long_desc && + FAILED(StringCbLength(ann->token_acq.long_desc, + KHUI_MAXCB_LONG_DESC, + &cbtalong))))) || + ann->version < AFS_PLUGIN_VERSION_MIN || + ann->version > AFS_PLUGIN_VERSION_MAX) + + return KHM_ERROR_INVALID_PARAM; + + if (n_extensions == MAX_EXTENSIONS) + return KHM_ERROR_NO_RESOURCES; + + cbname += sizeof(wchar_t); + cbtashort += sizeof(wchar_t); + cbtalong += sizeof(wchar_t); + + ext = &extensions[n_extensions]; + + *ext = *ann; + + tmp = PMALLOC(cbname); +#ifdef DEBUG + assert(tmp); +#endif + StringCbCopy(tmp, cbname, ann->name); + ext->name = tmp; + + if (ann->provide_token_acq) { + tmp = PMALLOC(cbtashort); +#ifdef DEBUG + assert(tmp); +#endif + StringCbCopy(tmp, cbtashort, ann->token_acq.short_desc); + ext->token_acq.short_desc = tmp; + + if (ann->token_acq.long_desc) { + tmp = PMALLOC(cbtalong); +#ifdef DEBUG + assert(tmp); +#endif + StringCbCopy(tmp, cbtalong, + ann->token_acq.long_desc); + ext->token_acq.long_desc = tmp; + } else { + ext->token_acq.long_desc = NULL; + } + + ann->token_acq.method_id = next_method_id++; + ext->token_acq.method_id = ann->token_acq.method_id; + } else { + ZeroMemory(&ext->token_acq, sizeof(ext->token_acq)); + } + + n_extensions++; + + return KHM_ERROR_SUCCESS; +} + +void +afs_free_extension(khm_int32 idx) { + afs_extension * ext; + +#ifdef DEBUG + assert(idx >= 0 && idx < (khm_int32) n_extensions); +#endif + + ext = &extensions[idx]; + + if (ext->name) + PFREE((void *) ext->name); + if (ext->token_acq.short_desc) + PFREE((void *) ext->token_acq.short_desc); + if (ext->token_acq.long_desc) + PFREE((void *) ext->token_acq.long_desc); + if (ext->sub) + kmq_delete_subscription(ext->sub); + + ZeroMemory(ext, sizeof(*ext)); +} + +/* not thread safe. only call from plugin thread */ +void +afs_remove_extension(khm_int32 idx) { + if (idx < 0 || idx > (khm_int32) n_extensions) + return; + + afs_free_extension(idx); + + if (idx == n_extensions-1) { + n_extensions--; + } else { + MoveMemory(&extensions[idx], &extensions[idx + 1], + (n_extensions - (idx+1)) * sizeof(*extensions)); + } +} + +/* not thread safe. only call from the plugin thread */ +afs_extension * +afs_find_extension(const wchar_t * name) { + khm_size i; + + for (i=0; i < n_extensions; i++) { + if (extensions[i].name && + !wcscmp(extensions[i].name, name)) + return &extensions[i]; + } + + return NULL; +} + +/* not thread safe. only call from the plugin thread */ +khm_boolean +afs_is_valid_method_id(afs_tk_method method) { + khm_size i; + + if (method == AFS_TOKEN_AUTO || + method == AFS_TOKEN_KRB5 || + method == AFS_TOKEN_KRB524 || + method == AFS_TOKEN_KRB4) + return TRUE; + + for (i=0; i < n_extensions; i++) { + if (extensions[i].provide_token_acq && + extensions[i].token_acq.method_id == method) + return TRUE; + } + + return FALSE; +} + +khm_boolean +afs_method_describe(afs_tk_method method, khm_int32 flags, + wchar_t * wbuf, khm_size cbbuf) { + khm_size idx; + + switch(method) { + case AFS_TOKEN_AUTO: + return LoadString(hResModule, + ((flags & KCDB_TS_SHORT)? + IDS_NC_METHOD_AUTO: + IDS_NC_METHODL_AUTO), + wbuf, (int) cbbuf / sizeof(wchar_t)); + + case AFS_TOKEN_KRB5: + return LoadString(hResModule, + ((flags & KCDB_TS_SHORT)? + IDS_NC_METHOD_KRB5: + IDS_NC_METHODL_KRB5), + wbuf, (int) cbbuf / sizeof(wchar_t)); + + case AFS_TOKEN_KRB524: + return LoadString(hResModule, + ((flags & KCDB_TS_SHORT)? + IDS_NC_METHOD_KRB524: + IDS_NC_METHODL_KRB524), + wbuf, (int) cbbuf / sizeof(wchar_t)); + + case AFS_TOKEN_KRB4: + return LoadString(hResModule, + ((flags & KCDB_TS_SHORT)? + IDS_NC_METHOD_KRB4: + IDS_NC_METHODL_KRB4), + wbuf, (int) cbbuf / sizeof(wchar_t)); + + default: + for (idx = 0; idx < n_extensions; idx++) { + if(!extensions[idx].provide_token_acq || + extensions[idx].token_acq.method_id != method) + continue; + + if ((flags & KCDB_TS_SHORT) || + extensions[idx].token_acq.long_desc == NULL) + return SUCCEEDED(StringCbCopy(wbuf, cbbuf, + extensions[idx].token_acq.short_desc)); + else + return SUCCEEDED(StringCbCopy(wbuf, cbbuf, + extensions[idx].token_acq.long_desc)); + } + } + + return FALSE; +} + +afs_tk_method +afs_get_next_method_id(afs_tk_method method) { + khm_size idx; + + switch(method) { + case -1: + return AFS_TOKEN_AUTO; + case AFS_TOKEN_AUTO: + return AFS_TOKEN_KRB5; + case AFS_TOKEN_KRB5: + return AFS_TOKEN_KRB524; + case AFS_TOKEN_KRB524: + return AFS_TOKEN_KRB4; + case AFS_TOKEN_KRB4: + idx = 0; + break; + default: + for(idx = 0; idx < n_extensions; idx ++) { + if (extensions[idx].provide_token_acq && + extensions[idx].token_acq.method_id == method) + break; + } + idx++; + } + + for(; idx < n_extensions; idx++) { + if (extensions[idx].provide_token_acq) + return extensions[idx].token_acq.method_id; + } + + return -1; +} + +/* not thread safe. only call from the plugin thread */ +afs_extension * +afs_get_next_token_acq(afs_extension * f) { + khm_size idx; + + if (f == NULL) + idx = 0; + else + idx = (f - extensions) + 1; + + for(; idx < n_extensions; idx++) { + if (extensions[idx].provide_token_acq) + return &extensions[idx]; + } + + return NULL; +} + +afs_extension * +afs_get_extension(khm_size i) { + if (i >= n_extensions) + return NULL; + else + return &extensions[i]; +} + +afs_tk_method +afs_get_method_id(wchar_t * name) { + if (!wcscmp(name, AFS_TOKENNAME_AUTO)) + return AFS_TOKEN_AUTO; + else if (!wcscmp(name, AFS_TOKENNAME_KRB5)) + return AFS_TOKEN_KRB5; + else if (!wcscmp(name, AFS_TOKENNAME_KRB524)) + return AFS_TOKEN_KRB524; + else if (!wcscmp(name, AFS_TOKENNAME_KRB4)) + return AFS_TOKEN_KRB4; + else { + khm_size i; + + for (i=0; i < n_extensions; i++) { + if (!extensions[i].provide_token_acq) + continue; + + if (!wcscmp(extensions[i].name, name)) + return extensions[i].token_acq.method_id; + } + } + + return AFS_TOKEN_AUTO; +} + +khm_boolean +afs_get_method_name(afs_tk_method method, wchar_t * buf, khm_size cbbuf) { + if (method == AFS_TOKEN_AUTO) + return SUCCEEDED(StringCbCopy(buf, cbbuf, AFS_TOKENNAME_AUTO)); + else if (method == AFS_TOKEN_KRB5) + return SUCCEEDED(StringCbCopy(buf, cbbuf, AFS_TOKENNAME_KRB5)); + else if (method == AFS_TOKEN_KRB524) + return SUCCEEDED(StringCbCopy(buf, cbbuf, AFS_TOKENNAME_KRB524)); + else if (method == AFS_TOKEN_KRB4) + return SUCCEEDED(StringCbCopy(buf, cbbuf, AFS_TOKENNAME_KRB4)); + else { + khm_size i; + + for (i=0; i < n_extensions; i++) { + if (!extensions[i].provide_token_acq) + continue; + if (extensions[i].token_acq.method_id == method) + return SUCCEEDED(StringCbCopy(buf, cbbuf, + extensions[i].name)); + } + } + + return FALSE; +} + +/* not thread safe. only call from the plugin thread */ +khm_boolean +afs_ext_resolve_token(const wchar_t * cell, + const struct ktc_token * token, + const struct ktc_principal * serverp, + const struct ktc_principal * clientp, + khm_handle * pident, + afs_tk_method * pmethod) { + + afs_msg_resolve_token rt; + khm_size idx; + khm_int32 rv; + + ZeroMemory(&rt, sizeof(rt)); + + rt.cbsize = sizeof(rt); + + rt.cell = cell; + rt.token = token; + rt.serverp = serverp; + rt.clientp = clientp; + rt.method = AFS_TOKEN_AUTO; + rt.ident = NULL; + + for (idx = 0; idx < n_extensions; idx++) { + if (!extensions[idx].provide_token_acq) + continue; + + rv = kmq_send_sub_msg(extensions[idx].sub, + afs_msg_type_id, + AFS_MSG_RESOLVE_TOKEN, + 0, + (void *) &rt); + + if (KHM_SUCCEEDED(rv)) { + assert(rt.ident != NULL); + + *pident = rt.ident; + *pmethod = rt.method; + + return TRUE; + } + } + + return FALSE; +} + +/* not thread safe. only call from the plugin thread */ +khm_boolean +afs_ext_klog(afs_tk_method method, + khm_handle identity, + const char * service, + const char * cell, + const char * realm, + const afs_conf_cell * cell_config, + khm_int32 lifetime) { + + khm_size idx; + khm_int32 rv = KHM_ERROR_GENERAL; + afs_msg_klog msg; + afs_conf_cell cellconfig; + + ZeroMemory(&msg, sizeof(msg)); + ZeroMemory(&cellconfig, sizeof(cellconfig)); + + msg.cbsize = sizeof(msg); + + msg.identity = identity; + msg.service = service; + msg.cell = cell; + msg.realm = realm; + msg.lifetime = lifetime; + + msg.cell_config = &cellconfig; + + cellconfig = *cell_config; + cellconfig.cbsize = sizeof(cellconfig); + + for (idx = 0; idx < n_extensions; idx++) { + if (!extensions[idx].provide_token_acq || + (method != AFS_TOKEN_AUTO && + extensions[idx].token_acq.method_id != method)) + continue; + + rv = kmq_send_sub_msg(extensions[idx].sub, + afs_msg_type_id, + AFS_MSG_KLOG, + 0, + (void *) &msg); + + if (KHM_SUCCEEDED(rv)) + return TRUE; + } + + return FALSE; +} + +khm_int32 KHMAPI +afs_msg_ext(khm_int32 msg_subtype, khm_ui_4 uparam, void * vparam) { + switch(msg_subtype) { + case AFS_MSG_ANNOUNCE: + return afs_add_extension((afs_msg_announce *) vparam); + } + + return KHM_ERROR_SUCCESS; +} diff --git a/src/WINNT/netidmgr_plugin/afsfuncs.c b/src/WINNT/netidmgr_plugin/afsfuncs.c new file mode 100644 index 0000000..f27f323 --- /dev/null +++ b/src/WINNT/netidmgr_plugin/afsfuncs.c @@ -0,0 +1,1432 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +/* Disable the 'macro redefinition' warning which is getting + triggerred by a redefinition of the ENCRYPT and DECRYPT macros. */ +#pragma warning (push) +#pragma warning (disable: 4005) + +#include +#include +#include + +#pragma warning (pop) + +BOOL +afs_is_running(void) { + DWORD CurrentState; + + if (!AfsAvailable) + return FALSE; + + if (GetServiceStatus(NULL, TRANSARCAFSDAEMON, + &CurrentState, NULL) != NOERROR) + return FALSE; + if (CurrentState != SERVICE_RUNNING) + return FALSE; + + return TRUE; +} + +int +afs_unlog(void) +{ + long rc; + + if (!afs_is_running()) + return 0; + + rc = ktc_ForgetAllTokens(); + + return rc; +} + +int +afs_unlog_cred(khm_handle cred) +{ + long rc; + struct ktc_principal princ; + khm_size cbbuf; + wchar_t name[KCDB_MAXCCH_NAME]; + + if (!afs_is_running()) + return 0; + + cbbuf = sizeof(princ); + if(KHM_FAILED(kcdb_cred_get_attr(cred, afs_attr_server_princ, + NULL, &princ, &cbbuf))) + return 1; + + afs_princ_to_string(&princ, name, sizeof(name)); + + _report_cs1(KHERR_INFO, L"Destroying token %1!s!", + _cstr(name)); + _resolve(); + + rc = ktc_ForgetToken(&princ); + + return rc; +} + +/* convert a ktc_principal to a wchar_t string form that looks like + name.instance@cell return 0 if it worked. non-zero otherwise +*/ +int +afs_princ_to_string(struct ktc_principal * p, + wchar_t * buf, + size_t cbbuf) +{ + wchar_t wbuf[256]; + int rv = 0; + int l; + + l = AnsiStrToUnicode(wbuf, sizeof(wbuf), p->name); + wbuf[l] = L'\0'; + + rv = FAILED(StringCbCopy(buf, cbbuf, wbuf)); + if(p->instance[0]) { + StringCbCat(buf, cbbuf, L"."); + if((l = AnsiStrToUnicode(wbuf, sizeof(wbuf), p->instance)) > 0) { + wbuf[l] = L'\0'; + rv = rv || FAILED(StringCbCat(buf, cbbuf, wbuf)); + } + else + rv = 1; + } + if(p->cell[0]) { + rv = rv || FAILED(StringCbCat(buf, cbbuf, L"@")); + if((l = AnsiStrToUnicode(wbuf, sizeof(wbuf), p->cell)) > 0) { + wbuf[l] = L'\0'; + rv = rv || FAILED(StringCbCat(buf, cbbuf, wbuf)); + } + else + rv = 1; + } + + return rv; +} + +int +afs_list_tokens(void) +{ + int r; + + kcdb_credset_flush(afs_credset); + r = afs_list_tokens_internal(); + kcdb_credset_collect(NULL, afs_credset, NULL, afs_credtype_id, NULL); + + return r; +} + +/* is the credential provided an AFS token and is it from the + specified cell? */ +static khm_int32 KHMAPI +afs_filter_by_cell(khm_handle cred, khm_int32 flags, void * rock) +{ + wchar_t wcell[MAXCELLCHARS]; + wchar_t * tcell; + khm_size cbsize; + khm_int32 type; + + tcell = (wchar_t *) rock; + + if(KHM_FAILED(kcdb_cred_get_type(cred, &type)) || + type != afs_credtype_id) + return FALSE; + + cbsize = sizeof(wcell); + if(KHM_FAILED(kcdb_cred_get_attr(cred, afs_attr_cell, + NULL, wcell, &cbsize))) + return FALSE; + + if(wcscmp(wcell, tcell)) + return FALSE; + + return TRUE; +} + +struct token_filter_data { + wchar_t * cell; +}; + +khm_int32 KHMAPI +afs_filter_for_token(khm_handle cred, khm_int32 flags, void * rock) { + struct token_filter_data * pdata; + wchar_t ccell[MAXCELLCHARS]; + khm_size cb; + khm_int32 ctype; + + pdata = (struct token_filter_data *) rock; + + if (KHM_FAILED(kcdb_cred_get_type(cred, &ctype)) || + ctype != afs_credtype_id) + + return 0; + + cb = sizeof(ccell); + + if (KHM_FAILED(kcdb_cred_get_attr(cred, afs_attr_cell, + NULL, + ccell, + &cb)) || + _wcsicmp(ccell, pdata->cell)) + + return 0; + + return 1; +} + +khm_handle +afs_find_token(khm_handle credset, wchar_t * cell) { + struct token_filter_data fdata; + khm_handle cred = NULL; + + fdata.cell = cell; + + if (KHM_FAILED(kcdb_credset_find_filtered(credset, + -1, + afs_filter_for_token, + &fdata, + &cred, + NULL))) + return NULL; + else + return cred; +} + +static khm_int32 KHMAPI +afs_filter_krb5_tkt(khm_handle cred, khm_int32 flags, void * rock) +{ + wchar_t cname[KCDB_CRED_MAXCCH_NAME]; + khm_int32 type; + wchar_t * tcell; + wchar_t * t, *tkt_cell; + khm_size cbsize; + + tcell = (wchar_t *) rock; + + if(KHM_FAILED(kcdb_cred_get_type(cred, &type)) || + type != krb5_credtype_id) + return FALSE; + + cbsize = sizeof(cname); + if (KHM_FAILED(kcdb_cred_get_name(cred, cname, &cbsize))) + return FALSE; + + if (!wcsncmp(cname, L"afs/", 4)) { + + tkt_cell = cname + 4; + + t = wcschr(tkt_cell, L'@'); + if (t == NULL) + return FALSE; + *t = L'\0'; + + } else if (!wcsncmp(cname, L"afs@", 4)) { + + tkt_cell = cname + 4; + + } else { + return FALSE; + } + + if (_wcsicmp(tcell, tkt_cell)) + return FALSE; + + return TRUE; +} + +static khm_int32 KHMAPI +afs_filter_krb4_tkt(khm_handle cred, khm_int32 flags, void * rock) +{ + wchar_t cname[KCDB_CRED_MAXCCH_NAME]; + khm_int32 type; + wchar_t * tcell; + wchar_t * t, *tkt_cell; + khm_size cbsize; + + tcell = (wchar_t *) rock; + + if(KHM_FAILED(kcdb_cred_get_type(cred, &type)) || + type != krb4_credtype_id) + return FALSE; + + cbsize = sizeof(cname); + if (KHM_FAILED(kcdb_cred_get_name(cred, cname, &cbsize))) + return FALSE; + + if (!wcsncmp(cname, L"afs.", 4)) { + + tkt_cell = cname + 4; + + t = wcschr(tkt_cell, L'@'); + if (t == NULL) + return FALSE; + *t = L'\0'; + + } else if (!wcsncmp(cname, L"afs@", 4)) { + + tkt_cell = cname + 4; + + } else { + return FALSE; + } + + if (_wcsicmp(tcell, tkt_cell)) + return FALSE; + + return TRUE; +} + +/* collects all AFS tokens to the root credential set using the + generic afs_credset credential set + */ +int +afs_list_tokens_internal(void) +{ + struct ktc_principal aserver; + struct ktc_principal aclient; + struct ktc_token atoken; + int cellNum; + int BreakAtEnd; + wchar_t idname[256]; + wchar_t crname[256]; + wchar_t location[256]; + wchar_t *cell; + + DWORD rc; + + khm_handle ident = NULL; + khm_handle cred = NULL; + afs_tk_method method; + + FILETIME ft; + + if (!afs_is_running()) + return 0; + + kcdb_credset_flush(afs_credset); + + LoadString(hResModule, IDS_DEF_LOCATION, location, ARRAYLENGTH(location)); + + BreakAtEnd = 0; + cellNum = 0; + while (1) + { + memset(&aserver, 0, sizeof(aserver)); + if (rc = ktc_ListTokens(cellNum, &cellNum, &aserver)) + { + if (rc != KTC_NOENT) + return(0); + + if (BreakAtEnd == 1) + break; + } + BreakAtEnd = 1; + memset(&atoken, '\0', sizeof(atoken)); + if (rc = ktc_GetToken(&aserver, &atoken, sizeof(atoken), &aclient)) + { + if (rc == KTC_ERROR) + return(0); + + continue; + } + +#if 0 + /* failed attempt at trying to figure out the principal name from + the token. The ticket that is attached to the token is not + in a form that is useful at this point */ + idname[0] = L'\0'; + if(atoken.kvno == RXKAD_TKT_TYPE_KERBEROS_V5) { + krb5_context ctx = 0; + krb5_ccache cc = 0; + krb5_creds * k5c; + krb5_error_code code; + char * princ; + + code = khm_krb5_initialize(&ctx, &cc); + if(code) + goto _no_krb5; + + k5c = (krb5_creds *) atoken.ticket; + + code = pkrb5_unparse_name(ctx, k5c->client, &princ); + if(code) + goto _no_krb5; + + MultiByteToWideChar(CP_ACP, 0, princ, strlen(princ), idname, sizeof(idname)/sizeof(idname[0])); + + pkrb5_free_unparsed_name(ctx, princ); +_no_krb5: + ; + } +#endif + + method = AFS_TOKEN_AUTO; + + afs_princ_to_string(&aclient, idname, sizeof(idname)); + + /* We need to figure out a good client name which we can use + to create an identity which looks familiar to the user. No + good way of doing this, so we use a heuristic. + + Note that, we use another heuristic to find out which + identity to associate the token with. + + ASSUMPTION: + + The assumption here is that the principal for the token is + computed as follows: + + if realm != cell : principal looks like user@realm@cell + if realm == cell : principal looks like user@realm + + HEURISTIC: + + We strip the part of the string that follows the second '@' + sign to obtain the 'user@realm' part, which we use as the + credential name. If there is no second '@', we use the + whole principal name. */ + { + wchar_t * ats; + + ats = wcschr(idname, L'@'); + if(ats && (ats = wcschr(ats + 1, L'@'))) + *ats = L'\0'; + } + + afs_princ_to_string(&aserver, crname, sizeof(crname)); + + /* Ok, now we need to figure out which identity to associate + this token with. This is a little bit tricky, and there is + currently no good way of determining the original identity + used to obtain the token if it was done outside of + NetIDMgr. So we use a heuristic here. + + REQUIREMENT: + + Elsewhere, (actually in afsnewcreds.c) just after obtaining + AFS tokens through NetIDMgr, we enumerate the AFS tokens + and assign the root identity (used to obtain new creds) + with the AFS tokens. This would still be there in the root + credential set when we list tokens later on. + + HEURISTIC: + + If there exists an AFS token in the root credential set for + the same cell, we associate this token with the same + identity as that credential. + */ + cell = wcschr(crname, L'@'); + if(cell) { + cell++; + if(!*cell) + cell = NULL; + } + + ident = NULL; + if(cell) { + khm_handle c; + + if(KHM_SUCCEEDED(kcdb_credset_find_filtered(NULL, -1, + afs_filter_by_cell, + (void *) cell, + &c, NULL))) { + khm_size cb; + + kcdb_cred_get_identity(c, &ident); + cb = sizeof(method); + kcdb_cred_get_attr(c, afs_attr_method, NULL, + &method, &cb); + kcdb_cred_release(c); + } + } + + /* If that failed, we have try another trick. If there is a + Krb5 ticket of the form afs/@ or afs@ + where matches our cell, then we pick the identity + off of that. + + ASSUMPTION: + + If Krb5 was used to obatain the token, then there is a Krb5 + ticket of the form afs/@ or afs@ still + in the cache. This is also true for Krb524 token + acquisition. + + HEURISTIC: + + If such a Krb5 ticket is found, use the identity of that + credential as the identity of the AFS token. + + */ + if (ident == NULL && cell != NULL) { + khm_handle c; + + if(KHM_SUCCEEDED(kcdb_credset_find_filtered(NULL, -1, + afs_filter_krb5_tkt, + (void *) cell, + &c, NULL))) { + kcdb_cred_get_identity(c, &ident); + /* this could be Krb5 or Krb524, so we leave method at + AFS_TOKEN_AUTO. */ + method = AFS_TOKEN_AUTO; + kcdb_cred_release(c); + } + } + + /* If that didn't work either, we look for a Krb4 ticket of + the form afs.@ or afs@ which matches the + cell. + + ASSUMPTION: + + If Krb4 was used to obtain an AFS token, then there should + be a Krb4 ticket of the form afs.@ or + afs@ in the cache. + + HEURISTIC: + + If such a ticket is found, then use the identity of that + credential as the identity of the AFS token. + */ + if (ident == NULL && cell != NULL) { + khm_handle c; + + if (krb4_credtype_id < 0) { + kcdb_credtype_get_id(KRB4_CREDTYPE_NAME, + &krb4_credtype_id); + } + + if (krb4_credtype_id > 0 && + KHM_SUCCEEDED(kcdb_credset_find_filtered(NULL, -1, + afs_filter_krb4_tkt, + (void *) cell, + &c, NULL))) { + + kcdb_cred_get_identity(c, &ident); + kcdb_cred_release(c); + method = AFS_TOKEN_KRB4; + + } + } + + /* Finally, we allow any extension plugins to give this a shot */ + if (ident == NULL && cell != NULL) { + afs_ext_resolve_token(cell, + &atoken, + &aserver, + &aclient, + &ident, + &method); + } + + /* One more thing to try. If we have a cell->identity + mapping, then we try that. */ + if (ident == NULL && cell != NULL) { + khm_handle h_cellmap; + wchar_t tidname[KCDB_IDENT_MAXCCH_NAME]; + khm_size cb; + + cb = sizeof(tidname); + + if (KHM_SUCCEEDED(khc_open_space(csp_afscred, + L"Cells", 0, + &h_cellmap))) { + if (KHM_SUCCEEDED(khc_read_string(h_cellmap, + cell, + tidname, + &cb))) { + kcdb_identity_create(tidname, + KCDB_IDENT_FLAG_CREATE, + &ident); + } + khc_close_space(h_cellmap); + } + } + + /* all else failed */ + if(ident == NULL) { + if(KHM_FAILED(kcdb_identity_create(idname, + KCDB_IDENT_FLAG_CREATE, + &ident))) + goto _exit; + } + + if(KHM_FAILED(kcdb_cred_create(crname, ident, afs_credtype_id, &cred))) + goto _exit; + + kcdb_cred_set_attr(cred, afs_attr_method, &method, sizeof(method)); + + TimetToFileTime(atoken.endTime, &ft); + kcdb_cred_set_attr(cred, KCDB_ATTR_EXPIRE, &ft, sizeof(ft)); + if (atoken.startTime != 0) { + TimetToFileTime(atoken.startTime, &ft); + kcdb_cred_set_attr(cred, KCDB_ATTR_ISSUE, &ft, sizeof(ft)); + } + kcdb_cred_set_attr(cred, afs_attr_client_princ, + &aclient, sizeof(aclient)); + kcdb_cred_set_attr(cred, afs_attr_server_princ, + &aserver, sizeof(aserver)); + + if(cell) { + kcdb_cred_set_attr(cred, afs_attr_cell, cell, KCDB_CBSIZE_AUTO); + } + + kcdb_cred_set_attr(cred, KCDB_ATTR_LOCATION, + location, KCDB_CBSIZE_AUTO); + + kcdb_credset_add_cred(afs_credset, cred, -1); + + /* both these calls are NULL pointer safe */ + kcdb_cred_release(cred); + cred = NULL; + kcdb_identity_release(ident); + ident = NULL; + } + +_exit: + if(ident) + kcdb_identity_release(ident); + if(cred) + kcdb_cred_release(cred); + + return(0); +} + + +#define ALLOW_REGISTER 1 +static int +ViceIDToUsername(char *username, + char *realm_of_user, + char *realm_of_cell, + char * cell_to_use, + struct ktc_principal *aclient, + struct ktc_principal *aserver, + struct ktc_token *atoken) +{ + static char lastcell[MAXCELLCHARS+1] = { 0 }; + static char confname[512] = { 0 }; +#ifdef AFS_ID_TO_NAME + char username_copy[BUFSIZ]; +#endif /* AFS_ID_TO_NAME */ + long viceId = ANONYMOUSID; /* AFS uid of user */ + int status = 0; +#ifdef ALLOW_REGISTER + afs_int32 id; +#endif /* ALLOW_REGISTER */ + + if (confname[0] == '\0') { + StringCbCopyA(confname, sizeof(confname), AFSDIR_CLIENT_ETC_DIRPATH); + } + + StringCbCopyA(lastcell, sizeof(lastcell), aserver->cell); + + if (!pr_Initialize (0, confname, aserver->cell)) { + char sname[PR_MAXNAMELEN]; + StringCbCopyA(sname, sizeof(sname), username); + status = pr_SNameToId (sname, &viceId); + pr_End(); + } + + /* + * This is a crock, but it is Transarc's crock, so + * we have to play along in order to get the + * functionality. The way the afs id is stored is + * as a string in the username field of the token. + * Contrary to what you may think by looking at + * the code for tokens, this hack (AFS ID %d) will + * not work if you change %d to something else. + */ + + /* + * This code is taken from cklog -- it lets people + * automatically register with the ptserver in foreign cells + */ + +#ifdef ALLOW_REGISTER + if (status == 0) { + if (viceId != ANONYMOUSID) { +#else /* ALLOW_REGISTER */ + if ((status == 0) && (viceId != ANONYMOUSID)) +#endif /* ALLOW_REGISTER */ + { +#ifdef AFS_ID_TO_NAME + StringCbCopyA(username_copy, BUFSIZ, username); + StringCchPrintfA(username, BUFSIZ, "%s (AFS ID %d)", username_copy, (int) viceId); +#endif /* AFS_ID_TO_NAME */ + } +#ifdef ALLOW_REGISTER + } else if (strcmp(realm_of_user, realm_of_cell) != 0) { + id = 0; + StringCbCopyA(aclient->name, sizeof(aclient->name), username); + StringCbCopyA(aclient->instance, sizeof(aclient->instance), ""); + StringCbCopyA(aclient->cell, sizeof(aclient->cell), realm_of_user); + if (status = ktc_SetToken(aserver, atoken, aclient, 0)) + return status; + if (status = pr_Initialize(1L, confname, aserver->cell)) + return status; + status = pr_CreateUser(username, &id); + pr_End(); + if (status) + return status; +#ifdef AFS_ID_TO_NAME + StringCbCopyA(username_copy, BUFSIZ, username); + StringCchPrintfA(username, BUFSIZ, "%s (AFS ID %d)", username_copy, (int) viceId); +#endif /* AFS_ID_TO_NAME */ + } + } +#endif /* ALLOW_REGISTER */ + return status; +} + + +int +afs_klog(khm_handle identity, + char *service, + char *cell, + char *realm, + int LifeTime, + afs_tk_method method, + time_t * tok_expiration) { + + long rc; + CREDENTIALS creds; + struct ktc_principal aserver; + struct ktc_principal aclient; + char realm_of_user[REALM_SZ]; /* Kerberos realm of user */ + char realm_of_cell[REALM_SZ]; /* Kerberos realm of cell */ + char local_cell[MAXCELLCHARS+1]; + char Dmycell[MAXCELLCHARS+1]; + struct ktc_token atoken; + struct ktc_token btoken; + afs_conf_cell ak_cellconfig; /* General information about the cell */ + char RealmName[128]; + char CellName[128]; + char ServiceName[128]; + khm_handle confighandle; + khm_int32 supports_krb4 = 1; + + /* signalling */ + BOOL bGotCreds = FALSE; /* got creds? */ + + if (tok_expiration) + *tok_expiration = (time_t) 0; + + if (!afs_is_running()) { + _report_sr0(KHERR_WARNING, IDS_ERR_NOSERVICE); + return(0); + } + + if ( !realm ) realm = ""; + if ( !cell ) cell = ""; + if ( !service ) service = ""; + + memset(RealmName, '\0', sizeof(RealmName)); + memset(CellName, '\0', sizeof(CellName)); + memset(ServiceName, '\0', sizeof(ServiceName)); + memset(realm_of_user, '\0', sizeof(realm_of_user)); + memset(realm_of_cell, '\0', sizeof(realm_of_cell)); + memset(Dmycell, '\0', sizeof(Dmycell)); + + // NULL or empty cell returns information on local cell + if (cell && cell[0]) + StringCbCopyA(Dmycell, sizeof(Dmycell), cell); + + rc = afs_get_cellconfig(Dmycell, &ak_cellconfig, local_cell); + if (rc) { + _reportf(L"afs_get_cellconfig returns %ld", rc); + + _report_sr2(KHERR_ERROR, IDS_ERR_CELLCONFIG, _cstr(Dmycell), _int32(rc)); + _suggest_sr(IDS_ERR_CELLCONFIG_S, KHERR_SUGGEST_NONE); + _resolve(); + return(rc); + } + + StringCbCopyA(realm_of_cell, sizeof(realm_of_cell), + afs_realm_of_cell(&ak_cellconfig)); + + if (strlen(service) == 0) + StringCbCopyA(ServiceName, sizeof(ServiceName), "afs"); + else + StringCbCopyA(ServiceName, sizeof(ServiceName), service); + + if (strlen(cell) == 0) + StringCbCopyA(CellName, sizeof(CellName), local_cell); + else + StringCbCopyA(CellName, sizeof(CellName), cell); + + if (strlen(realm) == 0) + StringCbCopyA(RealmName, sizeof(RealmName), realm_of_cell); + else + StringCbCopyA(RealmName, sizeof(RealmName), realm); + + memset(&creds, '\0', sizeof(creds)); + + /*** Kerberos 5 and 524 ***/ + + if (method == AFS_TOKEN_AUTO || + method == AFS_TOKEN_KRB5 || + method == AFS_TOKEN_KRB524) { + + krb5_context context = 0; + krb5_ccache k5cc = 0; + krb5_creds increds; + krb5_creds * k5creds = 0; + krb5_error_code r; + krb5_principal client_principal = 0; + krb5_flags flags = 0; + + int retry = 0; + int len; + char *p; + + _reportf(L"Trying Kerberos 5"); + + if (!(r = khm_krb5_initialize(identity, &context, &k5cc))) { + int i; + + memset((char *)&increds, 0, sizeof(increds)); + + (*pkrb5_cc_get_principal)(context, k5cc, &client_principal); + i = krb5_princ_realm(context, client_principal)->length; + if (i > REALM_SZ-1) + i = REALM_SZ-1; + StringCchCopyNA(realm_of_user, ARRAYLENGTH(realm_of_user), + krb5_princ_realm(context, client_principal)->data, + i); + } else { + _reportf(L"khm_krb5_initialize returns code %d", r); + goto try_krb4; + } + + /* First try Service/Cell@REALM */ + if (r = (*pkrb5_build_principal)(context, &increds.server, + (int) strlen(RealmName), + RealmName, + ServiceName, + CellName, + 0)) { + _reportf(L"krb5_build_principal returns %d", r); + goto end_krb5; + } + + increds.client = client_principal; + increds.times.endtime = 0; + /* Ask for DES since that is what V4 understands */ + increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC; + +#ifdef KRB5_TC_NOTICKET + flags = 0; + r = pkrb5_cc_set_flags(context, k5cc, flags); +#endif + r = pkrb5_get_credentials(context, 0, k5cc, &increds, &k5creds); + if (r == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN || + r == KRB5KRB_ERR_GENERIC /* Heimdal */) { + /* Next try Service@REALM */ + pkrb5_free_principal(context, increds.server); + r = (*pkrb5_build_principal)(context, &increds.server, + (int) strlen(RealmName), + RealmName, + ServiceName, + 0); + if (r == 0) + r = pkrb5_get_credentials(context, 0, k5cc, + &increds, &k5creds); + } + + pkrb5_free_principal(context, increds.server); + pkrb5_free_principal(context, client_principal); + client_principal = 0; +#ifdef KRB5_TC_NOTICKET + flags = KRB5_TC_NOTICKET; + pkrb5_cc_set_flags(context, k5cc, flags); +#endif + + (void) pkrb5_cc_close(context, k5cc); + k5cc = 0; + + if (r) { + _reportf(L"Code %d while getting credentials", r); + goto end_krb5; + } + + if ( k5creds->ticket.length > MAXKTCTICKETLEN || + method == AFS_TOKEN_KRB524) { + goto try_krb524d; + } + + /* This code inserts the entire K5 ticket into the token */ + + _reportf(L"Trying K5 SetToken"); + + memset(&aserver, '\0', sizeof(aserver)); + StringCchCopyA(aserver.name, MAXKTCNAMELEN, ServiceName); + StringCchCopyA(aserver.cell, MAXKTCREALMLEN, CellName); + + memset(&atoken, '\0', sizeof(atoken)); + atoken.kvno = RXKAD_TKT_TYPE_KERBEROS_V5; + atoken.startTime = k5creds->times.starttime; + atoken.endTime = k5creds->times.endtime; + memcpy(&atoken.sessionKey, + k5creds->keyblock.contents, + k5creds->keyblock.length); + atoken.ticketLen = k5creds->ticket.length; + memcpy(atoken.ticket, k5creds->ticket.data, atoken.ticketLen); + + if (tok_expiration) + *tok_expiration = k5creds->times.endtime; + + retry_gettoken5: + rc = ktc_GetToken(&aserver, &btoken, sizeof(btoken), &aclient); + if (rc != 0 && rc != KTC_NOENT && rc != KTC_NOCELL) { + if ( rc == KTC_NOCM && retry < 20 ) { + Sleep(500); + retry++; + goto retry_gettoken5; + } + goto try_krb524d; + } + + if (atoken.kvno == btoken.kvno && + atoken.ticketLen == btoken.ticketLen && + !memcmp(&atoken.sessionKey, &btoken.sessionKey, + sizeof(atoken.sessionKey)) && + !memcmp(atoken.ticket, btoken.ticket, atoken.ticketLen)) { + + /* success */ + if (k5creds && context) + pkrb5_free_creds(context, k5creds); + + if (context) + pkrb5_free_context(context); + + _reportf(L"Same token already exists"); + + return 0; + } + + // * Reset the "aclient" structure before we call ktc_SetToken. + // * This structure was first set by the ktc_GetToken call when + // * we were comparing whether identical tokens already existed. + + len = min(k5creds->client->data[0].length,MAXKTCNAMELEN - 1); + StringCchCopyNA(aclient.name, MAXKTCNAMELEN, + k5creds->client->data[0].data, len); + + if ( k5creds->client->length > 1 ) { + StringCbCatA(aclient.name, sizeof(aclient.name), "."); + p = aclient.name + strlen(aclient.name); + len = (int) min(k5creds->client->data[1].length, + MAXKTCNAMELEN - strlen(aclient.name) - 1); + StringCchCopyNA(p, MAXKTCNAMELEN - strlen(aclient.name), + k5creds->client->data[1].data, len); + } + + aclient.instance[0] = '\0'; + + StringCbCopyA(aclient.cell, sizeof(aclient.cell), realm_of_cell); + + StringCbCatA(aclient.name, sizeof(aclient.name), "@"); + p = aclient.name + strlen(aclient.name); + len = (int) min(k5creds->client->realm.length, + MAXKTCNAMELEN - strlen(aclient.name) - 1); + StringCchCopyNA(p, MAXKTCNAMELEN - strlen(aclient.name), + k5creds->client->realm.data, len); + + ViceIDToUsername(aclient.name, realm_of_user, realm_of_cell, CellName, + &aclient, &aserver, &atoken); + + rc = ktc_SetToken(&aserver, &atoken, &aclient, 0); + if (!rc) { + /* success */ + + if (k5creds && context) + pkrb5_free_creds(context, k5creds); + + if (context) + pkrb5_free_context(context); + + return 0; + } + + _reportf(L"SetToken returns code %d", rc); + + try_krb524d: + + _reportf(L"Trying Krb524"); + + if (method == AFS_TOKEN_AUTO || + method == AFS_TOKEN_KRB524) { + /* This requires krb524d to be running with the KDC */ + r = pkrb524_convert_creds_kdc(context, k5creds, &creds); + if (r) { + _reportf(L"Code %d while converting credentials", r); + goto end_krb5; + } + rc = KSUCCESS; + bGotCreds = TRUE; + } + + end_krb5: + if (client_principal) + pkrb5_free_principal(context, client_principal); + + if (k5creds && context) + pkrb5_free_creds(context, k5creds); + + if (context) + pkrb5_free_context(context); + } + + /* Kerberos 4 */ + try_krb4: + + kcdb_identity_get_config(identity, 0, &confighandle); + khc_read_int32(confighandle, L"Krb4Cred\\Krb4NewCreds", &supports_krb4); + khc_close_space(confighandle); + + if (!supports_krb4) { + _reportf(L"Kerberos 4 not configured"); + } + + if (!bGotCreds && supports_krb4 && + (method == AFS_TOKEN_AUTO || + method == AFS_TOKEN_KRB4)) { + + KTEXT_ST ticket; + + _reportf(L"Trying Kerberos 4"); + + if (!realm_of_user[0] ) { + if ((rc = (*pkrb_get_tf_realm)((*ptkt_string)(), realm_of_user)) + != KSUCCESS) { + /* can't determine realm of user */ + _reportf(L"krb_get_tf_realm returns %d", rc); + goto end_krb4; + } + } + + _reportf(L"Trying to find %S.%S@%S", ServiceName, CellName, RealmName); + rc = (*pkrb_get_cred)(ServiceName, CellName, RealmName, &creds); + if (rc == NO_TKT_FIL) { + // if the problem is that we have no krb4 tickets + // do not attempt to continue + _reportf(L"krb_get_cred returns %d (no ticket file)", rc); + goto end_krb4; + } + + if (rc != KSUCCESS) { + _reportf(L"Trying to find %S@%S", ServiceName, RealmName); + rc = (*pkrb_get_cred)(ServiceName, "", RealmName, &creds); + } + + if (rc != KSUCCESS) { + _reportf(L"Trying to obtain new ticket"); + if ((rc = (*pkrb_mk_req)(&ticket, ServiceName, + CellName, RealmName, 0)) + == KSUCCESS) { + if ((rc = (*pkrb_get_cred)(ServiceName, CellName, + RealmName, &creds)) != KSUCCESS) { + goto end_krb4; + } else { + _reportf(L"Got %S.%S@%S", ServiceName, CellName, RealmName); + } + } else if ((rc = (*pkrb_mk_req)(&ticket, ServiceName, + "", RealmName, 0)) + == KSUCCESS) { + if ((rc = (*pkrb_get_cred)(ServiceName, "", + RealmName, &creds)) != KSUCCESS) { + goto end_krb4; + } else { + _reportf(L"Got %S@%S", ServiceName, RealmName); + } + } else { + goto end_krb4; + } + } + + bGotCreds = TRUE; + + end_krb4: + ; + } + + if (bGotCreds) { + + memset(&aserver, '\0', sizeof(aserver)); + StringCchCopyA(aserver.name, MAXKTCNAMELEN, ServiceName); + StringCchCopyA(aserver.cell, MAXKTCREALMLEN, CellName); + + memset(&atoken, '\0', sizeof(atoken)); + atoken.kvno = creds.kvno; + atoken.startTime = creds.issue_date; + atoken.endTime = (*pkrb_life_to_time)(creds.issue_date,creds.lifetime); + memcpy(&atoken.sessionKey, creds.session, 8); + atoken.ticketLen = creds.ticket_st.length; + memcpy(atoken.ticket, creds.ticket_st.dat, atoken.ticketLen); + + if (tok_expiration) + *tok_expiration = atoken.endTime; + + if (!(rc = ktc_GetToken(&aserver, &btoken, + sizeof(btoken), &aclient)) && + atoken.kvno == btoken.kvno && + atoken.ticketLen == btoken.ticketLen && + !memcmp(&atoken.sessionKey, &btoken.sessionKey, + sizeof(atoken.sessionKey)) && + !memcmp(atoken.ticket, btoken.ticket, atoken.ticketLen)) { + + /* success! */ + return(0); + } + + // Reset the "aclient" structure before we call ktc_SetToken. + // This structure was first set by the ktc_GetToken call when + // we were comparing whether identical tokens already existed. + + StringCchCopyA(aclient.name, MAXKTCNAMELEN, creds.pname); + if (creds.pinst[0]) { + StringCchCatA(aclient.name, MAXKTCNAMELEN, "."); + StringCchCatA(aclient.name, MAXKTCNAMELEN, creds.pinst); + } + + StringCbCopyA(aclient.instance, sizeof(aclient.instance), ""); + + StringCchCatA(aclient.name, MAXKTCNAMELEN, "@"); + StringCchCatA(aclient.name, MAXKTCNAMELEN, creds.realm); + + StringCbCopyA(aclient.cell, sizeof(aclient.cell), CellName); + + ViceIDToUsername(aclient.name, realm_of_user, realm_of_cell, CellName, + &aclient, &aserver, &atoken); + + // NOTE: On WIN32, the order of SetToken params changed... + // to ktc_SetToken(&aserver, &aclient, &atoken, 0) + // from ktc_SetToken(&aserver, &atoken, &aclient, 0) on + // Unix... The afscompat ktc_SetToken provides the Unix order + + if (rc = ktc_SetToken(&aserver, &atoken, &aclient, 0)) { + afs_report_error(rc, "ktc_SetToken()"); + return(rc); + } + } else if (method == AFS_TOKEN_AUTO || + method >= AFS_TOKEN_USER) { + /* we couldn't get a token using Krb5, Krb524 or Krb4, either + because we couldn't get the necessary credentials or + because the method was set to not use those. Now we + dispatch to any extensions to see if they have better + luck. */ + + rc = !afs_ext_klog(method, + identity, + ServiceName, + CellName, + RealmName, + &ak_cellconfig, + LifeTime); + } else { + /* if the return code was not set, we should set it now. + Otherwise we let the code go through. */ + if (!rc) { + /* No tokens were obtained. We should report something */ + _report_sr1(KHERR_ERROR, IDS_ERR_GENERAL, + _cptr(CellName)); + _resolve(); + + rc = KHM_ERROR_GENERAL; + } + } + + return rc; +} + +/**************************************/ +/* afs_realm_of_cell(): */ +/**************************************/ +static char * +afs_realm_of_cell(afs_conf_cell *cellconfig) +{ + char krbhst[MAX_HSTNM]=""; + static char krbrlm[REALM_SZ+1]=""; + krb5_context ctx = 0; + char ** realmlist=NULL; + krb5_error_code r; + + if (!cellconfig) + return 0; + + if ( pkrb5_init_context ) { + r = pkrb5_init_context(&ctx); + if ( !r ) + r = pkrb5_get_host_realm(ctx, cellconfig->hostName[0], &realmlist); + if ( !r && realmlist && realmlist[0] ) { + StringCbCopyA(krbrlm, sizeof(krbrlm), realmlist[0]); + pkrb5_free_host_realm(ctx, realmlist); + } + if (ctx) + pkrb5_free_context(ctx); + } + + if ( !krbrlm[0] ) { + StringCbCopyA(krbrlm, sizeof(krbrlm), + (char *)(*pkrb_realmofhost)(cellconfig->hostName[0])); + if ((*pkrb_get_krbhst)(krbhst, krbrlm, 1) != KSUCCESS) + krbrlm[0] = '\0'; + } + + if ( !krbrlm[0] ) { + char *s = krbrlm; + char *t = cellconfig->name; + int c; + + while (c = *t++) + { + if (islower(c)) c=toupper(c); + *s++ = c; + } + *s++ = 0; + } + return(krbrlm); +} + +/**************************************/ +/* afs_get_cellconfig(): */ +/**************************************/ +static int +afs_get_cellconfig(char *cell, afs_conf_cell *cellconfig, char *local_cell) +{ + int rc; + int ttl; + + local_cell[0] = (char)0; + memset(cellconfig, 0, sizeof(*cellconfig)); + + cellconfig->cbsize = sizeof(*cellconfig); + + /* WIN32: cm_GetRootCellName(local_cell) - NOTE: no way to get max chars */ + if (rc = cm_GetRootCellName(local_cell)) { + return(rc); + } + + if (strlen(cell) == 0) + StringCbCopyA(cell, (MAXCELLCHARS+1) * sizeof(char), local_cell); + + /* WIN32: cm_SearchCellFile(cell, pcallback, pdata) */ + StringCbCopyA(cellconfig->name, (MAXCELLCHARS+1) * sizeof(char), cell); + + rc = cm_SearchCellFile(cell, NULL, afs_get_cellconfig_callback, + (void*)cellconfig); + if(rc) + rc = cm_SearchCellByDNS(cell, NULL, &ttl, + afs_get_cellconfig_callback, + (void*) cellconfig); + + return rc; +} + +/**************************************/ +/* afs_get_cellconfig_callback(): */ +/**************************************/ +static long +afs_get_cellconfig_callback(void *cellconfig, + struct sockaddr_in *addrp, + char *namep) +{ + afs_conf_cell *cc = (afs_conf_cell *)cellconfig; + + cc->hostAddr[cc->numServers] = *addrp; + StringCbCopyA(cc->hostName[cc->numServers], + sizeof(cc->hostName[0]), namep); + cc->numServers++; + return(0); +} + + +/**************************************/ +/* afs_report_error(): */ +/**************************************/ +void +afs_report_error(LONG rc, LPCSTR FailedFunctionName) +{ + char message[256]; + const char *errText; + + // Using AFS defines as error messages for now, until Transarc + // gets back to me with "string" translations of each of these + // const. defines. + if (rc == KTC_ERROR) + errText = "KTC_ERROR"; + else if (rc == KTC_TOOBIG) + errText = "KTC_TOOBIG"; + else if (rc == KTC_INVAL) + errText = "KTC_INVAL"; + else if (rc == KTC_NOENT) + errText = "KTC_NOENT"; + else if (rc == KTC_PIOCTLFAIL) + errText = "KTC_PIOCTLFAIL"; + else if (rc == KTC_NOPIOCTL) + errText = "KTC_NOPIOCTL"; + else if (rc == KTC_NOCELL) + errText = "KTC_NOCELL"; + else if (rc == KTC_NOCM) + errText = "KTC_NOCM: The service, Transarc AFS Daemon, most likely is not started!"; + else + errText = "Unknown error!"; + + StringCbPrintfA(message, sizeof(message), + "%s\n(%s failed)", errText, FailedFunctionName); + _report_cs1(KHERR_ERROR, L"%1!S!", _cptr(message)); + _resolve(); + return; +} + +DWORD +GetServiceStatus(LPSTR lpszMachineName, + LPSTR lpszServiceName, + DWORD *lpdwCurrentState, + DWORD *lpdwWaitHint) +{ + DWORD hr = NOERROR; + SC_HANDLE schSCManager = NULL; + SC_HANDLE schService = NULL; + DWORD fdwDesiredAccess = 0; + SERVICE_STATUS ssServiceStatus = {0}; + BOOL fRet = FALSE; + + *lpdwCurrentState = 0; + + fdwDesiredAccess = GENERIC_READ; + + schSCManager = OpenSCManagerA(lpszMachineName, + NULL, + fdwDesiredAccess); + + if(schSCManager == NULL) { + hr = GetLastError(); + goto cleanup; + } + + schService = OpenServiceA(schSCManager, + lpszServiceName, + fdwDesiredAccess); + + if(schService == NULL) { + hr = GetLastError(); + goto cleanup; + } + + fRet = QueryServiceStatus(schService, + &ssServiceStatus); + + if(fRet == FALSE) { + hr = GetLastError(); + goto cleanup; + } + + *lpdwCurrentState = ssServiceStatus.dwCurrentState; + if (lpdwWaitHint) + *lpdwWaitHint = ssServiceStatus.dwWaitHint; +cleanup: + + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); + + return(hr); +} + +DWORD ServiceControl(LPSTR lpszMachineName, + LPSTR lpszServiceName, + DWORD dwNewState) { + + DWORD hr = NOERROR; + SC_HANDLE schSCManager = NULL; + SC_HANDLE schService = NULL; + DWORD fdwDesiredAccess = 0; + SERVICE_STATUS ssServiceStatus = {0}; + BOOL fRet = FALSE; + DWORD dwCurrentState = 0; + + dwCurrentState = 0; + + fdwDesiredAccess = GENERIC_READ; + + schSCManager = OpenSCManagerA(lpszMachineName, NULL, + fdwDesiredAccess); + + if(schSCManager == NULL) { + hr = GetLastError(); + goto cleanup; + } + + fdwDesiredAccess = GENERIC_READ | GENERIC_EXECUTE; + + schService = OpenServiceA(schSCManager, lpszServiceName, + fdwDesiredAccess); + + if(schService == NULL) { + hr = GetLastError(); + goto cleanup; + } + + fRet = QueryServiceStatus(schService, &ssServiceStatus); + + if(fRet == FALSE) { + hr = GetLastError(); + goto cleanup; + } + + dwCurrentState = ssServiceStatus.dwCurrentState; + + if (dwCurrentState == SERVICE_STOPPED && + dwNewState == SERVICE_RUNNING) { + + fRet = StartService(schService, 0, NULL); + + if (fRet == FALSE) { + hr = GetLastError(); + goto cleanup; + } + } + + if (dwCurrentState == SERVICE_RUNNING && + dwNewState == SERVICE_STOPPED) { + fRet = ControlService(schService, SERVICE_CONTROL_STOP, + &ssServiceStatus); + + if (fRet == FALSE) { + hr = GetLastError(); + goto cleanup; + } + } + +cleanup: + + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); + + return(hr); +} diff --git a/src/WINNT/netidmgr_plugin/afsfuncs.h b/src/WINNT/netidmgr_plugin/afsfuncs.h new file mode 100644 index 0000000..6a163cd --- /dev/null +++ b/src/WINNT/netidmgr_plugin/afsfuncs.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_AFSFUNCS_H +#define __KHIMAIRA_AFSFUNCS_H + + +BOOL +afs_is_running(void); + +int +afs_princ_to_string(struct ktc_principal * p, wchar_t * buf, size_t cbbuf); + +int +afs_list_tokens(void); + +khm_handle +afs_find_token(khm_handle credset, wchar_t * cell); + +int +afs_list_tokens_internal(void); + +int +afs_klog(khm_handle identity, + char *service, + char *cell, + char *realm, + int LifeTime, + afs_tk_method method, + time_t * tok_expiration /* OUT: expiration time of new + token */ + ); + +int +afs_unlog(void); + +int +afs_unlog_cred(khm_handle cred); + +DWORD +GetServiceStatus(LPSTR lpszMachineName, + LPSTR lpszServiceName, + DWORD *lpdwCurrentState, + DWORD *lpdwWaitHint); + +DWORD +ServiceControl(LPSTR lpszMachineName, + LPSTR lpszServiceName, + DWORD dwNewState); + +void afs_report_error(LONG rc, LPCSTR FailedFunctionName); + +static char *afs_realm_of_cell(afs_conf_cell *); +static long afs_get_cellconfig_callback(void *, struct sockaddr_in *, char *); +static int afs_get_cellconfig(char *, afs_conf_cell *, char *); + +#endif diff --git a/src/WINNT/netidmgr_plugin/afshelp.c b/src/WINNT/netidmgr_plugin/afshelp.c new file mode 100644 index 0000000..e143c06 --- /dev/null +++ b/src/WINNT/netidmgr_plugin/afshelp.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#define NOSTRSAFE + +#include +#include +#include +#include + +#ifdef DEBUG +#include +#endif + +#include + +static wchar_t helpfile[MAX_PATH] = L""; + +/* can only be called from the UI thread */ +HWND +afs_html_help(HWND caller, + wchar_t * postfix, + UINT cmd, + DWORD_PTR data) { + + wchar_t fullp[MAX_PATH + MAX_PATH]; + + if (!helpfile[0]) { + DWORD rv; + + rv = GetModuleFileNameEx(GetCurrentProcess(), + hInstance, + helpfile, + ARRAYLENGTH(helpfile)); +#ifdef DEBUG + assert(rv != 0); +#endif + PathRemoveFileSpec(helpfile); + PathAppend(helpfile, AFS_HELPFILE); + } + + StringCbCopy(fullp, sizeof(fullp), helpfile); + if (postfix) + StringCbCat(fullp, sizeof(fullp), postfix); + + return HtmlHelp(caller, fullp, cmd, data); +} diff --git a/src/WINNT/netidmgr_plugin/afsnewcreds.c b/src/WINNT/netidmgr_plugin/afsnewcreds.c new file mode 100644 index 0000000..e35c347 --- /dev/null +++ b/src/WINNT/netidmgr_plugin/afsnewcreds.c @@ -0,0 +1,2781 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include +#include +#include +#include +#include + +/* UI stuff */ + +#define WMNC_AFS_UPDATE_ROWS (WMNC_USER + 1) + +typedef struct tag_afs_ident_token_set { + khm_handle ident; + afs_cred_list * l; + khm_boolean add_new; + khm_boolean update_info; +} afs_ident_token_set; + + +void +afs_cred_flush_rows(afs_cred_list * l) { + int i; + + for(i=0; in_rows; i++) { + if(l->rows[i].cell) + PFREE(l->rows[i].cell); + if(l->rows[i].realm) + PFREE(l->rows[i].realm); + } + + if(l->nc_rows) { + ZeroMemory(l->rows, sizeof(l->rows[0]) * l->nc_rows); + } + + l->n_rows = 0; +} + +void +afs_cred_free_rows(afs_cred_list * l) { + + afs_cred_flush_rows(l); + + if(l->rows) + PFREE(l->rows); + l->rows = NULL; + l->n_rows = 0; + l->nc_rows = 0; +} + +void +afs_cred_assert_rows(afs_cred_list * l, int n) { + afs_cred_row * rows; + + if(n > l->nc_rows) { + l->nc_rows = UBOUNDSS(n, AFS_DLG_ROW_ALLOC, AFS_DLG_ROW_ALLOC); + rows = PMALLOC(sizeof(afs_cred_row) * l->nc_rows); + ZeroMemory(rows, sizeof(afs_cred_row) * l->nc_rows); + + if(l->rows) { + if(l->n_rows) + memcpy(rows, l->rows, sizeof(afs_cred_row) * l->n_rows); + PFREE(l->rows); + } + l->rows = rows; + } +} + +void +afs_cred_delete_row(afs_cred_list * l, int i) { + if (i < 0 || i >= l->n_rows) + return; + + if(i < (l->n_rows - 1)) { + if(l->rows[i].cell) + PFREE(l->rows[i].cell); + if(l->rows[i].realm) + PFREE(l->rows[i].realm); + memmove(&(l->rows[i]), + &(l->rows[i+1]), + ((l->n_rows - (i+1)) * + sizeof(l->rows[0]))); + } + l->n_rows--; +} + +afs_cred_row * +afs_cred_get_new_row(afs_cred_list * l) { + afs_cred_row * r; + + afs_cred_assert_rows(l, l->n_rows + 1); + r = &(l->rows[l->n_rows]); + l->n_rows++; + + ZeroMemory(r, sizeof(*r)); + + return r; +} + +afs_cred_row * +afs_cred_add_row_from_cred(afs_cred_list * l, + khm_handle cred) { + khm_int32 rv; + afs_cred_row * row; + khm_size cb; + wchar_t cell[MAXCELLCHARS]; + int i; + + cb = sizeof(cell); + rv = kcdb_cred_get_attr(cred, + afs_attr_cell, + NULL, + cell, + &cb); +#ifdef DEBUG + assert(rv == KHM_ERROR_SUCCESS && cb != 0); +#endif + + /* check if we already have the cell listed. */ + for (i=0; in_rows; i++) { + if (!_wcsicmp(l->rows[i].cell, cell)) + return &l->rows[i]; + } + + row = afs_cred_get_new_row(l); + + row->cell = PMALLOC(cb); + StringCbCopy(row->cell, cb, cell); + + cb = sizeof(row->method); + rv = kcdb_cred_get_attr(cred, + afs_attr_method, + NULL, + &row->method, + &cb); + + if (KHM_FAILED(rv)) { + row->method = AFS_TOKEN_AUTO; + row->realm = NULL; + return row; + } + + rv = kcdb_cred_get_attr(cred, + afs_attr_realm, + NULL, + NULL, + &cb); + + if (rv == KHM_ERROR_TOO_LONG && cb > sizeof(wchar_t)) { + row->realm = PMALLOC(cb); +#ifdef DEBUG + assert(row->realm); +#endif + rv = kcdb_cred_get_attr(cred, + afs_attr_realm, + NULL, + row->realm, + &cb); + + if (KHM_FAILED(rv)) { + if (row->realm) + PFREE(row->realm); + row->realm = NULL; + } + } else { + row->realm = NULL; + } + + return row; +} + +khm_int32 KHMAPI +afs_cred_add_cred_proc(khm_handle cred, void * rock) { + afs_cred_list * l = (afs_cred_list *) rock; + khm_int32 t; + + if (KHM_FAILED(kcdb_cred_get_type(cred, &t)) || + t != afs_credtype_id) + return KHM_ERROR_SUCCESS; + + afs_cred_add_row_from_cred(l, cred); + + return KHM_ERROR_SUCCESS; +} + +void +afs_cred_get_context_creds(afs_cred_list *l, + khui_action_context * ctx) { + khm_handle credset = NULL; + + if (KHM_FAILED(kcdb_credset_create(&credset))) + return; + + if (KHM_FAILED(kcdb_credset_extract_filtered(credset, + NULL, + khui_context_cursor_filter, + (void *) ctx))) + goto _cleanup; + + kcdb_credset_apply(credset, + afs_cred_add_cred_proc, + (void *) l); + + _cleanup: + if (credset) + kcdb_credset_delete(credset); +} + +khm_int32 KHMAPI +afs_get_id_creds_apply_proc(khm_handle cred, void * rock) { + khm_int32 t; + afs_ident_token_set * ts; + afs_cred_list * l; + khm_handle ident; + wchar_t cell[MAXCELLCHARS]; + khm_size cb; + int i; + khm_int32 cflags = 0; + + ts = (afs_ident_token_set *) rock; + l = ts->l; + + kcdb_cred_get_type(cred, &t); + if (t != afs_credtype_id) + return KHM_ERROR_SUCCESS; + + cb = sizeof(cell); + if (KHM_FAILED(kcdb_cred_get_attr(cred, afs_attr_cell, + NULL, + cell, &cb))) + return KHM_ERROR_SUCCESS; + + kcdb_cred_get_flags(cred, &cflags); + + kcdb_cred_get_identity(cred, &ident); + + if (kcdb_identity_is_equal(ident, ts->ident)) { + + for (i=0; i < l->n_rows; i++) { + if (!_wcsicmp(l->rows[i].cell, cell)) { + khm_int32 method; + + /* if the token exists, then these are implied */ + l->rows[i].flags = + DLGROW_FLAG_EXISTS | + DLGROW_FLAG_CHECKED | + DLGROW_FLAG_VALID; + + if (cflags & KCDB_CRED_FLAG_EXPIRED) + l->rows[i].flags |= DLGROW_FLAG_EXPIRED; + + if (ts->update_info) { + wchar_t realm[KHUI_MAXCCH_NAME]; + + cb = sizeof(method); + if (KHM_SUCCEEDED + (kcdb_cred_get_attr(cred, afs_attr_method, + NULL, + &method, &cb)) && + afs_is_valid_method_id(method)) + l->rows[i].method = method; + + cb = sizeof(realm); + if (KHM_SUCCEEDED + (kcdb_cred_get_attr(cred, afs_attr_realm, + NULL, + realm, &cb)) && + cb > sizeof(wchar_t)) { + + if (l->rows[i].realm) + PFREE(l->rows[i].realm); + l->rows[i].realm = PMALLOC(cb); + StringCbCopy(l->rows[i].realm, + cb, + realm); + } + } + break; + } + } + + /* not found? add! */ + if (i >= l->n_rows && ts->add_new) { + afs_cred_row * r; + + r = afs_cred_add_row_from_cred(l, cred); + + r->flags = DLGROW_FLAG_VALID | DLGROW_FLAG_CHECKED | + DLGROW_FLAG_EXISTS; + + if (cflags & KCDB_CRED_FLAG_EXPIRED) + r->flags |= DLGROW_FLAG_EXPIRED; + } + + } else { /* different identities */ + + for (i=0; i < l->n_rows; i++) { + if (!_wcsicmp(l->rows[i].cell, cell)) { + l->rows[i].flags = + DLGROW_FLAG_NOTOWNED | DLGROW_FLAG_EXISTS | + DLGROW_FLAG_VALID | DLGROW_FLAG_CHECKED; + if (cflags & KCDB_CRED_FLAG_EXPIRED) + l->rows[i].flags |= DLGROW_FLAG_EXPIRED; + } + } + + } + + kcdb_identity_release(ident); + + return KHM_ERROR_SUCCESS; +} + +void +afs_remove_token_from_identities(wchar_t * cell) { + wchar_t * idents = NULL; + wchar_t * t; + khm_size cb_id; + khm_size n_id = 0; + + do { + if (kcdb_identity_enum(KCDB_IDENT_FLAG_CONFIG, + KCDB_IDENT_FLAG_CONFIG, + NULL, + &cb_id, + &n_id) != KHM_ERROR_TOO_LONG || + n_id == 0) { + if (idents) + PFREE(idents); + return; + } + + if (idents) + PFREE(idents); + idents = PMALLOC(cb_id); + + if (kcdb_identity_enum(KCDB_IDENT_FLAG_CONFIG, + KCDB_IDENT_FLAG_CONFIG, + idents, + &cb_id, + &n_id) == KHM_ERROR_SUCCESS) + break; + } while(TRUE); + + for (t=idents; + t && *t; + t = multi_string_next(t)) { + + khm_handle h_id = NULL; + khm_handle csp_ident = NULL; + khm_handle csp_afs = NULL; + khm_size cb; + wchar_t vbuf[1024]; + wchar_t * tbuf = NULL; + + kcdb_identity_create(t, 0, &h_id); + if (h_id == NULL) { +#ifdef DEBUG + assert(FALSE); +#endif + continue; + } + + if (KHM_FAILED(kcdb_identity_get_config(h_id, 0, &csp_ident))) + goto _cleanup_loop; + + if (KHM_FAILED(khc_open_space(csp_ident, CSNAME_AFSCRED, + 0, &csp_afs))) + goto _cleanup_loop; + + if (khc_read_multi_string(csp_afs, L"Cells", NULL, &cb) + != KHM_ERROR_TOO_LONG) + goto _cleanup_loop; + + if (cb < sizeof(vbuf)) + tbuf = vbuf; + else + tbuf = PMALLOC(cb); + + if (khc_read_multi_string(csp_afs, L"Cells", tbuf, &cb) + != KHM_ERROR_SUCCESS) + goto _cleanup_loop; + + if (multi_string_find(tbuf, cell, 0) == NULL) + goto _cleanup_loop; + + multi_string_delete(tbuf, cell, 0); + + khc_write_multi_string(csp_afs, L"Cells", tbuf); + + _cleanup_loop: + kcdb_identity_release(h_id); + if (csp_ident) + khc_close_space(csp_ident); + if (csp_afs) + khc_close_space(csp_afs); + if (tbuf && tbuf != vbuf) + PFREE(tbuf); + } + + if (idents) + PFREE(idents); +} + +khm_boolean +afs_check_add_token_to_identity(wchar_t * cell, khm_handle ident, + khm_handle * ident_conflict) { + wchar_t * idents = NULL; + wchar_t * t; + khm_size cb_id; + khm_size n_id = 0; + khm_boolean ok_to_add = TRUE; + + /* check if this cell is listed for any other identity. */ + + do { + if (kcdb_identity_enum(KCDB_IDENT_FLAG_CONFIG, + KCDB_IDENT_FLAG_CONFIG, + NULL, + &cb_id, + &n_id) != KHM_ERROR_TOO_LONG || + n_id == 0) { + if (idents) + PFREE(idents); + return TRUE; + } + + if (idents) + PFREE(idents); + idents = PMALLOC(cb_id); + + if (kcdb_identity_enum(KCDB_IDENT_FLAG_CONFIG, + KCDB_IDENT_FLAG_CONFIG, + idents, + &cb_id, + &n_id) == KHM_ERROR_SUCCESS) + break; + } while(TRUE); + + for (t=idents; + ok_to_add && t && *t; + t = multi_string_next(t)) { + + khm_handle h_id = NULL; + khm_handle csp_ident = NULL; + khm_handle csp_afs = NULL; + khm_size cb; + wchar_t vbuf[1024]; + wchar_t * tbuf = NULL; + + kcdb_identity_create(t, 0, &h_id); + if (h_id == NULL) { +#ifdef DEBUG + assert(FALSE); +#endif + continue; + } + + if (kcdb_identity_is_equal(h_id, ident)) { + kcdb_identity_release(h_id); + continue; + } + + if (KHM_FAILED(kcdb_identity_get_config(h_id, 0, &csp_ident))) + goto _cleanup_loop; + + if (KHM_FAILED(khc_open_space(csp_ident, CSNAME_AFSCRED, + 0, &csp_afs))) + goto _cleanup_loop; + + if (khc_read_multi_string(csp_afs, L"Cells", NULL, &cb) + != KHM_ERROR_TOO_LONG) + goto _cleanup_loop; + + if (cb < sizeof(vbuf)) + tbuf = vbuf; + else + tbuf = PMALLOC(cb); + + if (khc_read_multi_string(csp_afs, L"Cells", tbuf, &cb) + != KHM_ERROR_SUCCESS) + goto _cleanup_loop; + + if (multi_string_find(tbuf, cell, 0) == NULL) + goto _cleanup_loop; + + /* we found another identity which gets tokens for the + same cell */ + + ok_to_add = FALSE; + + if (ident_conflict) { + *ident_conflict = h_id; + kcdb_identity_hold(h_id); + } + + _cleanup_loop: + kcdb_identity_release(h_id); + if (csp_ident) + khc_close_space(csp_ident); + if (csp_afs) + khc_close_space(csp_afs); + if (tbuf && tbuf != vbuf) + PFREE(tbuf); + } + + if (idents) + PFREE(idents); + + return ok_to_add; +} + +void +afs_cred_get_identity_creds(afs_cred_list * l, + khm_handle ident, + khm_boolean * penabled) { + khm_handle h_id = NULL; + khm_handle h_afs = NULL; + khm_handle h_cells = NULL; /* per identity cells space */ + khm_handle h_gcells = NULL; /* global cells space */ + khm_boolean load_defs = TRUE; + khm_size cbi; + wchar_t * ms = NULL; + wchar_t * s = NULL; + afs_ident_token_set ts; + khm_int32 t; + + if (penabled) + *penabled = TRUE; + + afs_cred_flush_rows(l); + + kcdb_identity_get_config(ident, 0, &h_id); + if(!h_id) + goto _done_config; + + if(KHM_FAILED(khc_open_space(h_id, CSNAME_AFSCRED, + 0, &h_afs))) + goto _done_config; + + if (penabled) { + t = 1; + if (KHM_FAILED(khc_read_int32(h_afs, L"AFSEnabled", &t))) + khc_read_int32(csp_params, L"AFSEnabled", &t); + *penabled = !!t; + } + + if(KHM_FAILED(khc_open_space(h_afs, L"Cells", + 0, &h_cells))) + goto _done_config; + + if(khc_read_multi_string(h_afs, L"Cells", NULL, &cbi) != + KHM_ERROR_TOO_LONG) + goto _done_config; + + load_defs = FALSE; + + ms = PMALLOC(cbi); + ZeroMemory(ms, cbi); + + khc_read_multi_string(h_afs, L"Cells", ms, &cbi); + + s = ms; + for(s = ms; s && *s; s = multi_string_next(s)) { + afs_cred_row * r; + size_t cb; + khm_handle h_cell = NULL; + + /* is this a valid cell name? */ + if(FAILED(StringCbLength(s, MAXCELLCHARS, &cb))) + continue; + cb += sizeof(wchar_t); + + r = afs_cred_get_new_row(l); + + r->cell = PMALLOC(cb); + StringCbCopy(r->cell, cb, s); + + r->realm = NULL; + r->method = 0; + r->flags = 0; + + if(KHM_SUCCEEDED(khc_open_space(h_cells, s, + 0, &h_cell))) { + khm_int32 i; + wchar_t wname[KHUI_MAXCCH_NAME]; + khm_size cb; + + if(khc_read_string(h_cell, L"Realm", + NULL, &cbi) == + KHM_ERROR_TOO_LONG && + cbi > sizeof(wchar_t)) { + + r->realm = PMALLOC(cbi); + khc_read_string(h_cell, L"Realm", r->realm, &cbi); + } + + i = AFS_TOKEN_AUTO; + + cb = sizeof(wname); + if (KHM_SUCCEEDED(khc_read_string(h_cell, L"MethodName", + wname, &cb))) { + + r->method = afs_get_method_id(wname); + + /* remove the deprecated value if it is present. */ + khc_remove_value(h_cell, L"Method", 0); + + } else if (KHM_SUCCEEDED(khc_read_int32(h_cell, + L"Method", &i))) { + /* the Method property is deprecated. We detect and + correct this whenever possible. */ + + if (!afs_is_valid_method_id(i)) + i = AFS_TOKEN_AUTO; + + r->method = i; + + afs_get_method_name(i, wname, sizeof(wname)); + + khc_write_string(h_cell, L"MethodName", + wname); + + khc_remove_value(h_cell, L"Method", 0); + } + + khc_close_space(h_cell); + } + } + + if(ms) { + PFREE(ms); + ms = NULL; + } + + _done_config: + + if (load_defs) { + /* We want to load defaults */ + char buf[MAXCELLCHARS]; + wchar_t wbuf[MAXCELLCHARS]; + wchar_t wmethod[KHUI_MAXCCH_NAME]; + wchar_t * defcells; + khm_size cb_defcells; + afs_cred_row * r; + khm_size sz; + + khc_open_space(csp_params, L"Cells", 0, &h_gcells); + + if(!cm_GetRootCellName(buf)) { + AnsiStrToUnicode(wbuf, sizeof(wbuf), buf); + + if (afs_check_add_token_to_identity(wbuf, ident, NULL)) { + khm_handle h_cell = NULL; + + r = afs_cred_get_new_row(l); + + StringCbLength(wbuf, sizeof(wbuf), &sz); + sz += sizeof(wchar_t); + + r->cell = PMALLOC(sz); + StringCbCopy(r->cell, sz, wbuf); + + if (h_gcells && + KHM_SUCCEEDED(khc_open_space(h_gcells, wbuf, 0, &h_cell))) { + khm_size cb; + + cb = sizeof(wmethod); + if (KHM_SUCCEEDED(khc_read_string(h_cell, L"MethodName", wmethod, &cb))) { + r->method = afs_get_method_id(wmethod); + } else { + r->method = AFS_TOKEN_AUTO; + } + + cb = sizeof(wbuf); + if (KHM_SUCCEEDED(khc_read_string(h_cell, L"Realm", wbuf, &cb))) { + r->realm = PMALLOC(cb); + StringCbCopy(r->realm, cb, wbuf); + } else { + r->realm = NULL; + } + + khc_close_space(h_cell); + + } else { + r->realm = NULL; + r->method = AFS_TOKEN_AUTO; + } + + r->flags = 0; + } + } + + if (khc_read_multi_string(csp_params, L"DefaultCells", + NULL, &cb_defcells) == KHM_ERROR_TOO_LONG && + cb_defcells > sizeof(wchar_t) * 2) { + wchar_t * c_cell; + + defcells = PMALLOC(cb_defcells); + if (defcells == NULL) + goto _done_defaults; + + if (KHM_FAILED(khc_read_multi_string(csp_params, L"DefaultCells", + defcells, &cb_defcells))) { + PFREE(defcells); + goto _done_defaults; + } + + for (c_cell = defcells; + c_cell && *c_cell; + c_cell = multi_string_next(c_cell)) { + + khm_size cb; + int i; + khm_handle h_cell = NULL; + afs_cred_row * r; + + if (FAILED(StringCbLength(c_cell, (MAXCELLCHARS + 1) * sizeof(wchar_t), + &cb))) + continue; + cb += sizeof(wchar_t); + + for (i=0; i < l->n_rows; i++) { + if (!_wcsicmp(l->rows[i].cell, c_cell)) + break; + } + + if (i < l->n_rows) + continue; + + r = afs_cred_get_new_row(l); + + r->cell = PMALLOC(cb); + StringCbCopy(r->cell, cb, c_cell); + + if (h_gcells && + KHM_SUCCEEDED(khc_open_space(h_gcells, c_cell, 0, &h_cell))) { + + cb = sizeof(wmethod); + if (KHM_SUCCEEDED(khc_read_string(h_cell, L"MethodName", wmethod, &cb))) { + r->method = afs_get_method_id(wmethod); + } else { + r->method = AFS_TOKEN_AUTO; + } + + cb = sizeof(wbuf); + if (KHM_SUCCEEDED(khc_read_string(h_cell, L"Realm", wbuf, &cb))) { + r->realm = PMALLOC(cb); + StringCbCopy(r->realm, cb, wbuf); + } else { + r->realm = NULL; + } + + khc_close_space(h_cell); + } else { + r->realm = NULL; + r->method = AFS_TOKEN_AUTO; + } + + r->flags = 0; + } + + PFREE(defcells); + } + } + + _done_defaults: + + ts.ident = ident; + ts.l = l; + ts.add_new = TRUE; + ts.update_info = FALSE; + + kcdb_credset_apply(NULL, afs_get_id_creds_apply_proc, + &ts); + + if(h_id) + khc_close_space(h_id); + if(h_afs) + khc_close_space(h_afs); + if(h_cells) + khc_close_space(h_cells); + if(h_gcells) + khc_close_space(h_gcells); +} + +void +nc_dlg_enable(HWND hwnd, BOOL enable) { + if(enable) { + SendDlgItemMessage(hwnd, IDC_NCAFS_OBTAIN, BM_SETCHECK, + BST_CHECKED, 0); + } else { + SendDlgItemMessage(hwnd, IDC_NCAFS_OBTAIN, BM_SETCHECK, + BST_UNCHECKED, 0); + } + + EnableWindow(GetDlgItem(hwnd,IDC_NCAFS_CELL), enable); + EnableWindow(GetDlgItem(hwnd,IDC_NCAFS_REALM), enable); + EnableWindow(GetDlgItem(hwnd,IDC_NCAFS_METHOD), enable); + EnableWindow(GetDlgItem(hwnd,IDC_NCAFS_TOKENLIST), enable); + EnableWindow(GetDlgItem(hwnd,IDC_NCAFS_ADD_TOKEN), enable); + EnableWindow(GetDlgItem(hwnd,IDC_NCAFS_DELETE_TOKEN), enable); +} + +void +nc_dlg_show_tooltip(HWND hwnd, + UINT_PTR id, + LPWSTR msg, + LPWSTR title, + int type, + int x, + int y) +{ + afs_dlg_data * d; + TOOLINFO ti; + + d = (afs_dlg_data *)(LONG_PTR) GetWindowLongPtr(hwnd, DWLP_USER); + + ZeroMemory(&ti, sizeof(ti)); + ti.cbSize = sizeof(ti); + ti.hwnd = hwnd; + ti.uId = id; + SendMessage(d->tooltip, TTM_GETTOOLINFO, 0, (LPARAM) &ti); + + ti.hinst = hResModule; + ti.lpszText = msg; + + SendMessage(d->tooltip, TTM_SETTOOLINFO, 0, (LPARAM) &ti); + + if(IS_INTRESOURCE(title)) { + wchar_t wbuf[1024]; + UINT resid; + + resid = (UINT)(UINT_PTR) title; + + LoadString(hResModule, resid, wbuf, ARRAYLENGTH(wbuf)); + SendMessage(d->tooltip, TTM_SETTITLE, type, (LPARAM) wbuf); + } else + SendMessage(d->tooltip, TTM_SETTITLE, type, (LPARAM) title); + + SendMessage(d->tooltip, TTM_TRACKACTIVATE, TRUE, (LPARAM) &ti); + SendMessage(d->tooltip, TTM_TRACKPOSITION, 0, (LPARAM) MAKELONG(x,y)); + + d->tooltip_visible = TRUE; + + SetTimer(hwnd, DLG_TOOLTIP_TIMER_ID, DLG_TOOLTIP_TIMEOUT, NULL); +} + +void +nc_dlg_hide_tooltip(HWND hwnd, UINT_PTR id) +{ + TOOLINFO ti; + afs_dlg_data * d; + + d = (afs_dlg_data *)(LONG_PTR) GetWindowLongPtr(hwnd, DWLP_USER); + + if(!d->tooltip_visible) + return; + + ZeroMemory(&ti, sizeof(ti)); + ti.cbSize = sizeof(ti); + ti.hwnd = hwnd; + ti.uId = id; + + SendMessage(d->tooltip, TTM_TRACKACTIVATE, FALSE, (LPARAM) &ti); + d->tooltip_visible = FALSE; +} + +void +afs_dlg_update_rows(HWND hwnd, afs_dlg_data * d) { + HWND hwlist; + LVITEM lvi; + wchar_t wauto[256]; + int i; + + CheckDlgButton(hwnd, IDC_NCAFS_OBTAIN, + (d->afs_enabled)? BST_CHECKED: BST_UNCHECKED); + + hwlist = GetDlgItem(hwnd, IDC_NCAFS_TOKENLIST); + + ListView_DeleteAllItems(hwlist); + + if(d->creds.n_rows == 0) + return; + + LoadString(hResModule, IDS_NC_AUTO, wauto, ARRAYLENGTH(wauto)); + + for(i=0; i < d->creds.n_rows; i++) { + wchar_t wbuf[256]; + int flags; + + ZeroMemory(&lvi, sizeof(lvi)); + + lvi.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE; + lvi.iItem = d->creds.n_rows + 1; + + lvi.stateMask = LVIS_STATEIMAGEMASK; + flags = d->creds.rows[i].flags; + if ((flags & DLGROW_FLAG_EXISTS) && + (flags & DLGROW_FLAG_NOTOWNED)) { + lvi.state = INDEXTOSTATEIMAGEMASK(d->idx_bad_token); + } else if ((flags & DLGROW_FLAG_EXISTS)) { + lvi.state = INDEXTOSTATEIMAGEMASK(d->idx_existing_token); + } else { + lvi.state = INDEXTOSTATEIMAGEMASK(d->idx_new_token); + } + + lvi.lParam = (LPARAM) i; + + lvi.iSubItem = NCAFS_IDX_CELL; + lvi.pszText = d->creds.rows[i].cell; + + lvi.iItem = ListView_InsertItem(hwlist, &lvi); + + lvi.mask = LVIF_TEXT; /* subitems dislike lParam */ + lvi.iSubItem = NCAFS_IDX_REALM; + if(d->creds.rows[i].realm != NULL) + lvi.pszText = d->creds.rows[i].realm; + else + lvi.pszText = wauto; + ListView_SetItem(hwlist, &lvi); + + lvi.iSubItem = NCAFS_IDX_METHOD; + afs_method_describe(d->creds.rows[i].method, + KCDB_TS_SHORT, + wbuf, sizeof(wbuf)); + lvi.pszText = wbuf; + + ListView_SetItem(hwlist, &lvi); + } +} + +void +nc_dlg_del_token(HWND hwnd) { + afs_dlg_data * d; + khui_new_creds_by_type * nct; + + d = (afs_dlg_data *)(LONG_PTR) GetWindowLongPtr(hwnd, DWLP_USER); + + if (d->nc) + khui_cw_find_type(d->nc, afs_credtype_id, &nct); + + if(ListView_GetSelectedCount(GetDlgItem(hwnd, IDC_NCAFS_TOKENLIST)) == 0) { + wchar_t cell[KCDB_MAXCCH_NAME]; + int i; + + /* nothing is selected in the list view */ + /* we delete the row that matches the current contents of the + cell edit control */ + cell[0] = 0; + GetDlgItemText(hwnd, IDC_NCAFS_CELL, cell, ARRAYLENGTH(cell)); + for(i=0; icreds.n_rows; i++) { + if(!_wcsicmp(d->creds.rows[i].cell, cell)) { + /* found it */ + afs_cred_delete_row(&d->creds, i); + afs_dlg_update_rows(hwnd, d); + d->dirty = TRUE; + break; + } + } + } else { + /* something is selected in the token list view */ + /* we delete that */ + HWND hw; + LVITEM lvi; + int idx; + int row; + BOOL deleted = FALSE; + + hw = GetDlgItem(hwnd, IDC_NCAFS_TOKENLIST); + idx = -1; + do { + idx = ListView_GetNextItem(hw, idx, LVNI_SELECTED); + if(idx >= 0) { + ZeroMemory(&lvi, sizeof(lvi)); + lvi.iItem = idx; + lvi.iSubItem = 0; + lvi.mask = LVIF_PARAM; + if(!ListView_GetItem(hw, &lvi)) + continue; + row = (int) lvi.lParam; + if(row >= 0 && row < d->creds.n_rows) { + d->creds.rows[row].flags |= DLGROW_FLAG_DELETED; + deleted = TRUE; + } + } + } while(idx != -1); + + if(deleted) { + for(idx = 0; idx < d->creds.n_rows; idx ++) { + if(d->creds.rows[idx].flags & DLGROW_FLAG_DELETED) { + afs_cred_delete_row(&d->creds, idx); + idx--; /* we have to look at the current item again */ + } + } + + d->dirty = TRUE; + afs_dlg_update_rows(hwnd, d); + } + } + + if (d->nc) + SendMessage(d->nc->hwnd, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_UPDATE_CREDTEXT), 0); + else if (d->config_dlg && d->dirty) + khui_cfg_set_flags_inst(&d->cfg, KHUI_CNFLAG_MODIFIED, + KHUI_CNFLAG_MODIFIED); +} + +void +nc_dlg_add_token(HWND hwnd) { + afs_dlg_data * d; + afs_cred_row * prow; + afs_cred_row trow; + khui_new_creds_by_type * nct; + wchar_t buf[256]; + int idx; + size_t n; + size_t cb; + int i; + BOOL new_row = FALSE; + khm_handle ident = NULL; + + d = (afs_dlg_data *)(LONG_PTR) GetWindowLongPtr(hwnd, DWLP_USER); + + if (d->nc) + khui_cw_find_type(d->nc, afs_credtype_id, &nct); + else + nct = NULL; + + if((n = SendDlgItemMessage(hwnd, IDC_NCAFS_CELL, WM_GETTEXT, + (WPARAM) ARRAYLENGTH(buf), (LPARAM) buf)) + == 0) + { + /* probably should indicate that user should type something */ + RECT r; + GetWindowRect(GetDlgItem(hwnd, IDC_NCAFS_CELL), &r); + nc_dlg_show_tooltip(hwnd, + 0, + MAKEINTRESOURCE(IDS_NC_TT_NO_CELL), + MAKEINTRESOURCE(IDS_NC_TT_CANT_ADD), + 2, (r.left + r.right)/ 2, r.bottom); + return; + } + + if(n != wcsspn(buf, AFS_VALID_CELL_CHARS)) { + RECT r; + GetWindowRect(GetDlgItem(hwnd, IDC_NCAFS_CELL), &r); + nc_dlg_show_tooltip(hwnd, + 0, + MAKEINTRESOURCE(IDS_NC_TT_MALFORMED_CELL), + MAKEINTRESOURCE(IDS_NC_TT_CANT_ADD), + 2, (r.left + r.right)/2, r.bottom); + return; + } + + /* check if this is already listed */ + for(i=0;icreds.n_rows;i++) { + if(!_wcsicmp(buf, d->creds.rows[i].cell)) + break; + } + + if(i < d->creds.n_rows) { + new_row = FALSE; + + prow = &(d->creds.rows[i]); + } else { + new_row = TRUE; + prow = NULL; + } + + ZeroMemory(&trow, sizeof(trow)); + + cb = (n+1) * sizeof(wchar_t); + trow.cell = PMALLOC(cb); + StringCbCopy(trow.cell, cb, buf); + + /* now for the realm */ + do { + idx = (int) SendDlgItemMessage(hwnd, IDC_NCAFS_REALM, + CB_GETCURSEL, 0, 0); + if(idx != CB_ERR) { + int lp; + lp = (int) SendDlgItemMessage(hwnd, IDC_NCAFS_REALM, + CB_GETITEMDATA, idx, 0); + if(lp != CB_ERR && lp) /* this is the 'determine realm + automatically' item */ + { + trow.realm = NULL; + break; + } + } + + if((n = SendDlgItemMessage(hwnd, IDC_NCAFS_REALM, WM_GETTEXT, + ARRAYLENGTH(buf), (LPARAM) buf)) == 0) { + RECT r; + GetWindowRect(GetDlgItem(hwnd, IDC_NCAFS_REALM), &r); + nc_dlg_show_tooltip(hwnd, + 0, + MAKEINTRESOURCE(IDS_NC_TT_NO_REALM), + MAKEINTRESOURCE((new_row)? + IDS_NC_TT_CANT_ADD: + IDS_NC_TT_CANT_UPDATE), + 2, (r.left + r.right)/2, r.bottom); + goto _error_exit; + } + + if(n != wcsspn(buf, AFS_VALID_REALM_CHARS)) { + RECT r; + GetWindowRect(GetDlgItem(hwnd, IDC_NCAFS_REALM), &r); + nc_dlg_show_tooltip(hwnd, + 0, + MAKEINTRESOURCE(IDS_NC_TT_MALFORMED_REALM), + MAKEINTRESOURCE((new_row)? + IDS_NC_TT_CANT_ADD: + IDS_NC_TT_CANT_UPDATE), + 2, (r.left + r.right)/2, r.bottom); + goto _error_exit; + } + + cb = (n+1) * sizeof(wchar_t); + trow.realm = PMALLOC(cb); + StringCbCopy(trow.realm, cb, buf); + + } while(FALSE); + + idx = (int)SendDlgItemMessage(hwnd, IDC_NCAFS_METHOD, + CB_GETCURSEL, 0, 0); + if (idx != CB_ERR) { + trow.method = (afs_tk_method) + SendDlgItemMessage(hwnd, IDC_NCAFS_METHOD, CB_GETITEMDATA, + idx, 0); + } else { + trow.method = AFS_TOKEN_AUTO; + } + + if (d->nc && + d->nc->n_identities > 0 && + d->nc->identities[0]) { + + ident = d->nc->identities[0]; + + } else if (d->ident) { + + ident = d->ident; + + } + + if(new_row) { + khm_boolean ok_to_add = TRUE; + + if (ident) { + khm_handle id_conf = NULL; + + ok_to_add = + afs_check_add_token_to_identity(trow.cell, + ident, + &id_conf); + + if (!ok_to_add) { +#if KH_VERSION_API >= 5 + khui_alert * a; + wchar_t wbuf[512]; + wchar_t wfmt[128]; + wchar_t widname[KCDB_IDENT_MAXCCH_NAME]; + khm_size cb; + +#ifdef DEBUG + assert(id_conf); +#endif + khui_alert_create_empty(&a); + + cb = sizeof(widname); + kcdb_identity_get_name(id_conf, widname, &cb); + + LoadString(hResModule, IDS_NC_TT_CONFLICT, + wfmt, ARRAYLENGTH(wfmt)); + StringCbPrintf(wbuf, sizeof(wbuf), + wfmt, trow.cell, widname); + khui_alert_set_message(a, wbuf); + + LoadString(hResModule, IDS_NC_TT_PROBLEM, + wbuf, ARRAYLENGTH(wbuf)); + khui_alert_set_title(a, wbuf); + + khui_alert_add_command(a, KHUI_PACTION_KEEP); + khui_alert_add_command(a, KHUI_PACTION_REMOVE); + khui_alert_add_command(a, KHUI_PACTION_CANCEL); + + khui_alert_set_severity(a, KHERR_INFO); + + khui_alert_show_modal(a); + + ok_to_add = TRUE; + + if (a->response == KHUI_PACTION_REMOVE) { + afs_remove_token_from_identities(trow.cell); + } else if (a->response == KHUI_PACTION_CANCEL) { + ok_to_add = FALSE; + } + + khui_alert_release(a); +#else + wchar_t widname[KCDB_IDENT_MAXCCH_NAME]; + wchar_t wtitle[64]; + wchar_t wmsg[512]; + wchar_t wfmt[128]; + khm_size cb; + int r; + +#ifdef DEBUG + assert(id_conf); +#endif + + cb = sizeof(widname); + kcdb_identity_get_name(id_conf, widname, &cb); + LoadString(hResModule, IDS_NC_TT_PROBLEM, + wtitle, ARRAYLENGTH(wtitle)); + LoadString(hResModule, IDS_NC_TT_CONFLICTM, + wfmt, ARRAYLENGTH(wfmt)); + StringCbPrintf(wmsg, sizeof(wmsg), wfmt, + trow.cell, widname); + r = MessageBox(NULL, wmsg, wtitle, + MB_YESNOCANCEL | MB_ICONWARNING | + MB_APPLMODAL); + + ok_to_add = TRUE; + if (r == IDNO) { + afs_remove_token_from_identities(trow.cell); + } else if (r == IDCANCEL) { + ok_to_add = FALSE; + } +#endif + + kcdb_identity_release(id_conf); + } + + if (!ok_to_add) + goto _error_exit; + } + + prow = afs_cred_get_new_row(&d->creds); + } else { + if (prow->cell) + PFREE(prow->cell); + + if(prow->realm) + PFREE(prow->realm); + + ZeroMemory(prow, sizeof(*prow)); + } + + *prow = trow; + + if (ident) { + afs_ident_token_set ts; + + ts.ident = ident; + ts.l = &d->creds; + ts.add_new = FALSE; + ts.update_info = FALSE; + + kcdb_credset_apply(NULL, afs_get_id_creds_apply_proc, + &ts); + } + + afs_dlg_update_rows(hwnd, d); + + d->dirty = TRUE; + + if (d->nc) + SendMessage(d->nc->hwnd, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_UPDATE_CREDTEXT), 0); + else if (d->config_dlg) { + khui_cfg_set_flags_inst(&d->cfg, + KHUI_CNFLAG_MODIFIED, + KHUI_CNFLAG_MODIFIED); + } + + return; + +_error_exit: + if(trow.realm) + PFREE(trow.realm); + if(trow.cell) + PFREE(trow.cell); +} + +/* this is shared between the new credentials window and the AFS per + identity configuration dialog. */ +INT_PTR CALLBACK +afs_dlg_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + switch(uMsg) { + case WM_INITDIALOG: + { + HWND hw; + HIMAGELIST hw_ilist; + afs_dlg_data * d; + khui_new_creds_by_type * nct = NULL; + RECT r; + + d = PMALLOC(sizeof(*d)); + ZeroMemory(d, sizeof(*d)); + + InitializeCriticalSection(&d->cs); + + /* lParam is a pointer to a khui_new_creds structure */ + d->nc = (khui_new_creds *) lParam; + + if (d->nc) + khui_cw_find_type(d->nc, afs_credtype_id, &nct); + +#pragma warning(push) +#pragma warning(disable: 4244) + SetWindowLongPtr(hwnd, DWLP_USER, (LPARAM) d); +#pragma warning(pop) + + EnterCriticalSection(&d->cs); + + if (nct) + nct->aux = (LPARAM) d; + + /* create the tooltip window */ + d->tooltip = + CreateWindowEx(WS_EX_TOPMOST, + TOOLTIPS_CLASS, + NULL, + WS_POPUP | TTS_BALLOON | TTS_ALWAYSTIP, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + hwnd, /* make this an owned window, so + we don't have to worry about + destroying it */ + NULL, + hInstance, + NULL); + + SetWindowPos(d->tooltip, + HWND_TOPMOST, + 0, + 0, + 0, + 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + + { + TOOLINFO ti; + + ZeroMemory(&ti, sizeof(ti)); + ti.cbSize = sizeof(ti); + ti.uFlags = TTF_TRACK; + ti.hwnd = hwnd; + ti.uId = 0; + ti.hinst = hResModule; + ti.lpszText = L""; + GetClientRect(hwnd, &(ti.rect)); + + SendMessage(d->tooltip, TTM_ADDTOOL, 0, (LPARAM) &ti); + } + + /* we only initialize the constant bits here. */ + hw = GetDlgItem(hwnd, IDC_NCAFS_TOKENLIST); + + GetClientRect(hw, &r); + + /* set the list view status icons */ + hw_ilist = ImageList_Create(GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON), + ILC_COLOR8 | ILC_MASK, + 4, 4); +#ifdef DEBUG + assert(hw_ilist); +#endif + { + HICON hi; + + hi = LoadImage(hResModule, MAKEINTRESOURCE(IDI_NC_NEW), + IMAGE_ICON, + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON), + LR_DEFAULTCOLOR); + + d->idx_new_token = ImageList_AddIcon(hw_ilist, hi) + 1; + + DestroyIcon(hi); + + hi = LoadImage(hResModule, MAKEINTRESOURCE(IDI_NC_EXIST), + IMAGE_ICON, + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON), + LR_DEFAULTCOLOR); + d->idx_existing_token = ImageList_AddIcon(hw_ilist, hi) + 1; + + DestroyIcon(hi); + + hi = LoadImage(hResModule, + MAKEINTRESOURCE(IDI_NC_NOTOWNED), + IMAGE_ICON, + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON), + LR_DEFAULTCOLOR); + d->idx_bad_token = ImageList_AddIcon(hw_ilist, hi) + 1 ; + + DestroyIcon(hi); + } + + ListView_SetImageList(hw, hw_ilist, LVSIL_STATE); + + ListView_DeleteAllItems(hw); + + /* set the columns */ + { + LVCOLUMN lc; + wchar_t wbuf[256]; + + lc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT; + lc.fmt = LVCFMT_LEFT; + lc.cx = ((r.right - r.left) * 2) / 5; + LoadString(hResModule, IDS_NCAFS_COL_CELL, + wbuf, ARRAYLENGTH(wbuf)); + lc.pszText = wbuf; + + ListView_InsertColumn(hw, 0, &lc); + + lc.mask |= LVCF_SUBITEM; + //lc.cx is the same as above + lc.iSubItem = NCAFS_IDX_REALM; + LoadString(hResModule, IDS_NCAFS_COL_REALM, + wbuf, ARRAYLENGTH(wbuf)); + + ListView_InsertColumn(hw, 1, &lc); + + lc.cx = ((r.right - r.left) * 1) / 5; + lc.iSubItem = NCAFS_IDX_METHOD; + LoadString(hResModule, IDS_NCAFS_COL_METHOD, + wbuf, ARRAYLENGTH(wbuf)); + + ListView_InsertColumn(hw, 2, &lc); + } + + /* Set the items for the 'method' combo box */ + hw = GetDlgItem(hwnd, IDC_NCAFS_METHOD); + + { + wchar_t wbuf[KHUI_MAXCB_SHORT_DESC]; + afs_tk_method method = -1; + int idx; + + SendMessage(hw, CB_RESETCONTENT, 0, 0); + + while((method = afs_get_next_method_id(method)) >= 0) { + afs_method_describe(method, KCDB_TS_SHORT, + wbuf, sizeof(wbuf)); + idx = (int)SendMessage(hw, CB_INSERTSTRING, + (WPARAM) -1, (LPARAM) wbuf); +#ifdef DEBUG + assert(idx != CB_ERR); +#endif + SendMessage(hw, CB_SETITEMDATA, (WPARAM) idx, + (LPARAM) method); + } + + /* finally, set the current selection to auto, which + is the first method returned by + afs_get_next_method_id() */ + SendMessage(hw, CB_SETCURSEL, 0, 0); + } + + d->afs_enabled = TRUE; + SendDlgItemMessage(hwnd, IDC_NCAFS_OBTAIN, + BM_SETCHECK, BST_CHECKED, 0); + + LeaveCriticalSection(&d->cs); + + /* the cells and realms combo boxes need to be filled + in the plugin thread since that requires making + potentially blocking and non-thread safe calls */ + } + return TRUE; + + case WM_DESTROY: + { + afs_dlg_data * d; + khui_new_creds_by_type * nct; + + d = (afs_dlg_data *)(LONG_PTR) + GetWindowLongPtr(hwnd, DWLP_USER); + + EnterCriticalSection(&d->cs); + + if (d->nc) { + khui_cw_find_type(d->nc, afs_credtype_id, &nct); + + nct->aux = (LPARAM) NULL; + } + + afs_cred_free_rows(&d->creds); + + LeaveCriticalSection(&d->cs); + DeleteCriticalSection(&d->cs); + + PFREE(d); + } + return TRUE; + + case WM_COMMAND: + { + afs_dlg_data * d; + khui_new_creds_by_type * nct; + + d = (afs_dlg_data *)(LONG_PTR) + GetWindowLongPtr(hwnd, DWLP_USER); + + EnterCriticalSection(&d->cs); + + if (d->nc) + khui_cw_find_type(d->nc, afs_credtype_id, &nct); + else + nct = NULL; + + nc_dlg_hide_tooltip(hwnd, 0); + + /* Handle WM_COMMAND */ + switch(wParam) { + case MAKEWPARAM(IDC_NCAFS_OBTAIN, BN_CLICKED): + { + BOOL c; + c = (SendDlgItemMessage(hwnd, IDC_NCAFS_OBTAIN, + BM_GETCHECK, 0, 0) + == BST_CHECKED); + d->afs_enabled = c; + d->dirty = TRUE; + if (d->nc) + khui_cw_enable_type(d->nc, afs_credtype_id, c); + else if (d->config_dlg) + khui_cfg_set_flags_inst(&d->cfg, + KHUI_CNFLAG_MODIFIED, + KHUI_CNFLAG_MODIFIED); + nc_dlg_enable(hwnd, c); + } + break; + + case MAKEWPARAM(IDC_NCAFS_ADD_TOKEN, BN_CLICKED): + { + nc_dlg_add_token(hwnd); + } + break; + + case MAKEWPARAM(IDC_NCAFS_DELETE_TOKEN, BN_CLICKED): + { + nc_dlg_del_token(hwnd); + } + break; + } + + LeaveCriticalSection(&d->cs); + } + return TRUE; + + case KHUI_WM_NC_NOTIFY: + { + afs_dlg_data * d; + khui_new_creds_by_type * nct; + + d = (afs_dlg_data *)(LONG_PTR) + GetWindowLongPtr(hwnd, DWLP_USER); + + EnterCriticalSection(&d->cs); + + if (d->nc) + khui_cw_find_type(d->nc, afs_credtype_id, &nct); + else + nct = NULL; + + switch(HIWORD(wParam)) { + case WMNC_DIALOG_SETUP: + { + SendDlgItemMessage(hwnd, IDC_NCAFS_CELL, + CB_RESETCONTENT, 0, 0); + + /* load the LRU cells */ + { + wchar_t * buf; + wchar_t *s; + khm_size cbbuf; + + if(khc_read_multi_string(csp_params, L"LRUCells", + NULL, &cbbuf) == + KHM_ERROR_TOO_LONG) { + buf = PMALLOC(cbbuf); + khc_read_multi_string(csp_params, L"LRUCells", + buf, &cbbuf); + s = buf; + while(*s) { + SendDlgItemMessage(hwnd, IDC_NCAFS_CELL, + CB_ADDSTRING, 0, (LPARAM) s); + s += wcslen(s) + 1; + } + PFREE(buf); + } + } + + /* now, if the root cell is not in the LRU, add it */ + { + char buf[256]; + wchar_t wbuf[256]; + + if(!cm_GetRootCellName(buf)) { + AnsiStrToUnicode(wbuf, sizeof(wbuf), buf); + if(SendDlgItemMessage(hwnd, + IDC_NCAFS_CELL, + CB_FINDSTRINGEXACT, + (WPARAM) -1, + (LPARAM) wbuf) == CB_ERR) { + SendDlgItemMessage(hwnd, IDC_NCAFS_CELL, + CB_ADDSTRING, + 0, (LPARAM) wbuf); + } + SendDlgItemMessage(hwnd, IDC_NCAFS_CELL, + CB_SELECTSTRING, + -1, (LPARAM) wbuf); + } + } + + SendDlgItemMessage(hwnd, IDC_NCAFS_REALM, + CB_RESETCONTENT, 0, 0); + + /* as for the realms, we have a special one here */ + { + wchar_t wbuf[256]; + int idx; + + LoadString(hResModule, IDS_NC_REALM_AUTO, wbuf, + (int) ARRAYLENGTH(wbuf)); + idx = (int) SendDlgItemMessage(hwnd, IDC_NCAFS_REALM, + CB_ADDSTRING, 0, + (LPARAM) wbuf); + /* item data for the realm strings is the + answer to the question, "is this the + 'determine realm automatically' item?" */ + SendDlgItemMessage(hwnd, IDC_NCAFS_REALM, + CB_SETITEMDATA, idx, TRUE); + SendDlgItemMessage(hwnd, IDC_NCAFS_REALM, + CB_SELECTSTRING, + -1, (LPARAM) wbuf); + } + + /* load the LRU realms */ + { + wchar_t * buf; + wchar_t *s; + int idx; + khm_size cbbuf; + + if(khc_read_multi_string(csp_params, L"LRURealms", + NULL, &cbbuf) == + KHM_ERROR_TOO_LONG) { + buf = PMALLOC(cbbuf); + khc_read_multi_string(csp_params, L"LRURealms", + buf, &cbbuf); + s = buf; + while(*s) { + if(SendDlgItemMessage(hwnd, IDC_NCAFS_REALM, + CB_FINDSTRINGEXACT, -1, + (LPARAM) s) == CB_ERR) { + idx = + (int) + SendDlgItemMessage(hwnd, + IDC_NCAFS_REALM, + CB_ADDSTRING, + 0, (LPARAM) s); + SendDlgItemMessage(hwnd, IDC_NCAFS_REALM, + CB_SETITEMDATA, + idx, FALSE); + } + + s += wcslen(s) + 1; + } + PFREE(buf); + } + } + + if (d->nc) + khui_cw_enable_type(d->nc, afs_credtype_id, + d->afs_enabled); + + nc_dlg_enable(hwnd, d->afs_enabled); + + afs_dlg_update_rows(hwnd, d); + } + break; + + case WMNC_UPDATE_CREDTEXT: + { + wchar_t wformat[256]; + wchar_t wstr[2048]; + khm_int32 flags; + + if(nct->credtext) { + PFREE(nct->credtext); + nct->credtext = NULL; + } + +#ifdef DEBUG + assert(d->nc); +#endif + + if (d->nc->n_identities == 0 || + KHM_FAILED(kcdb_identity_get_flags(d->nc->identities[0], + &flags)) || + !(flags & KCDB_IDENT_FLAG_VALID)) + /* in this case, we don't show any credential text */ + break; + + wstr[0] = 0; + + if(!d->afs_enabled) { + LoadString(hResModule, IDS_AFS_CREDTEXT_DIS, + wstr, ARRAYLENGTH(wstr)); + } else { + if(d->creds.n_rows == 0) { + LoadString(hResModule, IDS_AFS_CREDTEXT_0, + wstr, ARRAYLENGTH(wstr)); + } else if(d->creds.n_rows == 1) { + LoadString(hResModule, IDS_AFS_CREDTEXT_1, + wformat, ARRAYLENGTH(wformat)); + StringCbPrintf(wstr, sizeof(wstr), wformat, + d->creds.rows[0].cell); + } else { + int i; + wchar_t wcells[1024]; + + LoadString(hResModule, IDS_AFS_CREDTEXT_N, + wformat, ARRAYLENGTH(wformat)); + wcells[0] = 0; + for(i=0; icreds.n_rows; i++) { + if(i > 0) + StringCbCat(wcells, sizeof(wcells), + L", "); + if(FAILED(StringCbCat(wcells, + sizeof(wcells), + d->creds.rows[i].cell))) { + size_t cch; + /* looks like we overflowed */ + /* add an ellipsis at the end */ + StringCchLength(wcells, ARRAYLENGTH(wcells), &cch); + cch = min(ARRAYLENGTH(wcells) - 4, cch); + StringCchCopy(wcells + cch, 4, L"..."); + + break; + } + } + + StringCbPrintf(wstr, sizeof(wstr), wformat, wcells); + } + } + + if(wstr[0] != 0) { + size_t cbs; + StringCbLength(wstr, sizeof(wstr), &cbs); + cbs += sizeof(wchar_t); + assert(nct->credtext == NULL); + nct->credtext = PMALLOC(cbs); + StringCbCopy(nct->credtext, cbs, wstr); + } else { + /* something went wrong */ + nct->credtext = NULL; + } + } + break; + + case WMNC_CREDTEXT_LINK: + { + khui_htwnd_link * l; + wchar_t wid[KHUI_MAXCCH_HTLINK_FIELD]; + wchar_t * wids; + + l = (khui_htwnd_link *) lParam; + + StringCchCopyN(wid, ARRAYLENGTH(wid), l->id, l->id_len); + wids = wcschr(wid, L':'); + + if(!wids) + break; + else + wids++; + +#ifdef DEBUG + assert(d->nc); +#endif + + if(!wcscmp(wids, L"Enable")) { + SendDlgItemMessage(hwnd, IDC_NCAFS_OBTAIN, + BM_SETCHECK, BST_CHECKED, 0); + d->afs_enabled = TRUE; + khui_cw_enable_type(d->nc, afs_credtype_id, TRUE); + nc_dlg_enable(hwnd, TRUE); + } + } + break; + + case WMNC_IDENTITY_CHANGE: + kmq_post_sub_msg(afs_sub, KMSG_CRED, + KMSG_CRED_DIALOG_NEW_IDENTITY, 0, + (void *) d->nc); + break; + + case WMNC_AFS_UPDATE_ROWS: + afs_dlg_update_rows(hwnd, d); + +#ifdef DEBUG + assert(d->nc); +#endif + + PostMessage(d->nc->hwnd, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_UPDATE_CREDTEXT), 0); + break; + } + + LeaveCriticalSection(&d->cs); + } + return TRUE; + + case WM_NOTIFY: + if(wParam == IDC_NCAFS_TOKENLIST) { + LPNMHDR lpnmh = (LPNMHDR) lParam; + + if(lpnmh->code == LVN_ITEMCHANGED) { + /* when an item in the list view is clicked, we + load the corresponding values into the edit and + combo boxes */ + NMLISTVIEW *lpnmlv = (NMLISTVIEW *) lpnmh; + LVITEM lvi; + HWND hw; + int idx; + int row; + afs_dlg_data * d; + + if (!(lpnmlv->uChanged & LVIF_STATE) || + !(lpnmlv->uNewState & LVIS_SELECTED) || + (lpnmlv->iItem == -1)) + + return TRUE; + + d = (afs_dlg_data *)(LONG_PTR) + GetWindowLongPtr(hwnd, DWLP_USER); + + EnterCriticalSection(&d->cs); + + hw = GetDlgItem(hwnd, IDC_NCAFS_TOKENLIST); + + idx = lpnmlv->iItem; + + ZeroMemory(&lvi, sizeof(lvi)); + lvi.iItem = idx; + lvi.iSubItem = 0; + lvi.mask = LVIF_PARAM; + + if(!ListView_GetItem(hw, &lvi)) + goto _done_notify_select; + + /* ok, now lvi.lParam should be the row of the token */ + row = (int) lvi.lParam; + if(row < 0 || row >= d->creds.n_rows) + goto _done_notify_select; + + SetDlgItemText(hwnd, IDC_NCAFS_CELL, + d->creds.rows[row].cell); + if(d->creds.rows[row].realm != NULL) { + SetDlgItemText(hwnd, IDC_NCAFS_REALM, + d->creds.rows[row].realm); + } else { + wchar_t wbuf[256]; + int idx; + + LoadString(hResModule, IDS_NC_REALM_AUTO, wbuf, + ARRAYLENGTH(wbuf)); + idx = (int) SendDlgItemMessage(hwnd, IDC_NCAFS_REALM, + CB_FINDSTRINGEXACT, -1, + (LPARAM) wbuf); + SendDlgItemMessage(hwnd, IDC_NCAFS_REALM, CB_SETCURSEL, + idx, 0); + } + SendDlgItemMessage(hwnd, IDC_NCAFS_METHOD, CB_SETCURSEL, + d->creds.rows[row].method, 0); + _done_notify_select: + LeaveCriticalSection(&d->cs); + + } else if (lpnmh->code == NM_DBLCLK) { + + LPNMITEMACTIVATE pnmi; + LVITEM lvi; + HWND hw; + afs_dlg_data * d; + int row; + int x,y; + RECT r; + + d = (afs_dlg_data *)(LONG_PTR) + GetWindowLongPtr(hwnd, DWLP_USER); + + EnterCriticalSection(&d->cs); + + pnmi = (LPNMITEMACTIVATE) lpnmh; + + hw = GetDlgItem(hwnd, IDC_NCAFS_TOKENLIST); + + ZeroMemory(&lvi, sizeof(lvi)); + lvi.iItem = pnmi->iItem; + lvi.iSubItem = 0; + lvi.mask = LVIF_PARAM; + + if (!ListView_GetItem(hw, &lvi)) + goto _done_notify_click; + + row = (int) lvi.lParam; + if(row < 0 || row >= d->creds.n_rows) + goto _done_notify_click; + + ListView_GetItemRect(hw, pnmi->iItem, &r, LVIR_SELECTBOUNDS); + x = (r.left + r.right) / 2; + y = (r.bottom); + + GetWindowRect(hw, &r); + y += r.top; + x += r.left; + + if (d->creds.rows[row].flags & DLGROW_FLAG_NOTOWNED) { + nc_dlg_show_tooltip(hwnd, 0, + MAKEINTRESOURCE(IDS_NC_TT_CONFLICTD), + MAKEINTRESOURCE(IDS_NC_TT_PROBLEM), + 2, + x,y); + } else if (d->creds.rows[row].flags & + DLGROW_FLAG_EXPIRED) { + nc_dlg_show_tooltip(hwnd, 0, + MAKEINTRESOURCE(IDS_NC_TT_EXPIRED), + MAKEINTRESOURCE(IDS_NC_TT_DETAILS), + 1, + x, y); + } else if (d->creds.rows[row].flags & + DLGROW_FLAG_EXISTS) { + nc_dlg_show_tooltip(hwnd, 0, + MAKEINTRESOURCE(IDS_NC_TT_EXISTS), + MAKEINTRESOURCE(IDS_NC_TT_DETAILS), + 1, x, y); + } else { + nc_dlg_show_tooltip(hwnd, 0, + MAKEINTRESOURCE(IDS_NC_TT_NEW), + MAKEINTRESOURCE(IDS_NC_TT_DETAILS), + 1, x, y); + } + + _done_notify_click: + LeaveCriticalSection(&d->cs); + } + } + return TRUE; + + case WM_TIMER: + { + if(wParam == DLG_TOOLTIP_TIMER_ID) { + KillTimer(hwnd, DLG_TOOLTIP_TIMER_ID); + nc_dlg_hide_tooltip(hwnd, 0); + } + } + return TRUE; + + case WM_HELP: + { + static const DWORD ctx_help[] = { + IDC_NCAFS_OBTAIN, IDH_OBTAIN, + IDC_NCAFS_CELL, IDH_CELL, + IDC_NCAFS_REALM, IDH_REALM, + IDC_NCAFS_METHOD, IDH_METHOD, + IDC_NCAFS_ADD_TOKEN, IDH_ADD, + IDC_NCAFS_DELETE_TOKEN, IDH_DELETE, + IDC_NCAFS_TOKENLIST, IDH_TOKENLIST, + 0 + }; + + LPHELPINFO hlp; + + hlp = (LPHELPINFO) lParam; + + if (hlp->iContextType != HELPINFO_WINDOW) + break; + + afs_html_help(hlp->hItemHandle, L"::/popups_newcred.txt", + HH_TP_HELP_WM_HELP, (DWORD_PTR) ctx_help); + } + return TRUE; + } /* switch(uMsg) */ + + return FALSE; +} + + +/* passed in to kcdb_credset_apply along with the afs_credset to adjust + newly acquired credentials to include informatino derived from the + new creds operation */ +khm_int32 KHMAPI +afs_adjust_token_ident_proc(khm_handle cred, void * vd) +{ + wchar_t cell[MAXCELLCHARS]; + afs_ident_token_set * b = (afs_ident_token_set *) vd; + afs_cred_list * l; + khm_size cbbuf; + int i; + + l = b->l; + + /* ASSUMPTION: for each user, there can be tokens for only one + cell */ + + cbbuf = sizeof(cell); + + if(KHM_FAILED(kcdb_cred_get_attr(cred, afs_attr_cell, NULL, cell, &cbbuf))) + return KHM_ERROR_SUCCESS; /* remember, kcdb doesn't care if + this run succeeded or not. all + it wants to know if whether or + not we want to continue the + search */ + + for(i=0; in_rows; i++) { + if((l->rows[i].flags & DLGROW_FLAG_DONE) && + !_wcsicmp(cell, l->rows[i].cell)) { + khm_int32 method; + + kcdb_cred_set_identity(cred, b->ident); + if(l->rows[i].realm) + kcdb_cred_set_attr(cred, afs_attr_realm, l->rows[i].realm, + KCDB_CBSIZE_AUTO); + else + kcdb_cred_set_attr(cred, afs_attr_realm, NULL, 0); + + method = l->rows[i].method; + kcdb_cred_set_attr(cred, afs_attr_method, &method, + KCDB_CBSIZE_AUTO); + + break; + } + } + + return KHM_ERROR_SUCCESS; +} + +void +afs_cred_write_ident_data(afs_dlg_data * d) { + wchar_t * lru_cell = NULL; + wchar_t * lru_realm = NULL; + wchar_t * id_cell = NULL; + khm_size cbidcell; + khm_size cbcell; + khm_size cbrealm; + khm_size cbt; + size_t cbz; + khm_handle h_idc = NULL; + khm_handle h_afs = NULL; + khm_handle h_acells = NULL; + khm_handle h_cellmap = NULL; + wchar_t idname[KCDB_IDENT_MAXCCH_NAME]; + khm_handle ident = NULL; + afs_cred_list * l; + int i; + + l = &d->creds; + + if (d->nc && + d->nc->n_identities > 0 && + d->nc->identities[0]) + + ident = d->nc->identities[0]; + + else if (d->config_dlg) + + ident = d->ident; + + if (!ident) + return; + + cbt = sizeof(idname); + kcdb_identity_get_name(ident, idname, &cbt); + + khc_open_space(csp_afscred, L"Cells", 0, &h_cellmap); + + if(ident) { + if(KHM_SUCCEEDED(kcdb_identity_get_config(ident, + KHM_FLAG_CREATE, + &h_idc))) { + khc_open_space(h_idc, CSNAME_AFSCRED, + KHM_FLAG_CREATE, &h_afs); + } + + if(h_afs) { + khc_open_space(h_afs, L"Cells", KHM_FLAG_CREATE, + &h_acells); + } + } + + if (h_afs && d) { + khc_write_int32(h_afs, L"AFSEnabled", + !!d->afs_enabled); + } + + if(khc_read_multi_string(csp_params, + L"LRUCells", + NULL, + &cbcell) == KHM_ERROR_TOO_LONG) { + cbcell += MAXCELLCHARS * sizeof(wchar_t) * + l->n_rows; + lru_cell = PMALLOC(cbcell); + ZeroMemory(lru_cell, cbcell); + cbt = cbcell; + + khc_read_multi_string(csp_params, + L"LRUCells", + lru_cell, + &cbt); + } else { + cbcell = MAXCELLCHARS * sizeof(wchar_t) * + l->n_rows; + if (cbcell > 0) { + lru_cell = PMALLOC(cbcell); + ZeroMemory(lru_cell, cbcell); + } else { + lru_cell = NULL; + } + } + + if(khc_read_multi_string(csp_params, + L"LRURealms", + NULL, + &cbrealm) == KHM_ERROR_TOO_LONG) { + cbrealm += MAXCELLCHARS * sizeof(wchar_t) * l->n_rows; + lru_realm = PMALLOC(cbrealm); + ZeroMemory(lru_realm, cbrealm); + cbt = cbrealm; + + khc_read_multi_string(csp_params, + L"LRURealms", + lru_realm, + &cbt); + } else { + cbrealm = MAXCELLCHARS * sizeof(wchar_t) * l->n_rows; + if (cbrealm > 0) { + lru_realm = PMALLOC(cbrealm); + ZeroMemory(lru_realm, cbrealm); + } else { + lru_cell = NULL; + } + } + + cbidcell = MAXCELLCHARS * sizeof(wchar_t) * l->n_rows; + if (cbidcell > 0) { + id_cell = PMALLOC(cbidcell); + ZeroMemory(id_cell, cbidcell); + } else { + id_cell = NULL; + } + + for(i=0; i < l->n_rows; i++) + if(!(l->rows[i].flags & DLGROW_FLAG_DELETED)) { + khm_handle h_acell = NULL; + + if(!multi_string_find(lru_cell, + l->rows[i].cell, 0)) { + cbz = cbcell; + multi_string_append(lru_cell, &cbz, + l->rows[i].cell); + } + + if(l->rows[i].realm && + !multi_string_find(lru_realm, + l->rows[i].realm, 0)) { + cbz = cbrealm; + multi_string_append(lru_realm, &cbz, + l->rows[i].realm); + } + + cbz = cbidcell; + multi_string_append(id_cell, &cbz, + l->rows[i].cell); + + if(h_acells && + KHM_SUCCEEDED(khc_open_space(h_acells, + l->rows[i].cell, + KHM_FLAG_CREATE, + &h_acell))) { + wchar_t methodname[KHUI_MAXCCH_NAME]; + + afs_get_method_name(l->rows[i].method, + methodname, + sizeof(methodname)); + + khc_write_string(h_acell, L"MethodName", + methodname); + + if(l->rows[i].realm) + khc_write_string(h_acell, L"Realm", + l->rows[i].realm); + else + khc_write_string(h_acell, L"Realm", L""); + khc_close_space(h_acell); + } + + if (h_cellmap) { + khc_write_string(h_cellmap, + l->rows[i].cell, + idname); + } + } + + if (lru_cell) + khc_write_multi_string(csp_params, + L"LRUCells", lru_cell); + if (lru_realm) + khc_write_multi_string(csp_params, + L"LRURealms", lru_realm); + if (id_cell) + khc_write_multi_string(h_afs, L"Cells", + id_cell); + + if (d->config_dlg) { + if (d->dirty) + khui_cfg_set_flags_inst(&d->cfg, KHUI_CNFLAG_APPLIED, + KHUI_CNFLAG_APPLIED | + KHUI_CNFLAG_MODIFIED); + else + khui_cfg_set_flags_inst(&d->cfg, 0, + KHUI_CNFLAG_MODIFIED); + } + + d->dirty = FALSE; + + if(h_cellmap) + khc_close_space(h_cellmap); + if(h_idc) + khc_close_space(h_idc); + if(h_afs) + khc_close_space(h_afs); + if(h_acells) + khc_close_space(h_acells); + if(id_cell) + PFREE(id_cell); + if(lru_cell) + PFREE(lru_cell); + if(lru_realm) + PFREE(lru_realm); +} + +khm_int32 +afs_msg_newcred(khm_int32 msg_subtype, + khm_ui_4 uparam, + void * vparam) { + + switch(msg_subtype) { + case KMSG_CRED_NEW_CREDS: + { + khui_new_creds * nc; + khui_new_creds_by_type * nct; + wchar_t wbuf[256]; + size_t cbsize; + + nc = (khui_new_creds *) vparam; + + nct = PMALLOC(sizeof(*nct)); + ZeroMemory(nct, sizeof(*nct)); + + nct->type = afs_credtype_id; + nct->ordinal = 3; + + LoadString(hResModule, IDS_AFS_NAME, wbuf, ARRAYLENGTH(wbuf)); + StringCbLength(wbuf, sizeof(wbuf), &cbsize); + cbsize += sizeof(wchar_t); + + nct->name = PMALLOC(cbsize); + StringCbCopy(nct->name, cbsize, wbuf); + + nct->h_module = hResModule; + nct->dlg_proc = afs_dlg_proc; + nct->dlg_template = MAKEINTRESOURCE(IDD_NC_AFS); + nct->type_deps[nct->n_type_deps++] = krb5_credtype_id; + + if (krb4_credtype_id < 0) { + kcdb_credtype_get_id(KRB4_CREDTYPE_NAME, + &krb4_credtype_id); + } + if (krb4_credtype_id >= 0) { + nct->type_deps[nct->n_type_deps++] = + krb4_credtype_id; + } + + khui_cw_add_type(nc, nct); + } + break; + + case KMSG_CRED_RENEW_CREDS: + { + khui_new_creds * nc; + khui_new_creds_by_type * nct; + + nc = (khui_new_creds *) vparam; + + nct = PMALLOC(sizeof(*nct)); + ZeroMemory(nct, sizeof(*nct)); + + nct->type = afs_credtype_id; + nct->type_deps[nct->n_type_deps++] = krb5_credtype_id; + if (krb4_credtype_id < 0) { + kcdb_credtype_get_id(KRB4_CREDTYPE_NAME, + &krb4_credtype_id); + } + if (krb4_credtype_id >= 0) { + nct->type_deps[nct->n_type_deps++] = + krb4_credtype_id; + } + + khui_cw_add_type(nc, nct); + } + break; + + case KMSG_CRED_DIALOG_PRESTART: + { + khui_new_creds * nc; + khui_new_creds_by_type * nct = NULL; + HWND hwnd; + + nc = (khui_new_creds *) vparam; + khui_cw_find_type(nc, afs_credtype_id, &nct); + + if(!nct) + break; + + hwnd = nct->hwnd_panel; + if (!hwnd) + break; + + PostMessage(hwnd, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0,WMNC_DIALOG_SETUP), 0); + } + break; + + case KMSG_CRED_DIALOG_NEW_IDENTITY: + { + khui_new_creds * nc; + khui_new_creds_by_type * nct = NULL; + afs_dlg_data * d; + + nc = (khui_new_creds *) vparam; + khui_cw_find_type(nc, afs_credtype_id, &nct); + + if(nct == NULL) + break; + + d = (afs_dlg_data *) nct->aux; + + if(d == NULL) + break; + + EnterCriticalSection(&d->cs); + + if (nct->aux == 0) { + LeaveCriticalSection(&d->cs); + break; + } + + /* we should load up the selected tokens for this + identity */ + if(nc->n_identities == 0) { + LeaveCriticalSection(&d->cs); + /* no identities selected. nothing to do */ + break; + } + + afs_cred_get_identity_creds(&d->creds, nc->identities[0], + &d->afs_enabled); + + LeaveCriticalSection(&d->cs); + + PostMessage(nct->hwnd_panel, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_AFS_UPDATE_ROWS), 0); + } + break; + + case KMSG_CRED_PROCESS: + { + khui_new_creds * nc; + khui_new_creds_by_type * nct = NULL; + afs_cred_list tlist; + afs_cred_list * l; + int i; + BOOL failed = FALSE; /* one or more cells failed */ + BOOL succeeded = FALSE; /* one or more cells succeeded */ + BOOL free_tlist = FALSE; + khm_handle ident = NULL; + afs_dlg_data * d = NULL; + BOOL get_tokens = TRUE; + BOOL ident_renew_triggered = TRUE; + khm_handle csp_afscred = NULL; + khm_handle csp_cells = NULL; + + nc = (khui_new_creds *) vparam; + khui_cw_find_type(nc, afs_credtype_id, &nct); + + if(!nct) + break; + + _begin_task(0); + _report_cs0(KHERR_INFO, + L"Getting AFS tokens..."); + _describe(); + + if(nc->result != KHUI_NC_RESULT_PROCESS && + nc->subtype != KMSG_CRED_RENEW_CREDS) { + /* nothing to do */ + khui_cw_set_response(nc, afs_credtype_id, + KHUI_NC_RESPONSE_SUCCESS); + + _report_cs0(KHERR_INFO, + L"Cancelling"); + _end_task(); + break; + } + + /* we can't proceed if Kerberos 5 has failed */ + if(!khui_cw_type_succeeded(nc, krb5_credtype_id)) { + khui_cw_set_response(nc, afs_credtype_id, + KHUI_NC_RESPONSE_FAILED); + + _report_cs0(KHERR_INFO, + L"Kerberos 5 plugin failed to process credentials request. Aborting"); + _end_task(); + break; + } + + if (nc->subtype == KMSG_CRED_RENEW_CREDS) { + + if (nc->ctx.scope == KHUI_SCOPE_IDENT || + + (nc->ctx.scope == KHUI_SCOPE_CREDTYPE && + nc->ctx.cred_type == afs_credtype_id) || + + (nc->ctx.scope == KHUI_SCOPE_CRED && + nc->ctx.cred_type == afs_credtype_id)) { + + _report_cs1(KHERR_INFO, + L"AFS Renew Creds :: ident %1!p!", + _cptr(nc->ctx.identity)); + + } else { + + _report_cs0(KHERR_INFO, + L"Renew request not applicable to AFS"); + _end_task(); + break; + + } + + if (nc->ctx.identity != NULL) { + ident = nc->ctx.identity; + } else { + khui_cw_set_response(nc, afs_credtype_id, + KHUI_NC_RESPONSE_FAILED); + + _report_cs0(KHERR_INFO, + L"No identity specified. Aborting"); + _end_task(); + break; + } + + ZeroMemory(&tlist, sizeof(tlist)); + l = &tlist; + free_tlist = TRUE; + + afs_cred_get_identity_creds(l, ident, NULL); + + /* if the identity has any tokens associated with it + that aren't persistent, we should renew those as + well. */ + afs_cred_get_context_creds(l, &nc->ctx); + + if (nc->ctx.scope == KHUI_SCOPE_CREDTYPE || + nc->ctx.scope == KHUI_SCOPE_CRED) { + + ident_renew_triggered = FALSE; + + } + + } else { + _report_cs1(KHERR_INFO, + L"AFS New Creds :: ident %1!p!", + _cptr(nc->identities[0])); + + d = (afs_dlg_data *) nct->aux; + if(!d) { + _report_cs0(KHERR_INFO, + L"No dialog data found. Aborting"); + + khui_cw_set_response(nc, afs_credtype_id, + KHUI_NC_RESPONSE_FAILED); + _end_task(); + break; + } + + EnterCriticalSection(&d->cs); + + l = &d->creds; + + ident = nc->identities[0]; + if (!ident) { + LeaveCriticalSection(&d->cs); + + _report_cs0(KHERR_INFO, + L"No identity specified. Aborting"); + + khui_cw_set_response(nc, afs_credtype_id, + KHUI_NC_RESPONSE_FAILED); + + _end_task(); + break; + } + + get_tokens = d->afs_enabled; + } + + if (!get_tokens) + goto _skip_tokens; + + if (KHM_SUCCEEDED(kmm_get_plugin_config(AFS_PLUGIN_NAME, 0, + &csp_afscred))) + khc_open_space(csp_afscred, L"Cells", 0, &csp_cells); + + /* looks like k5 worked. Now see about getting those + tokens */ + for(i=0; in_rows; i++) { + int code; + char cell[MAXCELLCHARS]; + char realm[MAXCELLCHARS]; + khm_handle ctoken; + FILETIME ft_old; + FILETIME ft_new; + time_t new_exp = 0; + khm_size cb; + khm_int32 method = AFS_TOKEN_AUTO; + khm_handle csp_cell = NULL; + + if (l->rows[i].flags & + (DLGROW_FLAG_DONE | DLGROW_FLAG_DELETED)) + + continue; + + ZeroMemory(cell, sizeof(cell)); + ZeroMemory(realm, sizeof(realm)); + + UnicodeStrToAnsi(cell, sizeof(cell), l->rows[i].cell); + if (l->rows[i].realm != NULL) + UnicodeStrToAnsi(realm, sizeof(realm), + l->rows[i].realm); + + ZeroMemory(&ft_old, sizeof(ft_old)); + + if (!ident_renew_triggered && + (ctoken = afs_find_token(NULL, l->rows[i].cell))) { + + cb = sizeof(ft_old); + kcdb_cred_get_attr(ctoken, KCDB_ATTR_EXPIRE, + NULL, &ft_old, &cb); + + kcdb_cred_release(ctoken); + } + + if (l->rows[i].method == AFS_TOKEN_AUTO && csp_cells && + KHM_SUCCEEDED(khc_open_space(csp_cells, + l->rows[i].cell, 0, + &csp_cell))) { + + if (KHM_FAILED(khc_read_int32(csp_cell, L"Method", &method))) { + method = l->rows[i].method; + } else { + _report_cs3(KHERR_INFO, + L"Overriding method %1!d! with global default %2!d! for cell %3!s!", + _int32(l->rows[i].method), + _int32(method), + _cstr(l->rows[i].cell)); + _resolve(); + } + + khc_close_space(csp_cell); + } else { + method = l->rows[i].method; + } + + _report_cs3(KHERR_INFO, + L"Getting tokens for cell %1!S! with realm %2!S! using method %3!d!", + _cstr(cell), + _cstr(realm), + _int32(method)); + _resolve(); + + /* make the call */ + code = afs_klog(ident, "", cell, realm, 0, + method, &new_exp); + + _report_cs1(KHERR_INFO, + L"klog returns code %1!d!", + _int32(code)); + + if(code) { + failed = TRUE; + l->rows[i].flags &= ~DLGROW_FLAG_DONE; + + if (!kherr_is_error()) { + /* failed to get tokens, but no error was reported */ + _report_sr1(KHERR_ERROR, IDS_ERR_GENERAL, + _cptr(cell)); + _resolve(); + } + + } else { + l->rows[i].flags |= DLGROW_FLAG_DONE; + succeeded = TRUE; + + if (new_exp && + !ident_renew_triggered) { + TimetToFileTime(new_exp, &ft_new); + + if (CompareFileTime(&ft_old, &ft_new) >= 0) { + /* getting a new token didn't improve the + situation much. We only get here if we + were trying to renew tokens. So we try + to trigger an identity renewal. Doing + so should get us new initial tickets + which will allow us to get a better + token. */ + + khui_action_context ctx; + + _reportf(L"Renewal of AFS tokens for cell %s failed to get a longer token. Triggering identity renewal", l->rows[i].cell); + + khui_context_create(&ctx, + KHUI_SCOPE_IDENT, + nc->ctx.identity, + KCDB_CREDTYPE_INVALID, + NULL); + khui_action_trigger(KHUI_ACTION_RENEW_CRED, + &ctx); + + khui_context_release(&ctx); + + ident_renew_triggered = TRUE; + } + } + } + } + + _skip_tokens: + + if(failed) { + /* we should indicate errors if anything went wrong */ + khui_cw_set_response(nc, afs_credtype_id, + KHUI_NC_RESPONSE_FAILED); + } else { + khui_cw_set_response(nc, afs_credtype_id, + KHUI_NC_RESPONSE_SUCCESS); + } + + if (succeeded && nc->subtype == KMSG_CRED_RENEW_CREDS) { + afs_ident_token_set b; + + afs_list_tokens_internal(); + + /* the tokens that we just acquired need adjusting to + include the realm, method and identity information + derived from the new creds operation. this is done + in afs_adjust_token_ident_proc */ + b.ident = ident; + b.l = l; + b.add_new = FALSE; + b.update_info = FALSE; + + kcdb_credset_apply(afs_credset, afs_adjust_token_ident_proc, + (void *) &b); + + kcdb_credset_collect(NULL, afs_credset, NULL, + afs_credtype_id, NULL); + + } else if (nc->subtype == KMSG_CRED_NEW_CREDS) { + afs_ident_token_set b; + + afs_list_tokens_internal(); + + /* the tokens that we just acquired need adjusting to + include the realm, method and identity information + derived from the new creds operation. this is done + in afs_adjust_token_ident_proc */ + b.ident = ident; + b.l = l; + b.add_new = FALSE; + b.update_info = FALSE; + + kcdb_credset_apply(afs_credset, afs_adjust_token_ident_proc, + (void *) &b); + + kcdb_credset_collect(NULL, afs_credset, NULL, + afs_credtype_id, NULL); + + afs_cred_write_ident_data(d); + } + + if (d) + LeaveCriticalSection(&d->cs); + + if (free_tlist) { + afs_cred_free_rows(&tlist); + } + + if (csp_afscred) + khc_close_space(csp_afscred); + + if (csp_cells) + khc_close_space(csp_cells); + + _end_task(); + } + break; + + case KMSG_CRED_END: + { + khui_new_creds * nc; + khui_new_creds_by_type * nct; + + nc = (khui_new_creds *) vparam; + khui_cw_find_type(nc, afs_credtype_id, &nct); + + if(!nct) + break; + + khui_cw_del_type(nc, afs_credtype_id); + + if (nct->name) + PFREE(nct->name); + if (nct->credtext) + PFREE(nct->credtext); + + PFREE(nct); + } + break; + } + + return KHM_ERROR_SUCCESS; +} diff --git a/src/WINNT/netidmgr_plugin/afsnewcreds.h b/src/WINNT/netidmgr_plugin/afsnewcreds.h new file mode 100644 index 0000000..cc760cd --- /dev/null +++ b/src/WINNT/netidmgr_plugin/afsnewcreds.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __AFS_NEWCREDS_H +#define __AFS_NEWCREDS_H + +typedef struct tag_afs_cred_row { + wchar_t * cell; + wchar_t * realm; + afs_tk_method method; + khm_int32 flags; +} afs_cred_row; + +/* we checked whether this cell exists */ +#define DLGROW_FLAG_CHECKED 0x00000001 + +/* cell was checked and was found to be valid */ +#define DLGROW_FLAG_VALID 0x00000002 + +/* cell was deleted */ +#define DLGROW_FLAG_DELETED 0x00000004 + +/* tokens obtained for cell */ +#define DLGROW_FLAG_DONE 0x00000008 + +/* tokens for this cell already exist */ +#define DLGROW_FLAG_EXISTS 0x00000010 + +/* tokens for this cell exist and is listed under a different + identity */ +#define DLGROW_FLAG_NOTOWNED 0x00000020 + +/* tokens for this cell exist and are expired */ +#define DLGROW_FLAG_EXPIRED 0x00000040 + +/* the subitem indexes for each data field */ +enum afs_ncwnd_subitems { + NCAFS_IDX_CELL=0, + NCAFS_IDX_REALM, + NCAFS_IDX_METHOD +}; + +#define DLG_TOOLTIP_TIMER_ID 1 +#define DLG_TOOLTIP_TIMEOUT 5000 + +typedef struct tag_afs_cred_list { + afs_cred_row * rows; + int n_rows; + int nc_rows; +} afs_cred_list; + +typedef struct tag_afs_dlg_data { + khui_new_creds * nc; + + afs_cred_list creds; + + khm_int32 afs_enabled; + + BOOL tooltip_visible; + BOOL dirty; + HWND tooltip; + + /* list view state image indices */ + int idx_new_token; + int idx_existing_token; + int idx_bad_token; + + CRITICAL_SECTION cs; + + /* used with configuration dialogs */ + khm_boolean config_dlg; + khui_config_init_data cfg; + khm_handle ident; +} afs_dlg_data; + +#define AFS_DLG_ROW_ALLOC 4 + +INT_PTR CALLBACK +afs_dlg_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam); + +void +afs_dlg_update_rows(HWND hwnd, afs_dlg_data * d); + +void +afs_cred_flush_rows(afs_cred_list * l); + +void +afs_cred_free_rows(afs_cred_list * l); + +void +afs_cred_assert_rows(afs_cred_list * l, int n); + +void +afs_cred_delete_row(afs_cred_list * l, int i); + +afs_cred_row * +afs_cred_get_new_row(afs_cred_list * l); + +khm_int32 KHMAPI +afs_cred_add_cred_proc(khm_handle cred, void * rock); + +void +afs_cred_get_context_creds(afs_cred_list *l, + khui_action_context * ctx); + +void +afs_cred_get_identity_creds(afs_cred_list * l, + khm_handle ident, + khm_boolean * enabled); + +void +afs_cred_write_ident_data(afs_dlg_data * d); + +khm_int32 +afs_msg_newcred(khm_int32 msg_subtype, + khm_ui_4 uparam, + void * vparam); + +#endif diff --git a/src/WINNT/netidmgr_plugin/afsp_version.h b/src/WINNT/netidmgr_plugin/afsp_version.h new file mode 100644 index 0000000..11974bf --- /dev/null +++ b/src/WINNT/netidmgr_plugin/afsp_version.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef __AFSPLUGIN_VERSION_H +#define __AFSPLUGIN_VERSION_H + +#define AFSPLUGIN_VERSION_MAJOR 1 +#define AFSPLUGIN_VERSION_MINOR 5 +#define AFSPLUGIN_VERSION_PATCH 0002 +#define AFSPLUGIN_VERSION_AUX 0 + +#define AFSPLUGIN_VERSION 1.5.0002.0 +#define AFSPLUGIN_VERSION_STR "1.5.0002.0" +#define AFSPLUGIN_VERSION_LST 1,5,0002,0 + +#endif diff --git a/src/WINNT/netidmgr_plugin/afsp_version.h.in b/src/WINNT/netidmgr_plugin/afsp_version.h.in new file mode 100644 index 0000000..a15043e --- /dev/null +++ b/src/WINNT/netidmgr_plugin/afsp_version.h.in @@ -0,0 +1,40 @@ +afsp_version.h: NTMakefile afsp_version.h.in + $(COPY) << $@ +/* Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef __AFSPLUGIN_VERSION_H +#define __AFSPLUGIN_VERSION_H + +#define AFSPLUGIN_VERSION_MAJOR $(AFSPLUGIN_VERSION_MAJOR) +#define AFSPLUGIN_VERSION_MINOR $(AFSPLUGIN_VERSION_MINOR) +#define AFSPLUGIN_VERSION_PATCH $(AFSPLUGIN_VERSION_PATCH) +#define AFSPLUGIN_VERSION_AUX $(AFSPLUGIN_VERSION_AUX) + +#define AFSPLUGIN_VERSION $(AFSPLUGIN_VERSION) +#define AFSPLUGIN_VERSION_STR "$(AFSPLUGIN_VERSION)" +#define AFSPLUGIN_VERSION_LST $(AFSPLUGIN_VERLIST) + +#endif +<< diff --git a/src/WINNT/netidmgr_plugin/afspext.h b/src/WINNT/netidmgr_plugin/afspext.h new file mode 100644 index 0000000..cfe802b --- /dev/null +++ b/src/WINNT/netidmgr_plugin/afspext.h @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __AFSPLUGIN_EXT_H +#define __AFSPLUGIN_EXT_H + +/*! \defgroup afs_ext OpenAFS Plugin extensions + + This section documents messages and data structures used by AFS + extension plugins. These are plugins which augment the behavior + of the AFS plugin. + + When performing specific tasks for NetIDMgr, the AFS plugin will + send out messages to the extension plugins either via broadcast or + unicast. The extension plugins provide functionality by + responding to these messages. + + @{*/ + +#define MAXCELLCHARS 64 +#define MAXHOSTCHARS 64 +#define MAXHOSTSPERCELL 8 + +#define TRANSARCAFSDAEMON "TransarcAFSDaemon" + +#define AFS_TOKENNAME_AUTO L"Auto" +#define AFS_TOKENNAME_KRB5 L"Kerberos5" +#define AFS_TOKENNAME_KRB524 L"Kerberos524" +#define AFS_TOKENNAME_KRB4 L"Kerberos4" + +/*! \brief An AFS token acquisition method identifier + + This takes on a value from ::afs_token_method or a token + acquisition method identifier assigned to an extension plugin. +*/ +typedef khm_int32 afs_tk_method; + +/*! \brief Predefined token acquisition methods */ +enum afs_token_method { + AFS_TOKEN_AUTO = 0, /*!< Automatic. This method iterates + through Krb5, Krb524, Krb4 and then + any extensions which provide token + acquisition methods until one of + them succeeds. */ + AFS_TOKEN_KRB5, /*!< Kerberos 5 */ + AFS_TOKEN_KRB524, /*!< Kerberos 5 with krb524 translation */ + AFS_TOKEN_KRB4, /*!< Kerberos 4 */ +}; + +/*! \brief Version of the OpenAFS Plugin + + This is an internal number that identifies the version of the + OpenAFS plugin API that this extension was built against. This + number is specified when sending the ::AFS_MSG_ANNOUNCE message. + */ +#define AFS_PLUGIN_VERSION 0x0000001 + +/*! \name Messages + + The AFS plugin registers the message type named ::AFS_MSG_TYPENAME + and sends messages of this type to notify any AFS extension + plugins to notify them of various events. + + @{*/ + +/*! \brief Name of the AFS plugin message + + This message type is registered when the AFS plugin starts and is + unregistered when the plugin stops. + + Use kmq_find_type() to find the type ID of this message type. + */ +#define AFS_MSG_TYPENAME L"AfsExtMessage" + +/*! \brief Announce an extension plugin + + Sent by an extension plugin to announce its existence to the AFS + plugin. This message should be sent by the extension plugin when + it has finished loading, and is the only message permitted to be + sent by an extension. All other messages are sent by the AFS + plugin. + + Since this message contains pointer parameters and there is no + cleanup performed on this, the message should be sent using + kmq_send_message(). + + + + + + +
Typetype ID of ::AFS_MSG_TYPENAME
Subtype::AFS_MSG_ANNOUNCE
uparam0
vparamPointer to a ::afs_msg_announce structure
+ + \note This message is only sent from extension plugins to the AFS plugin. + */ +#define AFS_MSG_ANNOUNCE 1 + +/*! \brief Parameter structure for announcing an extension plugin + + \see ::AFS_MSG_ANNOUNCE + */ +typedef struct tag_afs_msg_announce_v1 { + khm_size cbsize; /*!< Size of the strucutre. Set to \a + sizeof(::afs_msg_announce). If + there is a version skew between the + AFS plugin and the extension, then + this parameter will ensure that the + AFS plugin understands the correct + version of the structure. */ + + khm_ui_4 version; /*!< Version of the AFS plugin that + the extension is compiled for. Set + this to ::AFS_PLUGIN_VERSION. + Depending on this value, the AFS + plugin will either reject the + extension or determine which set of + messages and structures should be + used to communicate with the + extension. */ + + const wchar_t * name; /*!< Name of the extension. Should be + unique among all AFS extension + plugins. Size constrained by + ::KHUI_MAXCCH_NAME*/ + + khm_handle sub; /*!< A valid subscription for unicast + messages. This must have been + created through + kmq_create_subscription(). The + supplied handle will be + automatically released when the + plugin exits. However, if the + announcement message fails, then the + extension has to release the handle + itself. */ + + khm_boolean provide_token_acq; /*!< non-zero if the extension + provides a token acquisition + method. The \a token_acq + substructure should be filled if + this member is set to + non-zero. */ + + struct { + const wchar_t * short_desc; /*!< Short description of token + acquisition method. (localized, + required). Size is constrained by + ::KHUI_MAXCCH_SHORT_DESC */ + + const wchar_t * long_desc; /*!< Long description. (localized, + optional). Size is constrained by + ::KHUI_MAXCCH_LONG_DESC */ + + afs_tk_method method_id; /*!< Once the message is processed, + this will receive a new method + identifier. The value of this field + on entry is ignored. */ + + } token_acq; /*!< Registration information for + token acquisition method. Only + assumed to be valid if \a + provide_token_acq is TRUE. */ + +} afs_msg_announce; + +/*! \brief Sent to all extensions to resolve the identity of a token + + If the identity and credentials acquisition method of an AFS token + cannot be determined by the AFS plugin, this message is sent out + to extension plugins to allow them a chance to resolve it. + + If the extension plugin successfully resolves the identity and + token acquisition method of the specified token, it should return + ::KHM_ERROR_SUCCESS. Otherwise it should return a failure code. + The actual return code is not interpreted other than whether or + not it passes the ::KHM_SUCCEEDED() test. + + + + + + +
Typetype ID of ::AFS_MSG_TYPENAME
Subtype::AFS_MSG_RESOLVE_TOKEN
uparam0
vparamPointer to a ::afs_msg_resolve_token structure
+ + \note This message is only sent from the AFS plugin to extension plugins + + \note Only sent if the extension plugin has ::provide_token_acq set. + */ +#define AFS_MSG_RESOLVE_TOKEN 2 + +/*! \brief Message structure for AFS_MSG_RESOLVE_TOKEN + + Other than the fields marked as \a [OUT], all other fields should + be considered read-only and should not be modified. + + \see ::AFS_MSG_RESOLVE_TOKEN + */ +typedef struct tag_afs_msg_resolve_token_v1 { + khm_size cbsize; /*!< Size of the structure. This will + be set to \a + sizeof(::afs_msg_resolve_token). */ + + const wchar_t * cell; /*!< Specifies the cell that the token + belongs to. */ + + const struct ktc_token * token; /*!< The token */ + const struct ktc_principal * serverp; /*!< Server principal */ + const struct ktc_principal * clientp; /*!< Client principal */ + + khm_handle ident; /*!< [OUT] If the extension + successfully resolves the identity, + then it should assign a handle to + the identity to this field and + return ::KHM_ERROR_SUCCESS. The + handle will be automatically freed + by the AFS plugin. */ + + afs_tk_method method; /*!< [OUT] If the extension + successfully resolves the identity, + it should also assign the token + acquisition method identifier to + this field. The default method is + ::AFS_TOKEN_AUTO. This field + indicates the token acquisition + method that was used to obtain the + token and is used when the token + needs to be renewed. */ +} afs_msg_resolve_token; + +/*! \brief Sent to an extension plugin to obtain AFS tokens + + + + + + +
Typetype ID of ::AFS_MSG_TYPENAME
Subtype::AFS_MSG_KLOG
uparam0
vparamPointer to a ::afs_msg_klog
+ + \note Only sent from the AFS plugin to extension plugins + \note Only sent to extension plugins which have ::provide_token_acq set. +*/ +#define AFS_MSG_KLOG 3 + +/*! \brief Cell configuration information + + \see ::afs_msg_klog + + \note This structure uses ANSI char fields instead of unicode fields. + */ +typedef struct tag_afs_conf_cellA_v1 { + khm_size cbsize; /*!< set to \a sizeof(afs_conf_cell) */ + + char name[MAXCELLCHARS]; /*!< Name of the cell */ + short numServers; /*!< Number of servers for cell. + Upper bound of MAXHOSTSPERCELL */ + short flags; /*!< Not used. Set to zero. */ + struct sockaddr_in hostAddr[MAXHOSTSPERCELL]; + /*!< addresses for each server. There + are \a numServers entries.*/ + char hostName[MAXHOSTSPERCELL][MAXHOSTCHARS]; + /*!< names of the servers. There are + \a numServers entries. */ + char * linkedCell; /*!< Not used. Set to zero. */ +} afs_conf_cell; + +/*! \brief Message parameters for AFS_MSG_KLOG message + + \see ::AFS_MSG_KLOG + + \note This structure uses ANSI char fields instead of unicode fields. + */ +typedef struct tag_afs_msg_klogA_v1 { + khm_size cbsize; /*!< Set to \a sizeof(afs_msg_klog) */ + + khm_handle identity; /*!< Handle to identity for which we + are obtaining tokens. */ + + const char * service; /*!< Service name to use when + obtaining token. This can be NULL + if the service name has not be + determined. */ + + const char * cell; /*!< Name of cell to obtain tokens + for. Can be NULL if the local cell + is to be used. */ + + const char * realm; /*!< Realm to use when obtaining + tokens. Can be NULL if the realm + has not been determined. */ + + const afs_conf_cell * cell_config; /*!< Cell configuration for the + cell specified in \a cell. */ + + khm_int32 lifetime; /*!< Advisory lifetime specifier, in + seconds. If set to zero, means + there is no specification for + lifetime. Extensions should feel + free to ignore this parameter. */ +} afs_msg_klog; + +/*!@}*/ + +/*!@}*/ + +#endif diff --git a/src/WINNT/netidmgr_plugin/afsplugin.c b/src/WINNT/netidmgr_plugin/afsplugin.c new file mode 100644 index 0000000..3dd47e1 --- /dev/null +++ b/src/WINNT/netidmgr_plugin/afsplugin.c @@ -0,0 +1,642 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include +#include +#include +#include +#include +#include + +static BOOL initialized = FALSE; +khm_int32 afs_credtype_id = -1; +khm_int32 krb5_credtype_id = -1; +khm_int32 krb4_credtype_id = -1; +khm_int32 afs_msg_type_id = -1; +khm_int32 afs_type_principal = -1; +khm_int32 afs_type_method = -1; +khm_int32 afs_attr_client_princ = -1; +khm_int32 afs_attr_server_princ = -1; +khm_int32 afs_attr_cell = -1; +khm_int32 afs_attr_method = -1; +khm_int32 afs_attr_realm = -1; +khm_handle afs_credset = NULL; +khm_handle afs_sub = NULL; /* AFS message subscription */ + +/* forward dcls */ +khm_int32 KHMAPI +afs_msg_system(khm_int32 msg_subtype, khm_ui_4 uparam, void * vparam); + +khm_int32 KHMAPI +afs_msg_kcdb(khm_int32 msg_subtype, khm_ui_4 uparam, void * vparam); + +khm_int32 KHMAPI +afs_msg_cred(khm_int32 msg_subtype, khm_ui_4 uparam, void * vparam); + +khm_int32 KHMAPI +afs_msg_ext(khm_int32 msg_subtype, khm_ui_4 uparam, void * vparam); + +/* AFS plugin callback */ +khm_int32 KHMAPI +afs_plugin_cb(khm_int32 msg_type, + khm_int32 msg_subtype, + khm_ui_4 uparam, + void * vparam) +{ + if (msg_type == KMSG_SYSTEM) + return afs_msg_system(msg_subtype, uparam, vparam); + if (msg_type == KMSG_KCDB) + return afs_msg_kcdb(msg_subtype, uparam, vparam); + if (msg_type == KMSG_CRED) + return afs_msg_cred(msg_subtype, uparam, vparam); + if (msg_type == afs_msg_type_id) + return afs_msg_ext(msg_subtype, uparam, vparam); + + return KHM_ERROR_SUCCESS; +} + +/* ktc_principal attribute type */ +/* String */ + +khm_int32 KHMAPI +afs_type_principal_toString(const void * d, + khm_size cbd, + wchar_t * buffer, + khm_size * cb_buf, + khm_int32 flags) +{ + size_t cbsize; + struct ktc_principal * p; + wchar_t sprinc[512] = L""; + + if(!cb_buf) + return KHM_ERROR_INVALID_PARAM; + + p = (struct ktc_principal *) d; + + // assume this works. + afs_princ_to_string(p, sprinc, sizeof(sprinc)); + StringCbLength(sprinc, sizeof(sprinc), &cbsize); + cbsize += sizeof(wchar_t); + + if(!buffer || *cb_buf < cbsize) { + *cb_buf = cbsize; + return KHM_ERROR_TOO_LONG; + } + + StringCbCopy(buffer, *cb_buf, sprinc); + + *cb_buf = cbsize; + + return KHM_ERROR_SUCCESS; +} + +khm_boolean KHMAPI +afs_type_principal_isValid(const void * d, + khm_size cbd) +{ + /*TODO: check for more inconsistencies */ + if(cbd != sizeof(struct ktc_principal)) + return FALSE; + return TRUE; +} + +khm_int32 KHMAPI +afs_type_principal_comp(const void * d1, + khm_size cbd1, + const void * d2, + khm_size cbd2) +{ + struct ktc_principal * p1 = (struct ktc_principal *) d1; + struct ktc_principal * p2 = (struct ktc_principal *) d2; + int r; + + r = strcmp(p1->name, p2->name); + if(r != 0) + return r; + r = strcmp(p1->instance, p2->instance); + if(r != 0) + return r; + r = strcmp(p1->cell, p2->cell); + return r; +} + +khm_int32 KHMAPI +afs_type_principal_dup(const void * d_src, + khm_size cbd_src, + void * d_dst, + khm_size * cbd_dst) +{ + if(!d_dst || *cbd_dst < sizeof(struct ktc_principal)) { + *cbd_dst = sizeof(struct ktc_principal); + return KHM_ERROR_TOO_LONG; + } + + memcpy(d_dst, d_src, sizeof(struct ktc_principal)); + *cbd_dst = sizeof(struct ktc_principal); + + return KHM_ERROR_SUCCESS; +} + +khm_int32 KHMAPI +afs_type_method_toString(const void * data, + khm_size cb_data, + wchar_t * s_buf, + khm_size * pcb_s_buf, + khm_int32 flags) { + khm_int32 * pmethod = (khm_int32 *) data; + wchar_t wbuf[KHUI_MAXCCH_LONG_DESC]; + khm_size cb; + + if (!data || cb_data != sizeof(khm_int32)) + return KHM_ERROR_INVALID_PARAM; + + wbuf[0] = L'\0'; + if (!afs_method_describe(*pmethod, flags, wbuf, sizeof(wbuf))) { + LoadString(hResModule, + IDS_NC_METHOD_INVALID, + wbuf, + ARRAYLENGTH(wbuf)); + } + + StringCbLength(wbuf, sizeof(wbuf), &cb); + cb += sizeof(wchar_t); + + if (!s_buf || *pcb_s_buf < cb) { + *pcb_s_buf = cb; + return KHM_ERROR_TOO_LONG; + } else { + StringCbCopy(s_buf, *pcb_s_buf, wbuf); + *pcb_s_buf = cb; + return KHM_ERROR_SUCCESS; + } +} + +/* process KMSG_SYSTEM messages */ +khm_int32 KHMAPI +afs_msg_system(khm_int32 msg_subtype, + khm_ui_4 uparam, + void * vparam) +{ + khm_int32 rv = KHM_ERROR_UNKNOWN; + + switch(msg_subtype) { + case KMSG_SYSTEM_INIT: + /* Perform critical registrations and data structure + initalization */ + { + kcdb_credtype ct; + wchar_t buf[KCDB_MAXCCH_LONG_DESC]; + size_t cbsize; + kcdb_attrib att; + khm_handle csp_afscred = NULL; + khm_int32 disable_afscreds = FALSE; + + ZeroMemory(&ct, sizeof(ct)); + /* first of all, register the AFS token credential type */ + ct.id = KCDB_CREDTYPE_AUTO; + ct.name = AFS_CREDTYPE_NAME; + + if(LoadString(hResModule, + IDS_AFS_SHORT_DESC, + buf, + ARRAYLENGTH(buf)) != 0) { + StringCbLength(buf, sizeof(buf), &cbsize); + cbsize += sizeof(wchar_t); + ct.short_desc = PMALLOC(cbsize); + StringCbCopy(ct.short_desc, cbsize, buf); + } else + ct.short_desc = NULL; + + if(LoadString(hResModule, + IDS_AFS_LONG_DESC, + buf, + ARRAYLENGTH(buf)) != 0) { + StringCbLength(buf, sizeof(buf), &cbsize); + cbsize += sizeof(wchar_t); + ct.long_desc = PMALLOC(cbsize); + StringCbCopy(ct.long_desc, cbsize, buf); + } else + ct.long_desc = NULL; + + ct.icon = LoadImage(hResModule, + MAKEINTRESOURCE(IDI_AFSTOKEN), + IMAGE_ICON, + 0, 0, LR_DEFAULTSIZE); + + kmq_create_subscription(afs_plugin_cb, &afs_sub); + ct.sub = afs_sub; + + kcdb_credtype_register(&ct, &afs_credtype_id); + + /* register the attribute types */ + { + kcdb_type type; + + ZeroMemory(&type, sizeof(type)); + type.comp = afs_type_principal_comp; + type.dup = afs_type_principal_dup; + type.isValid = afs_type_principal_isValid; + type.toString = afs_type_principal_toString; + type.name = AFS_TYPENAME_PRINCIPAL; + type.id = KCDB_TYPE_INVALID; + type.cb_max = sizeof(struct ktc_principal); + type.cb_min = sizeof(struct ktc_principal); + type.flags = KCDB_TYPE_FLAG_CB_FIXED; + + if(KHM_FAILED(kcdb_type_register(&type, + &afs_type_principal))) + goto _exit_init; + } + + { + kcdb_type type; + kcdb_type *ti32 = NULL; + + kcdb_type_get_info(KCDB_TYPE_INT32, &ti32); + + ZeroMemory(&type, sizeof(type)); + type.comp = ti32->comp; + type.dup = ti32->dup; + type.isValid = ti32->isValid; + type.toString = afs_type_method_toString; + type.name = AFS_TYPENAME_METHOD; + type.id = KCDB_TYPE_INVALID; + type.cb_max = sizeof(khm_int32); + type.cb_min = sizeof(khm_int32); + type.flags = KCDB_TYPE_FLAG_CB_FIXED; + + if(KHM_FAILED(kcdb_type_register(&type, + &afs_type_method))) { + kcdb_type_release_info(ti32); + goto _exit_init; + } + + kcdb_type_release_info(ti32); + } + + /* now register the attributes */ + { + wchar_t short_desc[KCDB_MAXCCH_SHORT_DESC]; + + ZeroMemory(&att, sizeof(att)); + + att.type = KCDB_TYPE_STRING; + att.name = AFS_ATTRNAME_CELL; + LoadString(hResModule, + IDS_ATTR_CELL_SHORT_DESC, + short_desc, + ARRAYLENGTH(short_desc)); + att.short_desc = short_desc; + att.long_desc = NULL; + att.id = KCDB_ATTR_INVALID; + att.flags = KCDB_ATTR_FLAG_TRANSIENT; + + if(KHM_FAILED(rv = kcdb_attrib_register(&att, + &afs_attr_cell))) + goto _exit_init; + } + + { + wchar_t short_desc[KCDB_MAXCCH_SHORT_DESC]; + + ZeroMemory(&att, sizeof(att)); + + att.type = KCDB_TYPE_STRING; + att.name = AFS_ATTRNAME_REALM; + LoadString(hResModule, + IDS_ATTR_REALM_SHORT_DESC, + short_desc, + ARRAYLENGTH(short_desc)); + att.short_desc = short_desc; + att.long_desc = NULL; + att.id = KCDB_ATTR_INVALID; + att.flags = KCDB_ATTR_FLAG_TRANSIENT; + + if(KHM_FAILED(rv = kcdb_attrib_register(&att, + &afs_attr_realm))) + goto _exit_init; + } + + { + wchar_t short_desc[KCDB_MAXCCH_SHORT_DESC]; + + ZeroMemory(&att, sizeof(att)); + + att.type = afs_type_method; + att.name = AFS_ATTRNAME_METHOD; + LoadString(hResModule, + IDS_ATTR_METHOD_SHORT_DESC, + short_desc, + ARRAYLENGTH(short_desc)); + att.short_desc = short_desc; + att.long_desc = NULL; + att.id = KCDB_ATTR_INVALID; + att.flags = KCDB_ATTR_FLAG_TRANSIENT; + + if(KHM_FAILED(rv = kcdb_attrib_register(&att, + &afs_attr_method))) + goto _exit_init; + } + + { + wchar_t short_desc[KCDB_MAXCCH_SHORT_DESC]; + + ZeroMemory(&att, sizeof(att)); + + att.type = afs_type_principal; + att.name = AFS_ATTRNAME_CLIENT_PRINC; + LoadString(hResModule, + IDS_ATTR_CLIENT_PRINC_SHORT_DESC, + short_desc, + ARRAYLENGTH(short_desc)); + att.short_desc = short_desc; + att.long_desc = NULL; + att.id = KCDB_ATTR_INVALID; + att.flags = KCDB_ATTR_FLAG_TRANSIENT; + + if(KHM_FAILED(rv = kcdb_attrib_register(&att, &afs_attr_client_princ))) + goto _exit_init; + } + + { + wchar_t short_desc[KCDB_MAXCCH_SHORT_DESC]; + + ZeroMemory(&att, sizeof(att)); + + att.type = afs_type_principal; + att.name = AFS_ATTRNAME_SERVER_PRINC; + LoadString(hResModule, + IDS_ATTR_SERVER_PRINC_SHORT_DESC, + short_desc, ARRAYLENGTH(short_desc)); + att.short_desc = short_desc; + att.long_desc = NULL; + att.id = KCDB_ATTR_INVALID; + att.flags = KCDB_ATTR_FLAG_TRANSIENT; + + if(KHM_FAILED(rv = kcdb_attrib_register(&att, &afs_attr_server_princ))) + goto _exit_init; + } + + /* afs_credset is our stock credentials set that we + use for all our credset needs (instead of creating + a new one every time) */ + + if(KHM_FAILED(rv = kcdb_credset_create(&afs_credset))) + goto _exit_init; + + if(KHM_FAILED(rv = kcdb_credtype_get_id(KRB5_CREDTYPE_NAME, + &krb5_credtype_id))) + goto _exit_init; + + /* register the configuration nodes */ + { + khui_config_node node_ident; + khui_config_node_reg reg; + wchar_t wshort_desc[KHUI_MAXCCH_SHORT_DESC]; + wchar_t wlong_desc[KHUI_MAXCCH_LONG_DESC]; + + if (KHM_FAILED(rv = khui_cfg_open(NULL, + L"KhmIdentities", + &node_ident))) + goto _exit_init; + + ZeroMemory(®, sizeof(reg)); + reg.name = AFS_CONFIG_NODE_MAIN; + reg.short_desc = wshort_desc; + reg.long_desc = wlong_desc; + reg.h_module = hResModule; + reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_AFS); + reg.dlg_proc = afs_cfg_main_proc; + reg.flags = 0; + LoadString(hResModule, IDS_CFG_MAIN_LONG, + wlong_desc, ARRAYLENGTH(wlong_desc)); + LoadString(hResModule, IDS_CFG_MAIN_SHORT, + wshort_desc, ARRAYLENGTH(wshort_desc)); + + khui_cfg_register(NULL, ®); + + ZeroMemory(®, sizeof(reg)); + reg.name = AFS_CONFIG_NODE_IDS; + reg.short_desc = wshort_desc; + reg.long_desc = wshort_desc; + reg.h_module = hResModule; + reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_IDS_TAB); + reg.dlg_proc = afs_cfg_ids_proc; + reg.flags = KHUI_CNFLAG_SUBPANEL; + LoadString(hResModule, IDS_CFG_IDS_TAB, + wshort_desc, ARRAYLENGTH(wshort_desc)); + + khui_cfg_register(node_ident, ®); + + ZeroMemory(®, sizeof(reg)); + reg.name = AFS_CONFIG_NODE_ID; + reg.short_desc = wshort_desc; + reg.long_desc = wshort_desc; + reg.h_module = hResModule; + reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_ID_TAB); + reg.dlg_proc = afs_cfg_id_proc; + reg.flags = KHUI_CNFLAG_SUBPANEL | KHUI_CNFLAG_PLURAL; + LoadString(hResModule, IDS_CFG_ID_TAB, + wshort_desc, ARRAYLENGTH(wshort_desc)); + + khui_cfg_register(node_ident, ®); + } + + /* and register the AFS message type */ + rv = kmq_register_type(AFS_MSG_TYPENAME, &afs_msg_type_id); + + if (KHM_SUCCEEDED(rv)) + kmq_subscribe(afs_msg_type_id, afs_plugin_cb); + + /* if the configuration is set to disable afscreds.exe, + then we look for the shortcut and remove it if + found. */ + if (KHM_SUCCEEDED(kmm_get_plugin_config(AFS_PLUGIN_NAME, + 0, + &csp_afscred))) { + wchar_t wpath[MAX_PATH]; + + khc_read_int32(csp_afscred, L"Disableafscreds", + &disable_afscreds); + + if (disable_afscreds && + afs_cfg_get_afscreds_shortcut(wpath)) { + + DeleteFile(wpath); + + } + + khc_close_space(csp_afscred); + } + + _exit_init: + if(ct.short_desc) + PFREE(ct.short_desc); + if(ct.long_desc) + PFREE(ct.long_desc); + } + /* now that the critical stuff is done, we move on to the + non-critical stuff */ + if(KHM_SUCCEEDED(rv)) { + initialized = TRUE; + + /* obtain existing tokens */ + afs_list_tokens(); + } + + /* define this so that if there are no TGT's, we don't + deadlock trying to open a new creds dialog from within the + new creds dialog. */ + SetEnvironmentVariable(L"KERBEROSLOGIN_NEVER_PROMPT", L"1"); + + break; + /* end of KMSG_SYSTEM_INIT */ + + case KMSG_SYSTEM_EXIT: + if (afs_msg_type_id != -1) { + kmq_unsubscribe(afs_msg_type_id, afs_plugin_cb); + kmq_unregister_type(afs_msg_type_id); + } + if(afs_credtype_id >= 0) { + kcdb_credtype_unregister(afs_credtype_id); + } +#if 0 + if(afs_attr_client >= 0) { + kcdb_attrib_unregister(afs_attr_client); + } +#endif + if(afs_attr_cell >= 0) { + kcdb_attrib_unregister(afs_attr_cell); + } + if(afs_attr_realm >= 0) { + kcdb_attrib_unregister(afs_attr_realm); + } + if(afs_attr_method >= 0) { + kcdb_attrib_unregister(afs_attr_method); + } + if(afs_attr_client_princ >= 0) { + kcdb_attrib_unregister(afs_attr_client_princ); + } + if(afs_attr_server_princ >= 0) { + kcdb_attrib_unregister(afs_attr_server_princ); + } + if(afs_type_principal >= 0) { + kcdb_type_unregister(afs_type_principal); + } + if(afs_type_method >= 0) { + kcdb_type_unregister(afs_type_method); + } + initialized = FALSE; + if(afs_credset) + kcdb_credset_delete(afs_credset); + + /* afs_sub doesn't need to be deleted. That is taken care + of when unregistering the afs cred type */ + afs_sub = NULL; + + rv = KHM_ERROR_SUCCESS; + break; + /* end of KMSG_SYSTEM_EXIT */ + } + return rv; +} + +/* process KMSG_KCDB messages */ +khm_int32 KHMAPI +afs_msg_kcdb(khm_int32 msg_subtype, + khm_ui_4 uparam, + void * vparam) +{ + khm_int32 rv = KHM_ERROR_SUCCESS; + + /* we don't really do anything with this yet */ +#if 0 + switch(msg_subtype) { + } +#endif + + return rv; +} + + + +static khm_int32 KHMAPI +afs_cred_destroy_proc(khm_handle cred, void * rock) { + khm_int32 t; + + if (KHM_FAILED(kcdb_cred_get_type(cred, &t)) || + t != afs_credtype_id) + return KHM_ERROR_SUCCESS; + + afs_unlog_cred(cred); + + return KHM_ERROR_SUCCESS; +} + +/* process KMSG_CRED messages */ +khm_int32 KHMAPI +afs_msg_cred(khm_int32 msg_subtype, + khm_ui_4 uparam, + void * vparam) +{ + khm_int32 rv = KHM_ERROR_SUCCESS; + + switch(msg_subtype) { + case KMSG_CRED_REFRESH: + afs_list_tokens(); + break; + + case KMSG_CRED_DESTROY_CREDS: + { + khui_action_context * ctx; + + ctx = (khui_action_context *) vparam; + + if (ctx->credset) { + _begin_task(0); + _report_cs0(KHERR_INFO, L"Destroying AFS Tokens"); + _describe(); + + kcdb_credset_apply(ctx->credset, + afs_cred_destroy_proc, + NULL); + + _end_task(); + } + } + break; + + default: + + if (IS_CRED_ACQ_MSG(msg_subtype)) + return afs_msg_newcred(msg_subtype, uparam, vparam); + } + + return rv; +} + diff --git a/src/WINNT/netidmgr_plugin/afsplugin_custom.c b/src/WINNT/netidmgr_plugin/afsplugin_custom.c new file mode 100644 index 0000000..d32b878 --- /dev/null +++ b/src/WINNT/netidmgr_plugin/afsplugin_custom.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include +#include + +static +const TCHAR * const dword_props[] = { + _TEXT("OPENAFSVERSIONMAJOR"), + _TEXT("OPENAFSVERSIONMINOR"), + _TEXT("KFWVERSIONMAJOR") +}; + +static void strip_decoration(TCHAR * str, int cchlen) { + int i; + + if (str[0] != _T('#') || cchlen < 1) + return; + + for (i=1; i < cchlen && str[i]; i++) { + str[i-1] = str[i]; + } + + str[i-1] = _T('\0'); +} + +UINT __stdcall StripRegDecoration(MSIHANDLE hInstall) { + TCHAR propbuffer[16]; /* we are looking for string + representations of DOWRDs. They + can't be longer than this. */ + DWORD cch_buffer; + UINT rv; + int i; + + for (i=0; i < sizeof(dword_props)/sizeof(dword_props[0]); i++) { + cch_buffer = sizeof(propbuffer)/sizeof(propbuffer[0]); + rv = MsiGetProperty(hInstall, dword_props[i], propbuffer, &cch_buffer); + if (rv == ERROR_SUCCESS) { + strip_decoration(propbuffer, cch_buffer); + MsiSetProperty(hInstall, dword_props[i], propbuffer); + } + } + + return ERROR_SUCCESS; +} diff --git a/src/WINNT/netidmgr_plugin/dynimport.c b/src/WINNT/netidmgr_plugin/dynimport.c new file mode 100644 index 0000000..bb4b8bd --- /dev/null +++ b/src/WINNT/netidmgr_plugin/dynimport.c @@ -0,0 +1,463 @@ +/* +* Copyright (c) 2005 Massachusetts Institute of Technology +* +* Permission is hereby granted, free of charge, to any person +* obtaining a copy of this software and associated documentation +* files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, +* modify, merge, publish, distribute, sublicense, and/or sell copies +* of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +/* $Id$ */ + +#include +#include +#include +#include + +HINSTANCE hKrb4 = 0; +HINSTANCE hKrb5 = 0; +HINSTANCE hKrb524 = 0; +HINSTANCE hSecur32 = 0; +HINSTANCE hComErr = 0; +HINSTANCE hService = 0; +HINSTANCE hProfile = 0; +HINSTANCE hPsapi = 0; +HINSTANCE hToolHelp32 = 0; +HINSTANCE hCCAPI = 0; + +DWORD AfsAvailable = 0; + +// CCAPI +DECL_FUNC_PTR(cc_initialize); +DECL_FUNC_PTR(cc_shutdown); +DECL_FUNC_PTR(cc_get_NC_info); +DECL_FUNC_PTR(cc_free_NC_info); + +// krb4 functions +DECL_FUNC_PTR(get_krb_err_txt_entry); +DECL_FUNC_PTR(k_isinst); +DECL_FUNC_PTR(k_isname); +DECL_FUNC_PTR(k_isrealm); +DECL_FUNC_PTR(kadm_change_your_password); +DECL_FUNC_PTR(kname_parse); +DECL_FUNC_PTR(krb_get_cred); +DECL_FUNC_PTR(krb_get_krbhst); +DECL_FUNC_PTR(krb_get_lrealm); +DECL_FUNC_PTR(krb_get_pw_in_tkt); +DECL_FUNC_PTR(krb_get_tf_realm); +DECL_FUNC_PTR(krb_mk_req); +DECL_FUNC_PTR(krb_realmofhost); +DECL_FUNC_PTR(tf_init); +DECL_FUNC_PTR(tf_close); +DECL_FUNC_PTR(tf_get_cred); +DECL_FUNC_PTR(tf_get_pname); +DECL_FUNC_PTR(tf_get_pinst); +DECL_FUNC_PTR(LocalHostAddr); +DECL_FUNC_PTR(tkt_string); +DECL_FUNC_PTR(krb_set_tkt_string); +DECL_FUNC_PTR(initialize_krb_error_func); +DECL_FUNC_PTR(initialize_kadm_error_table); +DECL_FUNC_PTR(dest_tkt); +DECL_FUNC_PTR(krb_in_tkt); +DECL_FUNC_PTR(krb_save_credentials); +DECL_FUNC_PTR(krb_get_krbconf2); +DECL_FUNC_PTR(krb_get_krbrealm2); +DECL_FUNC_PTR(krb_life_to_time); + +// krb5 functions +DECL_FUNC_PTR(krb5_change_password); +DECL_FUNC_PTR(krb5_get_init_creds_opt_init); +DECL_FUNC_PTR(krb5_get_init_creds_opt_set_tkt_life); +DECL_FUNC_PTR(krb5_get_init_creds_opt_set_renew_life); +DECL_FUNC_PTR(krb5_get_init_creds_opt_set_forwardable); +DECL_FUNC_PTR(krb5_get_init_creds_opt_set_proxiable); +DECL_FUNC_PTR(krb5_get_init_creds_opt_set_address_list); +DECL_FUNC_PTR(krb5_get_init_creds_password); +DECL_FUNC_PTR(krb5_get_prompt_types); +DECL_FUNC_PTR(krb5_build_principal_ext); +DECL_FUNC_PTR(krb5_cc_get_name); +DECL_FUNC_PTR(krb5_cc_get_type); +DECL_FUNC_PTR(krb5_cc_resolve); +DECL_FUNC_PTR(krb5_cc_default); +DECL_FUNC_PTR(krb5_cc_default_name); +DECL_FUNC_PTR(krb5_cc_set_default_name); +DECL_FUNC_PTR(krb5_cc_initialize); +DECL_FUNC_PTR(krb5_cc_destroy); +DECL_FUNC_PTR(krb5_cc_close); +DECL_FUNC_PTR(krb5_cc_store_cred); +DECL_FUNC_PTR(krb5_cc_copy_creds); +DECL_FUNC_PTR(krb5_cc_retrieve_cred); +DECL_FUNC_PTR(krb5_cc_get_principal); +DECL_FUNC_PTR(krb5_cc_start_seq_get); +DECL_FUNC_PTR(krb5_cc_next_cred); +DECL_FUNC_PTR(krb5_cc_end_seq_get); +DECL_FUNC_PTR(krb5_cc_remove_cred); +DECL_FUNC_PTR(krb5_cc_set_flags); +// DECL_FUNC_PTR(krb5_cc_get_type); +DECL_FUNC_PTR(krb5_free_context); +DECL_FUNC_PTR(krb5_free_cred_contents); +DECL_FUNC_PTR(krb5_free_principal); +DECL_FUNC_PTR(krb5_get_in_tkt_with_password); +DECL_FUNC_PTR(krb5_init_context); +DECL_FUNC_PTR(krb5_parse_name); +DECL_FUNC_PTR(krb5_timeofday); +DECL_FUNC_PTR(krb5_timestamp_to_sfstring); +DECL_FUNC_PTR(krb5_unparse_name); +DECL_FUNC_PTR(krb5_get_credentials); +DECL_FUNC_PTR(krb5_mk_req); +DECL_FUNC_PTR(krb5_sname_to_principal); +DECL_FUNC_PTR(krb5_get_credentials_renew); +DECL_FUNC_PTR(krb5_free_data); +DECL_FUNC_PTR(krb5_free_data_contents); +// DECL_FUNC_PTR(krb5_get_realm_domain); +DECL_FUNC_PTR(krb5_free_unparsed_name); +DECL_FUNC_PTR(krb5_os_localaddr); +DECL_FUNC_PTR(krb5_copy_keyblock_contents); +DECL_FUNC_PTR(krb5_copy_data); +DECL_FUNC_PTR(krb5_free_creds); +DECL_FUNC_PTR(krb5_build_principal); +DECL_FUNC_PTR(krb5_get_renewed_creds); +DECL_FUNC_PTR(krb5_get_default_config_files); +DECL_FUNC_PTR(krb5_free_config_files); +DECL_FUNC_PTR(krb5_get_default_realm); +DECL_FUNC_PTR(krb5_set_default_realm); +DECL_FUNC_PTR(krb5_free_ticket); +DECL_FUNC_PTR(krb5_decode_ticket); +DECL_FUNC_PTR(krb5_get_host_realm); +DECL_FUNC_PTR(krb5_free_host_realm); +DECL_FUNC_PTR(krb5_c_random_make_octets); +DECL_FUNC_PTR(krb5_free_addresses); +DECL_FUNC_PTR(krb5_free_default_realm); + +// Krb524 functions +DECL_FUNC_PTR(krb524_init_ets); +DECL_FUNC_PTR(krb524_convert_creds_kdc); + +// ComErr functions +DECL_FUNC_PTR(com_err); +DECL_FUNC_PTR(error_message); + +// Profile functions +DECL_FUNC_PTR(profile_init); +DECL_FUNC_PTR(profile_flush); +DECL_FUNC_PTR(profile_release); +DECL_FUNC_PTR(profile_get_subsection_names); +DECL_FUNC_PTR(profile_free_list); +DECL_FUNC_PTR(profile_get_string); +DECL_FUNC_PTR(profile_get_values); +DECL_FUNC_PTR(profile_get_relation_names); +DECL_FUNC_PTR(profile_clear_relation); +DECL_FUNC_PTR(profile_add_relation); +DECL_FUNC_PTR(profile_update_relation); +DECL_FUNC_PTR(profile_release_string); + +// Service functions +DECL_FUNC_PTR(OpenSCManagerA); +DECL_FUNC_PTR(OpenServiceA); +DECL_FUNC_PTR(QueryServiceStatus); +DECL_FUNC_PTR(CloseServiceHandle); +DECL_FUNC_PTR(LsaNtStatusToWinError); + +// LSA Functions +DECL_FUNC_PTR(LsaConnectUntrusted); +DECL_FUNC_PTR(LsaLookupAuthenticationPackage); +DECL_FUNC_PTR(LsaCallAuthenticationPackage); +DECL_FUNC_PTR(LsaFreeReturnBuffer); +DECL_FUNC_PTR(LsaGetLogonSessionData); + +// CCAPI +FUNC_INFO ccapi_fi[] = { + MAKE_FUNC_INFO(cc_initialize), + MAKE_FUNC_INFO(cc_shutdown), + MAKE_FUNC_INFO(cc_get_NC_info), + MAKE_FUNC_INFO(cc_free_NC_info), + END_FUNC_INFO +}; + +FUNC_INFO k4_fi[] = { + MAKE_FUNC_INFO(get_krb_err_txt_entry), + MAKE_FUNC_INFO(k_isinst), + MAKE_FUNC_INFO(k_isname), + MAKE_FUNC_INFO(k_isrealm), + MAKE_FUNC_INFO(kadm_change_your_password), + MAKE_FUNC_INFO(kname_parse), + MAKE_FUNC_INFO(krb_get_cred), + MAKE_FUNC_INFO(krb_get_krbhst), + MAKE_FUNC_INFO(krb_get_lrealm), + MAKE_FUNC_INFO(krb_get_pw_in_tkt), + MAKE_FUNC_INFO(krb_get_tf_realm), + MAKE_FUNC_INFO(krb_mk_req), + MAKE_FUNC_INFO(krb_realmofhost), + MAKE_FUNC_INFO(tf_init), + MAKE_FUNC_INFO(tf_close), + MAKE_FUNC_INFO(tf_get_cred), + MAKE_FUNC_INFO(tf_get_pname), + MAKE_FUNC_INFO(tf_get_pinst), + MAKE_FUNC_INFO(LocalHostAddr), + MAKE_FUNC_INFO(tkt_string), + MAKE_FUNC_INFO(krb_set_tkt_string), + MAKE_FUNC_INFO(initialize_krb_error_func), + MAKE_FUNC_INFO(initialize_kadm_error_table), + MAKE_FUNC_INFO(dest_tkt), + /* MAKE_FUNC_INFO(lsh_LoadKrb4LeashErrorTables), */// XXX + MAKE_FUNC_INFO(krb_in_tkt), + MAKE_FUNC_INFO(krb_save_credentials), + MAKE_FUNC_INFO(krb_get_krbconf2), + MAKE_FUNC_INFO(krb_get_krbrealm2), + MAKE_FUNC_INFO(krb_life_to_time), + END_FUNC_INFO +}; + +FUNC_INFO k5_fi[] = { + MAKE_FUNC_INFO(krb5_change_password), + MAKE_FUNC_INFO(krb5_get_init_creds_opt_init), + MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_tkt_life), + MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_renew_life), + MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_forwardable), + MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_proxiable), + MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_address_list), + MAKE_FUNC_INFO(krb5_get_init_creds_password), + MAKE_FUNC_INFO(krb5_get_prompt_types), + MAKE_FUNC_INFO(krb5_build_principal_ext), + MAKE_FUNC_INFO(krb5_cc_get_name), + MAKE_FUNC_INFO(krb5_cc_get_type), + MAKE_FUNC_INFO(krb5_cc_resolve), + MAKE_FUNC_INFO(krb5_cc_default), + MAKE_FUNC_INFO(krb5_cc_default_name), + MAKE_FUNC_INFO(krb5_cc_set_default_name), + MAKE_FUNC_INFO(krb5_cc_initialize), + MAKE_FUNC_INFO(krb5_cc_destroy), + MAKE_FUNC_INFO(krb5_cc_close), + MAKE_FUNC_INFO(krb5_cc_copy_creds), + MAKE_FUNC_INFO(krb5_cc_store_cred), + MAKE_FUNC_INFO(krb5_cc_retrieve_cred), + MAKE_FUNC_INFO(krb5_cc_get_principal), + MAKE_FUNC_INFO(krb5_cc_start_seq_get), + MAKE_FUNC_INFO(krb5_cc_next_cred), + MAKE_FUNC_INFO(krb5_cc_end_seq_get), + MAKE_FUNC_INFO(krb5_cc_remove_cred), + MAKE_FUNC_INFO(krb5_cc_set_flags), + // MAKE_FUNC_INFO(krb5_cc_get_type), + MAKE_FUNC_INFO(krb5_free_context), + MAKE_FUNC_INFO(krb5_free_cred_contents), + MAKE_FUNC_INFO(krb5_free_principal), + MAKE_FUNC_INFO(krb5_get_in_tkt_with_password), + MAKE_FUNC_INFO(krb5_init_context), + MAKE_FUNC_INFO(krb5_parse_name), + MAKE_FUNC_INFO(krb5_timeofday), + MAKE_FUNC_INFO(krb5_timestamp_to_sfstring), + MAKE_FUNC_INFO(krb5_unparse_name), + MAKE_FUNC_INFO(krb5_get_credentials), + MAKE_FUNC_INFO(krb5_mk_req), + MAKE_FUNC_INFO(krb5_sname_to_principal), + MAKE_FUNC_INFO(krb5_get_credentials_renew), + MAKE_FUNC_INFO(krb5_free_data), + MAKE_FUNC_INFO(krb5_free_data_contents), + // MAKE_FUNC_INFO(krb5_get_realm_domain), + MAKE_FUNC_INFO(krb5_free_unparsed_name), + MAKE_FUNC_INFO(krb5_os_localaddr), + MAKE_FUNC_INFO(krb5_copy_keyblock_contents), + MAKE_FUNC_INFO(krb5_copy_data), + MAKE_FUNC_INFO(krb5_free_creds), + MAKE_FUNC_INFO(krb5_build_principal), + MAKE_FUNC_INFO(krb5_get_renewed_creds), + MAKE_FUNC_INFO(krb5_free_addresses), + MAKE_FUNC_INFO(krb5_get_default_config_files), + MAKE_FUNC_INFO(krb5_free_config_files), + MAKE_FUNC_INFO(krb5_get_default_realm), + MAKE_FUNC_INFO(krb5_set_default_realm), + MAKE_FUNC_INFO(krb5_free_ticket), + MAKE_FUNC_INFO(krb5_decode_ticket), + MAKE_FUNC_INFO(krb5_get_host_realm), + MAKE_FUNC_INFO(krb5_free_host_realm), + MAKE_FUNC_INFO(krb5_c_random_make_octets), + MAKE_FUNC_INFO(krb5_free_default_realm), + END_FUNC_INFO +}; + +FUNC_INFO k524_fi[] = { + MAKE_FUNC_INFO(krb524_init_ets), + MAKE_FUNC_INFO(krb524_convert_creds_kdc), + END_FUNC_INFO +}; + +FUNC_INFO profile_fi[] = { + MAKE_FUNC_INFO(profile_init), + MAKE_FUNC_INFO(profile_flush), + MAKE_FUNC_INFO(profile_release), + MAKE_FUNC_INFO(profile_get_subsection_names), + MAKE_FUNC_INFO(profile_free_list), + MAKE_FUNC_INFO(profile_get_string), + MAKE_FUNC_INFO(profile_get_values), + MAKE_FUNC_INFO(profile_get_relation_names), + MAKE_FUNC_INFO(profile_clear_relation), + MAKE_FUNC_INFO(profile_add_relation), + MAKE_FUNC_INFO(profile_update_relation), + MAKE_FUNC_INFO(profile_release_string), + END_FUNC_INFO +}; + +FUNC_INFO ce_fi[] = { + MAKE_FUNC_INFO(com_err), + MAKE_FUNC_INFO(error_message), + END_FUNC_INFO +}; + +FUNC_INFO service_fi[] = { + MAKE_FUNC_INFO(OpenSCManagerA), + MAKE_FUNC_INFO(OpenServiceA), + MAKE_FUNC_INFO(QueryServiceStatus), + MAKE_FUNC_INFO(CloseServiceHandle), + MAKE_FUNC_INFO(LsaNtStatusToWinError), + END_FUNC_INFO +}; + +FUNC_INFO lsa_fi[] = { + MAKE_FUNC_INFO(LsaConnectUntrusted), + MAKE_FUNC_INFO(LsaLookupAuthenticationPackage), + MAKE_FUNC_INFO(LsaCallAuthenticationPackage), + MAKE_FUNC_INFO(LsaFreeReturnBuffer), + MAKE_FUNC_INFO(LsaGetLogonSessionData), + END_FUNC_INFO +}; + +// psapi functions +DECL_FUNC_PTR(GetModuleFileNameExA); +DECL_FUNC_PTR(EnumProcessModules); + +FUNC_INFO psapi_fi[] = { + MAKE_FUNC_INFO(GetModuleFileNameExA), + MAKE_FUNC_INFO(EnumProcessModules), + END_FUNC_INFO +}; + +// toolhelp functions +DECL_FUNC_PTR(CreateToolhelp32Snapshot); +DECL_FUNC_PTR(Module32First); +DECL_FUNC_PTR(Module32Next); + +FUNC_INFO toolhelp_fi[] = { + MAKE_FUNC_INFO(CreateToolhelp32Snapshot), + MAKE_FUNC_INFO(Module32First), + MAKE_FUNC_INFO(Module32Next), + END_FUNC_INFO +}; + +khm_int32 init_imports(void) { + OSVERSIONINFO osvi; + int imp_rv = 1; + +#define CKRV if(!imp_rv) goto _err_ret + +#ifndef _WIN64 + imp_rv = LoadFuncs(KRB4_DLL, k4_fi, &hKrb4, 0, 1, 0, 0); + CKRV; +#endif + + imp_rv = LoadFuncs(KRB5_DLL, k5_fi, &hKrb5, 0, 1, 0, 0); + CKRV; + + imp_rv = LoadFuncs(COMERR_DLL, ce_fi, &hComErr, 0, 0, 1, 0); + CKRV; + + imp_rv = LoadFuncs(SERVICE_DLL, service_fi, &hService, 0, 1, 0, 0); + CKRV; + + imp_rv = LoadFuncs(SECUR32_DLL, lsa_fi, &hSecur32, 0, 1, 1, 1); + CKRV; + + imp_rv = LoadFuncs(KRB524_DLL, k524_fi, &hKrb524, 0, 1, 1, 1); + CKRV; + + imp_rv = LoadFuncs(PROFILE_DLL, profile_fi, &hProfile, 0, 1, 0, 0); + CKRV; + + imp_rv = LoadFuncs(CCAPI_DLL, ccapi_fi, &hCCAPI, 0, 1, 0, 0); + /* CCAPI_DLL is optional. No error check. */ + + memset(&osvi, 0, sizeof(OSVERSIONINFO)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&osvi); + + // XXX: We should really use feature testing, first + // checking for CreateToolhelp32Snapshot. If that's + // not around, we try the psapi stuff. + // + // Only load LSA functions if on NT/2000/XP + if(osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) + { + // Windows 9x + imp_rv = LoadFuncs(TOOLHELPDLL, toolhelp_fi, &hToolHelp32, 0, 1, 0, 0); + CKRV; + + hPsapi = 0; + } + else if(osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) + { + // Windows NT + imp_rv = LoadFuncs(PSAPIDLL, psapi_fi, &hPsapi, 0, 1, 0, 0); + CKRV; + + hToolHelp32 = 0; + } + + AfsAvailable = TRUE; //afscompat_init(); + + return KHM_ERROR_SUCCESS; + + _err_ret: + return KHM_ERROR_NOT_FOUND; +} + +khm_int32 exit_imports(void) { + //afscompat_close(); + + if (hKrb4) + FreeLibrary(hKrb4); + if (hKrb5) + FreeLibrary(hKrb5); + if (hProfile) + FreeLibrary(hProfile); + if (hComErr) + FreeLibrary(hComErr); + if (hService) + FreeLibrary(hService); + if (hSecur32) + FreeLibrary(hSecur32); + if (hKrb524) + FreeLibrary(hKrb524); + if (hPsapi) + FreeLibrary(hPsapi); + if (hToolHelp32) + FreeLibrary(hToolHelp32); + + return KHM_ERROR_SUCCESS; +} + +int (*Lcom_err)(LPSTR,long,LPSTR,...); +LPSTR (*Lerror_message)(long); +LPSTR (*Lerror_table_name)(long); + +void Leash_load_com_err_callback(FARPROC ce, + FARPROC em, + FARPROC etn) +{ + (FARPROC)Lcom_err=ce; + (FARPROC)Lerror_message=em; + (FARPROC)Lerror_table_name=etn; +} diff --git a/src/WINNT/netidmgr_plugin/dynimport.h b/src/WINNT/netidmgr_plugin/dynimport.h new file mode 100644 index 0000000..46c72e0 --- /dev/null +++ b/src/WINNT/netidmgr_plugin/dynimport.h @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2005 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_DYNIMPORT_H +#define __KHIMAIRA_DYNIMPORT_H + +/* Dynamic imports */ +#include +#include +#include + +#ifndef FAR +#define FAR +#endif + +extern HINSTANCE hKrb4; +extern HINSTANCE hKrb5; +extern HINSTANCE hProfile; + +/////////////////////////////////////////////////////////////////////////////// + +#define CCAPI_DLL "krbcc32.dll" +#define KRBCC32_DLL "krbcc32.dll" +#define SERVICE_DLL "advapi32.dll" +#define SECUR32_DLL "secur32.dll" +#define PROFILE_DLL "xpprof32.dll" + +////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include + +//// CCAPI +/* In order to avoid including the private CCAPI headers */ +typedef int cc_int32; + +#define CC_API_VER_1 1 +#define CC_API_VER_2 2 + +#define CCACHE_API cc_int32 + +/* +** The Official Error Codes +*/ +#define CC_NOERROR 0 +#define CC_BADNAME 1 +#define CC_NOTFOUND 2 +#define CC_END 3 +#define CC_IO 4 +#define CC_WRITE 5 +#define CC_NOMEM 6 +#define CC_FORMAT 7 +#define CC_LOCKED 8 +#define CC_BAD_API_VERSION 9 +#define CC_NO_EXIST 10 +#define CC_NOT_SUPP 11 +#define CC_BAD_PARM 12 +#define CC_ERR_CACHE_ATTACH 13 +#define CC_ERR_CACHE_RELEASE 14 +#define CC_ERR_CACHE_FULL 15 +#define CC_ERR_CRED_VERSION 16 + +enum { + CC_CRED_VUNKNOWN = 0, // For validation + CC_CRED_V4 = 1, + CC_CRED_V5 = 2, + CC_CRED_VMAX = 3 // For validation +}; + +typedef struct opaque_dll_control_block_type* apiCB; +typedef struct _infoNC { + char* name; + char* principal; + cc_int32 vers; +} infoNC; + +TYPEDEF_FUNC( +CCACHE_API, +CALLCONV_C, +cc_initialize, + ( + apiCB** cc_ctx, // < DLL's primary control structure. + // returned here, passed everywhere else + cc_int32 api_version, // > ver supported by caller (use CC_API_VER_1) + cc_int32* api_supported, // < if ~NULL, max ver supported by DLL + const char** vendor // < if ~NULL, vendor name in read only C string + ) +); + +TYPEDEF_FUNC( +CCACHE_API, +CALLCONV_C, +cc_shutdown, + ( + apiCB** cc_ctx // <> DLL's primary control structure. NULL after + ) +); + +TYPEDEF_FUNC( +CCACHE_API, +CALLCONV_C, +cc_get_NC_info, + ( + apiCB* cc_ctx, // > DLL's primary control structure + struct _infoNC*** ppNCi // < (NULL before call) null terminated, + // list of a structs (free via cc_free_infoNC()) + ) +); + +TYPEDEF_FUNC( +CCACHE_API, +CALLCONV_C, +cc_free_NC_info, + ( + apiCB* cc_ctx, + struct _infoNC*** ppNCi // < free list of structs returned by + // cc_get_cache_names(). set to NULL on return + ) +); +//// \CCAPI + +extern DWORD AfsAvailable; + +// service definitions +typedef SC_HANDLE (WINAPI *FP_OpenSCManagerA)(char *, char *, DWORD); +typedef SC_HANDLE (WINAPI *FP_OpenServiceA)(SC_HANDLE, char *, DWORD); +typedef BOOL (WINAPI *FP_QueryServiceStatus)(SC_HANDLE, LPSERVICE_STATUS); +typedef BOOL (WINAPI *FP_CloseServiceHandle)(SC_HANDLE); + +////////////////////////////////////////////////////////////////////////////// + +// CCAPI +extern DECL_FUNC_PTR(cc_initialize); +extern DECL_FUNC_PTR(cc_shutdown); +extern DECL_FUNC_PTR(cc_get_NC_info); +extern DECL_FUNC_PTR(cc_free_NC_info); + +// krb4 functions +extern DECL_FUNC_PTR(get_krb_err_txt_entry); +extern DECL_FUNC_PTR(k_isinst); +extern DECL_FUNC_PTR(k_isname); +extern DECL_FUNC_PTR(k_isrealm); +extern DECL_FUNC_PTR(kadm_change_your_password); +extern DECL_FUNC_PTR(kname_parse); +extern DECL_FUNC_PTR(krb_get_cred); +extern DECL_FUNC_PTR(krb_get_krbhst); +extern DECL_FUNC_PTR(krb_get_lrealm); +extern DECL_FUNC_PTR(krb_get_pw_in_tkt); +extern DECL_FUNC_PTR(krb_get_tf_realm); +extern DECL_FUNC_PTR(krb_mk_req); +extern DECL_FUNC_PTR(krb_realmofhost); +extern DECL_FUNC_PTR(tf_init); +extern DECL_FUNC_PTR(tf_close); +extern DECL_FUNC_PTR(tf_get_cred); +extern DECL_FUNC_PTR(tf_get_pname); +extern DECL_FUNC_PTR(tf_get_pinst); +extern DECL_FUNC_PTR(LocalHostAddr); +extern DECL_FUNC_PTR(tkt_string); +extern DECL_FUNC_PTR(krb_set_tkt_string); +extern DECL_FUNC_PTR(initialize_krb_error_func); +extern DECL_FUNC_PTR(initialize_kadm_error_table); +extern DECL_FUNC_PTR(dest_tkt); +extern DECL_FUNC_PTR(lsh_LoadKrb4LeashErrorTables); // XXX +extern DECL_FUNC_PTR(krb_in_tkt); +extern DECL_FUNC_PTR(krb_save_credentials); +extern DECL_FUNC_PTR(krb_get_krbconf2); +extern DECL_FUNC_PTR(krb_get_krbrealm2); +extern DECL_FUNC_PTR(krb_life_to_time); + +// krb5 functions +extern DECL_FUNC_PTR(krb5_change_password); +extern DECL_FUNC_PTR(krb5_get_init_creds_opt_init); +extern DECL_FUNC_PTR(krb5_get_init_creds_opt_set_tkt_life); +extern DECL_FUNC_PTR(krb5_get_init_creds_opt_set_renew_life); +extern DECL_FUNC_PTR(krb5_get_init_creds_opt_set_forwardable); +extern DECL_FUNC_PTR(krb5_get_init_creds_opt_set_proxiable); +extern DECL_FUNC_PTR(krb5_get_init_creds_opt_set_renew_life); +extern DECL_FUNC_PTR(krb5_get_init_creds_opt_set_address_list); +extern DECL_FUNC_PTR(krb5_get_init_creds_password); +extern DECL_FUNC_PTR(krb5_get_prompt_types); +extern DECL_FUNC_PTR(krb5_build_principal_ext); +extern DECL_FUNC_PTR(krb5_cc_get_name); +extern DECL_FUNC_PTR(krb5_cc_get_type); +extern DECL_FUNC_PTR(krb5_cc_resolve); +extern DECL_FUNC_PTR(krb5_cc_default); +extern DECL_FUNC_PTR(krb5_cc_default_name); +extern DECL_FUNC_PTR(krb5_cc_set_default_name); +extern DECL_FUNC_PTR(krb5_cc_initialize); +extern DECL_FUNC_PTR(krb5_cc_destroy); +extern DECL_FUNC_PTR(krb5_cc_close); +extern DECL_FUNC_PTR(krb5_cc_copy_creds); +extern DECL_FUNC_PTR(krb5_cc_store_cred); +extern DECL_FUNC_PTR(krb5_cc_retrieve_cred); +extern DECL_FUNC_PTR(krb5_cc_get_principal); +extern DECL_FUNC_PTR(krb5_cc_start_seq_get); +extern DECL_FUNC_PTR(krb5_cc_next_cred); +extern DECL_FUNC_PTR(krb5_cc_end_seq_get); +extern DECL_FUNC_PTR(krb5_cc_remove_cred); +extern DECL_FUNC_PTR(krb5_cc_set_flags); +// extern DECL_FUNC_PTR(krb5_cc_get_type); +extern DECL_FUNC_PTR(krb5_free_context); +extern DECL_FUNC_PTR(krb5_free_cred_contents); +extern DECL_FUNC_PTR(krb5_free_principal); +extern DECL_FUNC_PTR(krb5_get_in_tkt_with_password); +extern DECL_FUNC_PTR(krb5_init_context); +extern DECL_FUNC_PTR(krb5_parse_name); +extern DECL_FUNC_PTR(krb5_timeofday); +extern DECL_FUNC_PTR(krb5_timestamp_to_sfstring); +extern DECL_FUNC_PTR(krb5_unparse_name); +extern DECL_FUNC_PTR(krb5_get_credentials); +extern DECL_FUNC_PTR(krb5_mk_req); +extern DECL_FUNC_PTR(krb5_sname_to_principal); +extern DECL_FUNC_PTR(krb5_get_credentials_renew); +extern DECL_FUNC_PTR(krb5_free_data); +extern DECL_FUNC_PTR(krb5_free_data_contents); +// extern DECL_FUNC_PTR(krb5_get_realm_domain); +extern DECL_FUNC_PTR(krb5_free_unparsed_name); +extern DECL_FUNC_PTR(krb5_os_localaddr); +extern DECL_FUNC_PTR(krb5_copy_keyblock_contents); +extern DECL_FUNC_PTR(krb5_copy_data); +extern DECL_FUNC_PTR(krb5_free_creds); +extern DECL_FUNC_PTR(krb5_build_principal); +extern DECL_FUNC_PTR(krb5_get_renewed_creds); +extern DECL_FUNC_PTR(krb5_free_addresses); +extern DECL_FUNC_PTR(krb5_get_default_config_files); +extern DECL_FUNC_PTR(krb5_free_config_files); +extern DECL_FUNC_PTR(krb5_get_default_realm); +extern DECL_FUNC_PTR(krb5_set_default_realm); +extern DECL_FUNC_PTR(krb5_free_ticket); +extern DECL_FUNC_PTR(krb5_decode_ticket); +extern DECL_FUNC_PTR(krb5_get_host_realm); +extern DECL_FUNC_PTR(krb5_free_host_realm); +extern DECL_FUNC_PTR(krb5_c_random_make_octets); +extern DECL_FUNC_PTR(krb5_free_default_realm); + +// Krb524 functions +extern DECL_FUNC_PTR(krb524_init_ets); +extern DECL_FUNC_PTR(krb524_convert_creds_kdc); + +// ComErr functions +extern DECL_FUNC_PTR(com_err); +extern DECL_FUNC_PTR(error_message); + +// Profile functions +extern DECL_FUNC_PTR(profile_init); +extern DECL_FUNC_PTR(profile_flush); +extern DECL_FUNC_PTR(profile_release); +extern DECL_FUNC_PTR(profile_get_subsection_names); +extern DECL_FUNC_PTR(profile_free_list); +extern DECL_FUNC_PTR(profile_get_string); +extern DECL_FUNC_PTR(profile_get_values); +extern DECL_FUNC_PTR(profile_get_relation_names); +extern DECL_FUNC_PTR(profile_clear_relation); +extern DECL_FUNC_PTR(profile_add_relation); +extern DECL_FUNC_PTR(profile_update_relation); +extern DECL_FUNC_PTR(profile_release_string); + +// Service functions +extern DECL_FUNC_PTR(OpenSCManagerA); +extern DECL_FUNC_PTR(OpenServiceA); +extern DECL_FUNC_PTR(QueryServiceStatus); +extern DECL_FUNC_PTR(CloseServiceHandle); +extern DECL_FUNC_PTR(LsaNtStatusToWinError); + +// LSA Functions +extern DECL_FUNC_PTR(LsaConnectUntrusted); +extern DECL_FUNC_PTR(LsaLookupAuthenticationPackage); +extern DECL_FUNC_PTR(LsaCallAuthenticationPackage); +extern DECL_FUNC_PTR(LsaFreeReturnBuffer); +extern DECL_FUNC_PTR(LsaGetLogonSessionData); + +// toolhelp functions +TYPEDEF_FUNC( + HANDLE, + WINAPI, + CreateToolhelp32Snapshot, + (DWORD, DWORD) + ); +TYPEDEF_FUNC( + BOOL, + WINAPI, + Module32First, + (HANDLE, LPMODULEENTRY32) + ); +TYPEDEF_FUNC( + BOOL, + WINAPI, + Module32Next, + (HANDLE, LPMODULEENTRY32) + ); + +// psapi functions +TYPEDEF_FUNC( + DWORD, + WINAPI, + GetModuleFileNameExA, + (HANDLE, HMODULE, LPSTR, DWORD) + ); + +TYPEDEF_FUNC( + BOOL, + WINAPI, + EnumProcessModules, + (HANDLE, HMODULE*, DWORD, LPDWORD) + ); + +#define pGetModuleFileNameEx pGetModuleFileNameExA +#define TOOLHELPDLL "kernel32.dll" +#define PSAPIDLL "psapi.dll" + +// psapi functions +extern DECL_FUNC_PTR(GetModuleFileNameExA); +extern DECL_FUNC_PTR(EnumProcessModules); + +// toolhelp functions +extern DECL_FUNC_PTR(CreateToolhelp32Snapshot); +extern DECL_FUNC_PTR(Module32First); +extern DECL_FUNC_PTR(Module32Next); + +khm_int32 init_imports(void); +khm_int32 exit_imports(void); + +#endif diff --git a/src/WINNT/netidmgr_plugin/help/Index.hhk b/src/WINNT/netidmgr_plugin/help/Index.hhk new file mode 100644 index 0000000..c0ba08a --- /dev/null +++ b/src/WINNT/netidmgr_plugin/help/Index.hhk @@ -0,0 +1,9 @@ + + + + + + +
    +
+ diff --git a/src/WINNT/netidmgr_plugin/help/NTMakefile b/src/WINNT/netidmgr_plugin/help/NTMakefile new file mode 100644 index 0000000..8d41e06 --- /dev/null +++ b/src/WINNT/netidmgr_plugin/help/NTMakefile @@ -0,0 +1,37 @@ +# +# Copyright (c) 2005 Massachusetts Institute of Technology +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +RELDIR=WINNT\netidmgr_plugin +!INCLUDE ..\..\..\config\NTMakefile.$(SYS_NAME) +!INCLUDE ..\..\..\config\NTMakefile.version + +CHMFILE=$(DESTDIR)\root.client\usr\vice\etc\afsplhlp.chm + +install: $(CHMFILE) + +$(CHMFILE): afsplhlp.hhp + -hhc $** + $(COPY) afsplhlp.chm $(CHMFILE) + +clean:: + $(DEL) $(CHMFILE) \ No newline at end of file diff --git a/src/WINNT/netidmgr_plugin/help/afsplhlp.h b/src/WINNT/netidmgr_plugin/help/afsplhlp.h new file mode 100644 index 0000000..51738d4 --- /dev/null +++ b/src/WINNT/netidmgr_plugin/help/afsplhlp.h @@ -0,0 +1,14 @@ +#define IDH_OBTAIN 3000 +#define IDH_CELL 3001 +#define IDH_REALM 3002 +#define IDH_METHOD 3003 +#define IDH_ADD 3004 +#define IDH_DELETE 3005 +#define IDH_TOKENLIST 3006 +#define IDH_SVCSTATUS 3007 +#define IDH_SVCSTOP 3008 +#define IDH_SVCSTART 3009 +#define IDH_SVCVERSION 3010 +#define IDH_SVCCOMPANY 3011 +#define IDH_SVCCPL 3012 +#define IDH_STARTAFSCREDS 3013 diff --git a/src/WINNT/netidmgr_plugin/help/afsplhlp.hhp b/src/WINNT/netidmgr_plugin/help/afsplhlp.hhp new file mode 100644 index 0000000..5bc86f5 --- /dev/null +++ b/src/WINNT/netidmgr_plugin/help/afsplhlp.hhp @@ -0,0 +1,29 @@ +[OPTIONS] +Auto Index=Yes +Compatibility=1.1 or later +Compiled file=afsplhlp.chm +Contents file=toc.hhc +Default Window=MainHelpWnd +Default topic=html/welcome.htm +Display compile progress=No +Index file=Index.hhk +Language=0x409 English (United States) +Title=OpenAFS Plugin for NetIDMgr + +[WINDOWS] +MainHelpWnd="OpenAFS/NetIDMgr Help","toc.hhc","Index.hhk","html/welcome.htm","html/welcome.htm",,,,,0x42120,,0x384e,[271,372,593,566],0x830000,,,,,,0 + + +[ALIAS] + +[MAP] +#include afsplhlp.h + +[TEXT POPUPS] +afsplhlp.h +popups_newcred.txt +popups_cfg.txt + +[INFOTYPES] +Category:Usage +CategoryDesc:Usage instructions for OpenAFS/NetIDMgr diff --git a/src/WINNT/netidmgr_plugin/help/html/afsplhlp.css b/src/WINNT/netidmgr_plugin/help/html/afsplhlp.css new file mode 100644 index 0000000..f59706c --- /dev/null +++ b/src/WINNT/netidmgr_plugin/help/html/afsplhlp.css @@ -0,0 +1,73 @@ +BODY { + font-family:helvetica,sans-serif; + font-size:8pt; + font-style:normal; + background-color:white; + margin-top: 0; + margin-left: 0; + margin-right: 0; + } + +H1 { + font-size: 10pt; + border-bottom:1px solid black; + padding:5px; + background-color:#eeeeee; + } + +H2 { + } + +H3 { + font-size: 9pt; + border-bottom: 1px solid lightgrey; + padding: 5px; + } + +H4 { + font-size: 9pt; + font-style: italic; + border-bottom: 1px dashed lightgrey; + margin-left: 10px; + } + +P { + margin-left: 5px; + margin-right: 5px; + } + +P.caption { + margin-left: 5px; + margin-right: 5px; + font-style: italic; +} + +DIV.inline { + float: left; +} + +DIV.sidebar { + float: right; + background-color:#ffffb9; + border: 1px solid #ffff00; +} + +A.external { +} + +A.mail { +} + +IMG { + border: 0; +} + +SPAN.pre { + font-family: courier; + font-weight: bold; +} + +SPAN.title { + font-weight: bold; +} + diff --git a/src/WINNT/netidmgr_plugin/help/html/bugs.htm b/src/WINNT/netidmgr_plugin/help/html/bugs.htm new file mode 100644 index 0000000..214215c --- /dev/null +++ b/src/WINNT/netidmgr_plugin/help/html/bugs.htm @@ -0,0 +1,24 @@ + + + Reporting Bugs and Requesting Features + + + + + + +

Reporting Bugs and Requesting Features

+ +

If you experience a bug in the program, please send email to bugs@secure-endpoints.com and report it. Please +include as much information as possible to enable us to reproduce the +problem. +

+ +

If there is a feature you would like to see in a future release, +please send email to the same address given above. +

+ + + \ No newline at end of file diff --git a/src/WINNT/netidmgr_plugin/help/html/config_service.htm b/src/WINNT/netidmgr_plugin/help/html/config_service.htm new file mode 100644 index 0000000..93687d4 --- /dev/null +++ b/src/WINNT/netidmgr_plugin/help/html/config_service.htm @@ -0,0 +1,39 @@ + + + Configuring the AFS Service + + + + + + +

Configuring the AFS Service

+ +

The OpenAFS plug-in allows you to start or stop the AFS +client service, determine the installed version, and invoke the AFS +Control Panel. This is provided via the AFS configuration panel in +the NetIDMgr configuration dialog, as seen below: +

+ +

+ +

Depending on the status of the service, either the Stop service or the Start +service buttons will be enabled. If the service is in a state +where it will not accept these control requests, then both buttons will +be disabled. +

+ +

The Version field and the Company fields are populated based on the resources +found in the afsd_service.exe binary. This +is the main binary that provides the AFS client service. Unless the +build of OpenAFS that you have installed is a private build, the +version should correspond to the release of OpenAFS that is installed +on your machine.

+ +

Clicking the Open AFS Control Panel... +will open the AFS configuration utility.

+ + + \ No newline at end of file diff --git a/src/WINNT/netidmgr_plugin/help/html/copyright.htm b/src/WINNT/netidmgr_plugin/help/html/copyright.htm new file mode 100644 index 0000000..2d63278 --- /dev/null +++ b/src/WINNT/netidmgr_plugin/help/html/copyright.htm @@ -0,0 +1,42 @@ + + + License + + + + + + +

OpenAFS Plug-in for Network Identification Manager License

+ +

This software is being provided to you, the LICENSEE, by Secure +Endpoints Inc. under the following license. By obtaining, using and/or +copying this software, you agree that you have read, understood, and +will comply with these terms and conditions:

+ +

Permission to use, copy, modify and distribute this software and its +documentation for any purpose and without fee or royalty is hereby +granted, provided that you agree to comply with the following +copyright notice and statements, including the disclaimer, and that +the same appear on ALL copies of the software and documentation, +including modifications that you make for internal use or for +distribution:

+ +

Copyright 2005 by Secure Endpoints Inc.. All rights reserved.

+ +

THIS SOFTWARE IS PROVIDED "AS IS", AND SECURE ENDPOINTS INC. MAKES +NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. By way of +example, but not limitation, SECURE ENDPOINTS INC. MAKES NO +REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY +PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE OR +DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, +TRADEMARKS OR OTHER RIGHTS.

+ +

The name of Secure Endpoints Inc. may NOT be used in advertising or +publicity pertaining to distribution of the software. Title to +copyright in this software and any associated documentation shall at +all times remain with Secure Endpoints Inc., and USER agrees to +preserve same.

+ + + diff --git a/src/WINNT/netidmgr_plugin/help/html/images/openafs-logo.jpg b/src/WINNT/netidmgr_plugin/help/html/images/openafs-logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cbddc47c4a48c2f7945a89a1aa2e662c9fb2f10e GIT binary patch literal 35661 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

8`TI}RPyq6jDgB|{)_ZaI$ce7Ppe?|exqv)Uh9 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 literal 0 HcmV?d00001 diff --git a/src/WINNT/netidmgr_plugin/help/html/images/window_nc_afs_0.bmp b/src/WINNT/netidmgr_plugin/help/html/images/window_nc_afs_0.bmp new file mode 100644 index 0000000000000000000000000000000000000000..67e843f8cb837b2807bfedf58e4a4100996dd0ce GIT binary patch literal 336950 zcmeHwOK%)Wdab;;f5GtLZVcGK-8bNsSF^Y<4D{kI8eZQ`8!!w52E4K{wgKag4cLnT zj9JXWzA2T767(4?N+h< z?dQuXQa}nw0V$BGz=;#7v`8r+1*Cu!fCBPJ-9}3RDIf*16_7_}YomWerGONW0`f?C zq!f?>QXpG_w>|RAzx<7g6p#W^pv?*dx6YAA{+Bi>AO)mATNS`R4)VyhF23e11*AZs z0`ka0i4-6Oq(F-mkVm$7@ilcRAO#8)kVh6uqyQ-(1zN0tJhH`$uc=D`DNv|@JhD(C z1xNuY&|(GTku6?)OQX=o6e=K(ER;wAQa}o{ zSOIxtix*#0mjY6tPyu;lp+pLh0#cyG3dkc{y!e{B6p#Xi3dkc1B~pMCkOD1MKpxrR z#n;rOfD|ZHKpt5rkpiTE6lk#m^2io1zNRh(q(Gqp^2kDo6d(npK#LWSN49wJHFYT< z1qv0AM;1z?04X2^TC9LPvc-$9sY?MVP^f@BvQQ!gNC7F(Vg=-pEna+0T?$BnLIvcJ zg%T-13P^z#D20eNJL7hhAC0#cw*0eNJhL<*1s zQlP~O$Rk_4_?o&DkOGAY$Ri6SQh*eY0xecR9@*l>*VLte6ev_c9$6@n0;GTxXt4tF z$QCcYrY;4fK%oNi$U=z}AO)mAixrSZws`S1btxbP3Kft?7D}W5DIf(}tbjbS#fz`0 zO93fRsDM1OP$C6L0V&X81>}(}UVKem3P^!M1>}*15-C6mNP!kBAdhVE;%n+sKnfHp z(1k~S?|a`n@p|JyzW>t?-nn|h3NQ+r1f+lzkOIS~Ko=hQ-h1!;`t>$DI}5KggES4i zQM_F%FxX;(s?W?OlC1@%l}(?zQ4IbpsGjW zCK8{ugPTb6MVnBTM{>F*hTmIL7K2vmmj&X!sl7-tPY#17ZGi$_H#GTb7G{_au(X~5 zwyN!UTvlU{6BFkLQzk=B7`yYhZ39#w23|K}hNUurdOkR48dSBaM`9|$2W^@nfX&8R zW-vc=#|F~xEhdZc87zj~3F5w~y+|@okii0cE4v7oRuIQ;lm%oewqD#b#xvHJd!OH@ zwZ3F>KoIE<>W@pFseH#&l3iK|Ob{J5EfK}oNcH=fj|Ub&BCTkRx5bFHA==x9+`aW=bd->K^woYvESLmHks;? zUH}M#s_ngsF%$Rl9ep0(H?BAN%}4zebpiZ#;TsQ zUViU8`bgiGeCkLT8Kh+|2DB5HXnyXVG10tM*6L%}m%%Y$%)mhy=Gt=6lXISzYYC>D$68ILhps|KX;o(!eCJocj1v{jkd?FBsO>?Hk(b!xn_lB z@%d6&uWtJ0=U1y&06KfAt;#(jeJiUBFkrlo46v^j9-Y~&7xsSh-F>9R1Pp`GN^CK{ z+CuCF@z|tDWk7Ocm@$EXAOmLL)k+>|-}=e-NX)F8>2}k%nyCFhVzqIqmlSBS0;6{w zRq{y4;6G^ln@HYAVj1y~yvd8Hu}J|bAO#8)=-MOAAcGIucG%JT7o|{61xNuYFk%W+ z^+@>No9&%jI)B?W4Cwb~PolG>Wf02QdoBk6^3N1F|8 zGMJpd6o>(AW3aCR8=nO{?{ScgP2hpKF?qnSyFjb}w4!dl)$@U#R%I_=1<8&+n{Wda z@GC`ZtLdv8Ih)vuCXyEBn^IdXo;d>$bPrm)tXB0%`^_~U{bL?!zGx#V?~#6fhCPF9 ztY(3dy_kx5au}P1_NfZQ0x~RKi!8>vmxY63^6# zt9m2`8+_2FDFWC4y=8L9ewKzkV_`89@C@)7Nb*=gFY-RpXUK|YM}b%(2pDXL0Yj|} z$4qJ}_7Z5U0{IJ?zpQcJGVzS@XOY(@i}x{=aTgwGH<9*7iMWqU^+Q`Y zL;^G31M&f|DoNkPvZ0BaF^mD9$}m76R=`9f&uR=1ldmwlFp_BnnyCPm%&(d)oRPLS zhJ*LPhJ7%Z!gvM{#Ch0MBdFw&bvLxlkh8|kZ-iml8n&_D%jnS#Z# z)g)luKNc_TvzpddD((JJo_;Afqd8A&;@=5yHtNf@& zH8M~e)qn{mp5Kb#!hj&5s)JI1D@Bk$l9FNhNpf0|zo&=j!XxR90RHi4Hk)onecZkl zf81W(k27c8M!qRf8`Xdb6v%HyaA81@P}M;x;8)7ytnrkI$?98@@97~bd1N1+v0GA` z?!$VjZui4$a)uhEt1(rR6>I0%hJA4>c_b!nzs|N>Nq-aRpOA)+|8kRbnpY| z<95l4uQ65DA;e38Y6^7Wk$T2%HOWV#SM9D=d&1GsS+f|p0yTLgpYIbq7>!jwQCtn& zjJGN<03(Ux;G7uWi^)@gkM!6lw?oH_kGB{y3?6O3`4ZTCSq0$6CNLhe3esbX(Y~1q z_?03Cu+7E{%)uYEB>#`eI35hUu+>{v3M)At?O*II)ncIh5e^+=4U z7th%B6E*CaSd2I(C!S5d7s)`&nD>m&5RWQIHG0)R3RL}u`J&2M8;%(Z$l}>uplJ%^ zFKGU<#(m4gGsd4Sfh;*qLF93(U3g@*XY68tm<>0pa@61h%t!-S@ji?h3@~Q^tV%NO zg(hyP$3-i9c}#LtK`>cXV?f>5M8?F);zw1w5ttX2%&(d)sFAiehBNiShJ7%Z!gvOx z0)Ld$fXZYgkL>0dyMCl4TeFyIy|S_mBOfS$ zYQO}O%5Oz*VL*^j)j=u1l_JO=Ny)JMB&n^v%j^%=g-6!)j9oubkNq%vf$dn;j##Z% zFJ>{QoPh$U223!p{8j`P1_TLJ9h3ror993WPnnpkzWF>d`z=4Xa%&}z?A1W4b8D~#5sZoR)Q}uMpN?t*MVPD)z9*Ifo8M_ru7Il#VQefy5=-MOojNPGIa}8Ju zNP+SSRP{*QO1|0jjNS5TD^dzbfnigil1Ijq)H8NtO~gn6DKG*Gl=H~XFI`cQ0#ZN< zv`qncd^wN2aO0MW6p#W^plu42_Q;9*k5r_96p#XKQ=qg*-d$W$kpfac3baiDd8E8Q zSAkD1Ucx!jw#!2EH3eF}`Zj%eWUfQ#T50)J(()6a<*Q$}M;?7yGWoxqS!7rRU~9|I zzG0bu;Z+hWD67Hh+tPc|B7MARLS|Sb8sTvsclB+RxsJBn)Q45S#KhLhs-=oaX6pl} z)g%4PZ0w#f?Du_`pAJ!r0>`>axab5JL%i;uVPp(y8NZTrqrkCN-wxy#H=oIG<@tax zkF8gK_VsyW>I`Din3(U+baj+!3Y5P}{K|T4$MuY1zefxM2&xTOy=qjT!s-Xh%E9<$ zjd2-uN`aEAkA<_PDjs>k2mf9+^nF^G= zN^I$mH3d5#j@3oMeO934>T^^%r-5H?kJ&c|95+5E)v=oD_Q=A6+e8O7-NNB1AXS0F zt0c8o8B%9L8I=Yb)1%ct?$RGOzAn-F?vZs3TzUGbK##7HeojHhs;ofES6?34_a6aQ zK2-AO)mA zwgRO+5`o&JfE17dEmWYy`)m-RA_b&?6lkpix1TTja^i%q2}=PfAO)m=S3n;5_P-fk z8(}FR1*CvHQXVMEh5@$BCA$SVGR;PV9;!+K zDIf(#Re|^u_uC$sdaF6AVrX7cKnh5KK!Ml;Tjr6i{aUOhK6DBM*Dk8xEWUW~V)g0z z=8NslYG-Gyv%9v_+|t?GYi6Nn8#}ujyD!&wcJIGfrP}hyfBBmh{&|(AJah`c(RtMp z++27L{2xF3?%(~<@BYQ_pHS&ifxmwDH~;;g{&aC`Yo)V8k>!!{$f2`~kr?pq{N43m zo!%J#d~^H^3#!{=pIX@(`(*Q1A8%azd1w9(W}NDg*XEx5=oZ?nn>ls*)6-KoW*s7cQ*R_LIZa~76xcfdvzJ#+ z?@ylDoBRdK{L^6|^@4|c{rgiT^@sUCUt{yc7dE?+u#>%pU&_aDyO zp94^-pa7)1aqq#zukU{G#VJU-u)awX(|u&*k+A;;UT)1CRRe_@mLYY%`ru&V?7{V4 z?%z0%y_NmxFAk2hId`N`7K>#_{ow~~6J)VHJ8 zdmyMc_w4yMj~?HgeF#Y(t*m$Fk&#(10+&tA;aBM{t2;<$~y$eKL1 z#){V1fh8UZYn;B2lo?lUo`p>&W$IISG# z=T_EeIF;@raj`Oi0b@L$#Z-$A4p(C}%Of#M=|48wfYC+rV?zPdV(hwVOk0*`!Hn@3_!S)S+l zEX?i*FdQ@1w|9H*kvKXpK950K)stC_We8f?G2lyP&KebYfuU2NbLr#c4)WGTmcyB^ z{@?$7_u%H)L^E zz=kB_SwPAMPFc$R`65Fq)eVd*_$}IIerq;%<;&TjSZ9+>+5|kIBES6ts0OB z$mcCX+{1Rq_W0rL%P2I+eP@<1$b*T~ z`x9rjuAap-b3AfwYYSy{bF7bZX z_H&ODl3UJk5tG`Q#is`MfyBM|(%Sl?L3(7NT@554@JL)(Sh@yYUd&J@SPq+H!JjD>dgRXTZl}Qor*4yn$FXhi?5wPBE-bzn+t_*xlRP*xFuPS(8WR{ycK1FFOr>h!l8v`|_)W zZw}^fAKsrjygQkcJC~EYNqlK?+R6poQ{p}{=l9x|XXkK19~>Sg<=|jnr8@=o_V+io zI*=4k<>c*ErN2dF?-2mHj~pUj8Kt4(Q}(OHxx^-4f8tI)bmPa9ubp!L_83a?_czlQ zlF!=XdEbx3lOyjpyF!sv=SfNC7Ee3dkdMPbmeYfE379KpvT`js6{#0#ZN<$Rp*E zQa}nwfoug@?~z0C3r!7D3P1tf(hWobd8ED{+2Su94$;)*kpnSE98?}Dj~pVS7@?u= z&fi`C)#;7#&+(^*EU4lQUsmwSSiD(#tTR`SR$uk^~@bss5@ z92#30sevZ%*T%DSum5uY#(C_m;B|-x)2H{RP9+7ekDd4kHi^4Qd1PgW>%L>J3skHpv-cu6*MR1FlW`9n%P64r=UY@^J$a`P-~GAUD^ z!Xx*te1s3(^2oj!LVTdEJu=njjARoI>lyMfhzHnfCe>ok!B-FdUCRspzKfK@#P!)gx5%Hz8S$t|1q zL#;sP(#OdiBpwoZR1RmpN?!GK^K9}d`>5cjlK2%4W~!e@mb{{C53A~tys|kWG6szI zBp6h=PX*SipZnM&F-Tr~9)q;1C$kvK5VW#mz?aONH7fD~L!>}w?6c&N`k2996yP9$s_w|3;tMDJ(4+axf>=x)nngkJX>SHw0#4G9*IHv4QDH=ycP|| zj0FU(3=4SXWI!qt9F$doVN?K5(f#&yh|}k3kOs^kll{c0%`0axL3w1~JRvwxeIAL# zxWsMaL5_&GeAJUcJUjLc|C^X1@XO=KXd&PFn`)vAlnY8&pwR&WskT8S7m_7##)+2dkLm6P(8yPb}FslN? zr~p0E8zeju)<{Mk*=ILMJzQ0f#1(8?YSt_gU9y@6#nT3XCnjF6%O_GI`3x#3K~bGzIXIujECDv&k!Czx~1FtufeS^4;=@voJ^l z^2i=t`z6!%*CR^~s-Izv^~fwQ%;KBo0UA%f0(c#wy*1WuCG9?Pe`X9{EQd|9B=3&B z`U|AOiQ3QWLdT*4^2m(8MQo-OHFH$?W}wzo1>})5p@su_WQIqkp4TF!KwS#p&DyUP zzB!n`eRzNB@a|+%?p#jpCh?8QX)70SPl@|T{a$;pCMu7VN7i-KE6;HhID9zuYH=>H z$+?@rD9Mi}UpwXg?J<<(?{B6rB%ig%_5H}@0otTIQXYAnD_gO3DR8i|0AIx2BtCTG z_cti`O*d{C@tq)iy&Pp{?Bw>PPq!|9^m5@I4yvC=4$3Cwk@Cp8u6pI^r2_Iud1UrG zcY3M~PCXSVFcb>(^g>YbECpJBADJbLcqt$SqyQ9AO)mAwgRp9$f5X!rUoempg?@L^v&Xn2QOBi zu5Z5B?yPop);hauJIyVfy}f1@ing({yRrLneP{Rni&d&EkJR@gd-<(C<*rMCRFAy5 z@En)&fBf*ffA>ef`xn1|LZwRu{`%eD{P%zQ)5Wc=mClYl^61tYr(RuWvGPcP@(S$E z-(COJ>5cKv@u!9?sNxM@R`AMLyjgqW;?FztcaS#KBd^Up`O)9M|IdH^ho61;Zx^rM zQmL%K*Vkvhx;As_^vB=-vp@LpKmN(m)^>LuiKoxR8&eJ!kVlrktQA?K0$b;Q_VUW< z{mC=%pU&_aDyOp94^- zpa7)1aqq#zukU{G#VJU-u)awj(`RknN7i`ZD^+O)lJ{%lS-RJMxqst4_EzvZ#DnS6 z`%|Zqg4f4Rd<2`s-DH+WUb+7WpDq`cmY*ywy)MhZ-Iw?d2uzu@g<*vOWscH|7^pY* z?D;p39^agO2uUBUtk>a@UP#MFhE)KNJX!;R?juWI)e5Xrfy5(Wjd;a2%8V;F&%!2? zGW97ua_`DV_|Tp2kxOf9i_Ivw*z-qH7?9t+r>)OcR_C5A-g)#G9yzzNMzgAV9~nEJ zXAJv257;yykF4{;SFX|u)aa4R>+2|R%DM`!T%U?n0B#n*DgYSUvv1I90VIzVX1%OS zoPnvj0nb-g=NFgmK6(O=eAul=VvMoX#iTJYANL&~lj@NeGdDZwtqKgl%YZlz&WZ88 zm^>BuNRNGTJ9NzWc#9#!V1WTD(7E(+atDcrL>`sHnXi&pece2pe9As5_^Bj*g@bY8 zM2#;K`>g$9V*|yUvX(tg6`#kdp3Gvl7P3(=M0(f^h?xEFu=V&%b; zXLWdFN$11tOGbLr>N8IDNM1LX01=Fouh&{|E?GhXRDrp%HD=gkz*iXzz}A=~!@kC(R+*%D(n)z`msm6v@3rlTn2$|}UoR?iazDf+hYl~zMpA*Br0KU6n<_TJTuhb@{ zvP0Y6XA3f@Iw%G3_L}6)+9jo1c<1xve zf@(*s)~lxzQF5;UUhljZbIKa`IEApK zCX9K{81@X31l5vhEv!u6)P%<(aWB5Kw*IKE9$9FjO$bQ6kM!p{!)~ zSglu&CZe>l0(c#wy*1WuCG9?Pe`X9{EQd|9B=3&B`U`Z&i5g!ob|1O3yW8n+!PylY z(K8+gVfod{9rM$cY~9}3Sy|s)SbWjP`^b1n8_1OLNPmK38GW^}cv!~SSOI%Iuo^vb zZ*T9VO4kbP?(J=CZ7;5@$s>=R_+y5BT#xMarK9{UQ2=k&ezow;!TjyR`%{N^CzEpL za&k9`Z%j^Gxqy30+(+j8UiD}ZlH#eHyrHZ5w}?zI z>_8sb5>IN{G!Gw6y;_`0Y;x`nTsCeOXn zR)+$IuU_4qJO@ef@M1cfJW|j7X_|YqV>|~d3-CqUP2xj0et(04-*n@a5#I^I*UM3M z#!hZu`gH5!M=uxdL6WSWNB-mofBEC@zBhaA%XY}t+gUz)Fat4v`h&lkd73=D*or(- z9@*Q|PTA{GAk`x$A3VN3w*Z^ebI9=lx%+}ANB-y^{`kh@XDIb~+A_ay*f%x3U?G9`bKb0K6_ptfp!mWGFER+sP;;RCX^f>pC zUtYf+uZ(A67qnJ7Vrms+5J1M_gKB&f&#;St6p#W^pk4*eO-%&}Uw`qN+?P{R7jE2O zH3r_RVr&d~0_O3I#|ZG%7<-1<1f+lzkODO-aB+GX9vCEi+auA4A6b5#ZIj-sVhkoA zjUgYj`mtm+8Sh(tFJUPl1*AYN3Vc019sj}xnIn$`hfmyyW%0-NUKQiO&$0q6p>GA_ zt1$-SNyKw_0#ZNr)8 zT|f#*0V&W61-kRdU_{+`ST}uxVW=tvq<|EtOM%iJ$@@G5smJfe=ep^eIv9~sKnh5K z?iDEQk!23mrO)NKZ<(VjMhZv)DNwrtwR&Xjqf{96HQa}nc zPyuis0V$BJfIKo=n+E=qrXfiIDbQyHm?ZfmIp6RQDfJV@Jocf+1Pih46VR2gshxB=`gAO)nrC@J8)0n>v2Ew8T9CV%^1e>=XL z3_LP+7vzb_m@yTN_j}J94id8Jb25@3X!X??lQF<*>alz)19)5kDIf(#NrA{CpD!<) zO~N3p1UBhCGH?qBb6k({@jjc!mKKcPc&x0|K(KG})fgK?o`4xV6JTFuynqyt0;8e8 z+eQNaTUcC#P0}D^o9x~rp&WdLn^n0tUD0ZQdonR#j0X%e(pO^)CLoQmJu(EX#(2^n zT38B5fss+*Z7YKR%|Cq#n>1_0hi;lB?m@uBqVvIl5-kTOW9_uJ^us?KutB(|x0#cw=3Pc_``{+@YP1^S+1CNASW?kN9 zJT^IF#><@F%1DIyNUL(s0|g9u=81t%8@Gy)0#cy03Z!{tmQC9CCS#9`#Tz=y8M*<- znv0PFQlJ+KWO!tbO_uS=D|DbQF20&jaHAEYZF1*Cu!Xr}_f9qrp5`NN<5|Dat}DIf);KzkIxKMTQR<&oL< z*nw24N&zV_@(Rc!<%m*13P^!$1xDH<^*7fe|8Kk|C4g%OJAO)nr$SY8v zM|!EeCz1DLjL8^a72UHKW@d~r>`TCaY77tLyOR`<0#aav6)5MC@CLs@4^(qhNp1CH zjEx~rz@vD^MH;GKfqijrU=e zGiG=!0VyB_Mp%Jz9ti==B&Y-pCWm2@fw+qg$Zz$Lsn~b-I0(*Z3L;gN0#ZNTeOHfE17d*$R~KNIXJPMG8m(DbP*@iafH{_p!<^1*Cu! z7%m0g{+0A_ja{Ra0#ZN<^jZOVWcE)cdab?kO93g+d0w+%VH!*YQ AKL7v# literal 0 HcmV?d00001 diff --git a/src/WINNT/netidmgr_plugin/help/html/images/window_nc_afs_1.bmp b/src/WINNT/netidmgr_plugin/help/html/images/window_nc_afs_1.bmp new file mode 100644 index 0000000000000000000000000000000000000000..0d388cacfae4b6ca733a548776fc87324b96402c GIT binary patch literal 17534 zcmeHNJF?n95R`A>6S$nK$_JnvI3eT;9K#`Z;7-b4L_|i$32=*%n$hemET3oTiP_Rq z&GyXfr<)NHdW+@X-_^PO!ucouw)p#jzi-udoU7_j=RbG4(<$0q7^vp5k=Vy6w4Bd} zAhZlE=fl8!cH-CNX0wr7BePLNw?|{(=uUzGU(3~M^>J>y-H!LWosc5uBo-fw7%<># zxm+$GC4@xV@At^)gzT%e?M8JDm>j=zTg!`(RJ1qD@*y-4+Fe->UD_)tIYi^y))N7z z3%GRU%)tpVael^yeeMlKiEFG=l%%OxOG7cH(1`95H->Zx$|BjN!J$eJ8DxHEM@!J* zUtpip(#T+_qz1;2F2P(DaUGfFYA;h}WB6K1QQ9xdhqMHJY+wzx%N=oJNSC0=1+JSJ zA1)U$gUqIDnQfm&>Y5i=-1wB=`v`YJO^!z$qe&!gjC33~3t$?VL-Yu9vaOcu^%~!o zQjt*ca5xOK1Z6yu`&e+tMve?(x^9@nhzmB1T$bx%Fmv@RwC877Ek#Ib*;eIQ(c|&h z5{ILC>S;vyy_d)TnRV~lrW5aLDH6&VrHKYDMb1{y%y(YNuI|m1_|&ZR zR!eyrxvnL4@+D~c5{rD{$HHof{p$;?E=txVrpKlDg^P%xUc$gNEk#Ikft6X`?IZsZ zu0MMaM@#6`wYpv`7EROWiVQ73Tat&zw_2tHLC#*=3Xh)h3tt)*$mk5*)^hYE#sUf% z@U`T-XsO7;u_A0-27E2kchTdnGG;Gfz}FIAiuzRv9yeL3C4)xHC=3*7sh=`Yvbl## zT#)r1c*jK0MZ`;&9>jp!#WBMm0|WL2l{;1aszg6%G84bT(-uQ>o!TjaEP^x$UPsK- z(QSE4SpbRV0;}ti&zdMulbfpE9eF)J_o&1*61^7#*h5!)`e= zEXccPXvr5|u9hgul-4uV7rITiovd$WQtd(ry?uCxh)V}BGK`i(!-DjorTD0*F3KF6 zIowR)cAv?02Yf@&Y6%Je)%fRuUgz}7GRJ!kFQN6B+>-I%A;xMcLe|?(PEe3)2{1=J z@SN*9n~!B(AM@S311)iBMMbtk%F&#F$f2*X;3)TV!Mo8nsr?SL#LCjTq?~%)QAV1L liP!@z(UAky2?@m)@1iFn)y-uLv}81dWQ%vvq+;Sm^$)cd?M(mx literal 0 HcmV?d00001 diff --git a/src/WINNT/netidmgr_plugin/help/html/images/window_nc_afs_2.bmp b/src/WINNT/netidmgr_plugin/help/html/images/window_nc_afs_2.bmp new file mode 100644 index 0000000000000000000000000000000000000000..ce0f628792c2dee3840e693bdc3620dd8ffda370 GIT binary patch literal 82422 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 J&V<;){{bwW&=mjx literal 0 HcmV?d00001 diff --git a/src/WINNT/netidmgr_plugin/help/html/images/window_nc_main.bmp b/src/WINNT/netidmgr_plugin/help/html/images/window_nc_main.bmp new file mode 100644 index 0000000000000000000000000000000000000000..b3b02634dec1e95e88d5dd0f1e7be6601587ca64 GIT binary patch literal 413190 zcmeHw378bs)poN(^2aZV;n&YCt}#T7xJC``fXd-_h^Q#85pfSOizaS~3DE?9 zzc0o;E@&_U?t;oL$TG+<`!K`6Y~8cs|5l%wGlzSts;j4`d%CAjJ(uF#_bm6;Tese+ zsp;kPk-bOt4msO#_;)D&E$S8u9f5z{LVGxHX!%I;M>{RMzw91*xW6`KKp9X5lz|Ro z;MD=4Ir}>8+323^1otTA-x93flDVzeO4$2^?a z0kNhLbDjtYG}V{`#afE7k0k)@fuyF*x4mqNn(; zdpY8~Y|qfTL46A^*sWsffQ@7NZaKG)f2%wzq)i!629yD1z+fO}aA?8d&P^0_6{1Rt zy0)}jn=+sbCwlmTVHWGm zx@upcpRL<8d*;PYOh4zXSD*W2`5J90X5i!Ht3O({`t5gKe{|Yee|zwX+WH3j9K}Ss z{7F=nQoJlNTyV}+SM4kGgReH=N7Cn?EnKrH@2hWeSAVk^hPIS30K!-1Y+Ca5hWYc~ z#);S#j@aiYCer0kqOz2-1<3;i=UjEwzCz#sY8`&LC@L#2EGvt)a@g8Z#sE0qTvEC| zui&eVxi}G}RWy1WAD zYDrbKeU4%x*>Dn-rPN=c0XbJ)wXaaRB+Dx+(V(u{QqMr?wr%-a$~NQ`LRX6u(ABPe z5|yR2U72mVdQ>0%Tx-%;($%e1RcLf!>ORE`;Cd-2E6>REq3_DNKhQoJlNTtLoq zPIkTs$@#tj)z#K7N+4aWsH#HST3MM9O@0NWv@>Gz_9V--ht6JI|ke)`s)oY(SMp}O9=%62tOe}L~AdG`gwmF7K zwz~ex7F03 z(G5>V?6Hb?o^0C&$E34?#b30w;+w*f1n*ZnoU5*~oU)OLx@s>fxjt=gyvr%Y@c~yapEBF%sFUbQodhM% z#FC8wmnkwV5n|JaC7%P72W~SH9AVOAL4!TGT)kenkshQQkaN{luS;@!O%2+%>gtSW zSUJW#3858bxYECizc&?ct=N?C{c7ST(I%GIBVw6e8Ai55kV28XCnUiWkv=TB9*)gJ zf(WI@B+V<#3y)b)1_b0>b+yaus_UAUOC=5MfUDO&cZ|^eYVvfINeiMC#woBq?9v4r|Ggc0C{8(~4Z0XbJ)AzI9wIIWhH^l&^^{|Ce9q!QR%krxy4%&ykAY{lbHCa$p)6vZS@A^Ty>S@ zl#Q;WtLe5Z1I{Pl>g7{r8`*FYm8F2osRP3Ta<01SbxBr-!)VS8kG7OEfD2*E_R7sA zWeMJ|cI}g>ET#V149L0as+X?Tgu`fbVd_4`4B!b&+4jo3gmtxRpG0LT#mf@I1>{_H z)k{|+b#-BFoy0(GBvMgXU9@Fug7>Rk`y?t$slP%4a<01SrK=4M4fWbOiGjL?hN_y{ zEfw3Lt3?TXu65TwiON#yuh4*;tFGF*I)CL_T+U6+%}#4-YSh*l3^X)0R@H=YA_})x z+2<%Gk|ifmSxWuo8H97z)wP54`vS`8YQ>+D9~gZ=&eDZDbn83@ns@BjuymnwBFZXa zs;C{1B_~l?O8p8A!Z~#H*?pbw3s7CFzv+urS>E^;O zHx=0DC?=96CsA2S{o)M5x$3HYh2jPYS0x_My|-dbMz?=`wk+cs6Au1y&xsS!zWdeA zI*F+vJkwd?`THBGAv548Jk=m8Q+3tWRYjBmWk4B7gaOsnMA*|@Wk4BF25bgYS8Yxd zQ3jL&Wgrm-R96#WPji(4Wk4CQ8BkrdIZ;FzPzIENL>ORQU8kRG-TJxKiSVhp%78MU z3~W3(G-n^@eF535tEqo!v5u$=CHrs?^n(ks6>3C<9r}0903XHOLwOQFOnG;7ot! zqw`k=l!4?i0Etjt4I-*Ulnj6Yg8|jm zfTBu7>EEro#H^c^ z%0Lz}pt`EMnuW_=4_O&VDg&yks;fy|U|L)m$U+8GS5;TDaM|l2D+5VoAfT@1;B&3_ zrO&n2_glRT(vo^ow74>$3}iF|s;hdxn$ZhbC$9{oAp@$bs;gP+e7B zbz2vTDFezt`x#JORb6fW>d{PPKpAjjKy_7h)oooUrVJJskW-0^O!+`3l-mhlQYS$xG22#U- z>ZZ2I6 zRn^s0tu-A-8ORm}R9D-ttD(@hXuNTk4YWsM3P#yA_4#R1YFml4Tb z7;@ua1_|Lk<1iRhBU2a%sH+=K4n4aMeP4jS-|A(M=A2)tit*aCK4eya6=Pgt)Rmxt zu#1i8texwW5!+~;TALvA)F_x!3|y9FTL#N?#%+UhxxRsL3I%v|4crb!0L9SWYdASN zN7{y6->0A*%mw9ec9^WGcG@zay4nHvtA;$vuB%O;+y`49^3p>Fbq&xJ_ln_ReFCJ5 zY>7|>M%ZwzEYmsCHn=8Sym3Jo0E2XmFfPlvRGe`h%T8p($KX0#UmBd~19A)-{xxMO zwbP0L)zw6GRVrueW#HV(`q-SpMzA&Edbt@ODgiM-LU>OxJf;y$ID>3(mPG7eczI*5 zL|}6%SA(<2kTJH)$4dio5hK@Y8g@ZOWC|B&!>CVP-Wy@A!5qVeYZ%v5BW)Q_UF`r} zHO{V7OMhE>jUsMc*UL?UWXnC3@rr?$U2GUB5+_?CF@q^Uh8ab;8XGd$wt?7@K;*_* z3=+b*z&XY+IEHZPr7XF(gSoOyr!cr^V;8z?Qw(*za2c$2Iy0cUnk-$VONNw__q-Gs zp)kgyhH+W3)4;h4gEoqUZ5ZgbhP1h47~66z27PjQAg^H8p$(Ag^U!QiO|ERZonx;UIGyrsE% z-9Y1Va|>~>AYn0MVg`7P9up5KZr|1}Jf9Bf#kLTx^^X zHn=`#Ny7-UgJpoU5ovT~S+&!X0oBzG(ADVDchuG1d?eQ=%c_GbOK#)`Vg)*Y7w#JD z19ns$V~DuQ8o}twjVc3?LvU9{O3*dfwG>Bkx{a{QbvPL>19&(FIT8lf*h7F8nZ>Kms+U14|D`O405(@S7=RQh<6RL>cQx#q6ejE2h|D5eA{BhZ`ZN2>=*;#vND%J9?Kt*xki1iVBpW_7X;#!V&fFdDRvP?C|7nlOBP{J#slO{R$v=m8PB#f>=>>| z23K><;&d|P-iW7av^Q$nS%k<6Y>DcI}l$9vcTjn-Ku8wU~2h&?aLf z0^I0wByedPU2^fBif{~Y10fd&keAchkT|ChNL^W(5hGnDlFLS1Q#cs$v;l!3gV~k_ z#c&F_sLSbw7cnwGdMQOBJeEu`M&T?O%(h_|b$Bc;LWVH}cVTb}g9c*bGJxt!gyOj} z1sFv*O9r#;YKXJv>hE|^b=440*~RIQ-IVF7*J+l^%rz!vfNPc)*s;|xihV_R4ILA2 z_uS%5j5pqY=J8fbcD$S|kHv=cCeMy{xcK9fH+Rp#6?KMrlg2y+ea zJVkAem@6t~KwGTxMufK;$U%C@^myovF~`|}!awPeT3+45LyNN@7&c!!HWK6!KZ ztgO0fXsOYK1Tx}szY1P$kt9U!iGCaLJT0aJYe{aVyS0y)Rt%eE}-fv%chrsQ$sFyU3DQ< zA!R@r=zIn`OH^^>s;gddDsh}3G}YD4zf^Pv%0K`H=zrC0A)11~ELR0gt|0o7Hhoc^|{tMoIQmk)Q@fXn!~ZUoCnc6t1* z1dr=^qgZq~$#4N@gu@8ol$b?0ky9i>)m#w*<(}r_IwBX3>6K#{1>xe&uEW8IAusO< zVVn&kfGgNIA)E+9ZtP`{E5vjek=o5522@x5wN!fjbX6*3Oxr8Kd)?rwluqh0$Od8z zmyO6^_DX|oBa(4xuq}h-92mC^&V|7nhXIkcPuB>eQWAtsUE1?lL27ae8+Lu4f_AX1 zBW-&!vaH&f#enLnR8D`}Q&*)CO0NNOmks}#vXt5x z!GP+jzm`g`yRO>8C=oB47l#aB&@zk*ko*k7kQ=<{at*fGV3&-PUa}=ZDU8678+#e# z%0Sp_7Us+OD8cWaqAN0g{tJHh`1ugGd+0OM`7Al5uGmHoA-$GvR7%$Y9&%I^?1* za^=QZ49Z|i;TXe^UUD&lYKF~SBa*vp@Jy%(;nD`eT{c|9xTYEz$AIdpzm`gGe7YLF zHgO5<)LO3}7f>u6<8p(?MhIja25nrX6>$D1&@>WRk&PE zI_aw6a0QK+ox8>bxU(}x&p+4S8>6(ZJ9uS^wBrrLt83siDS#5UZHe%4I62~M%C5u_ zZ**k^w%IU(xyuGc8eL9TBjXrQUG>*e>2=ptoN^zXbalCczDvir;*73us;2@_R$Qh0 z2Qs{IRdx+FK(DTW^KnUB+6XYZJaZdogbl9GS<*1V>|hxnZA2PfSyt`LVnB6ODyP5g zsjJaT!ckXybCpm6<=#%T5L|S;Sx!FyS$^)MUCuGbm9sK}`AEs2NIQmc8PA}9ed%S} z7y>vW(y)h<6>P7KP(0Uk?Q%^uGL8Y&Revp&UQ<^y?qutPl>ucSB@C#pO6ByoRaaB8 zBy|vFAmbTOUG>*e>CFgTrC&KSeqHJTCfI%n0Bv8$OdU zYEsvzZ>RfIhKsy%&!ecJ3}hAqs;mB5D!qy5Dy<3<4Y@*W8L<&N*NAcLB8zYyPIjej z)R!)@CBoH=EOfnsIh_sHV3{r>jS;o045U8;s;g2t{p|#G)m1B`n9yor1O{(#!RF;e zHY2WGBSm&OkqdfxxiSpiT(h_WhmpZJWl68?Vgz9nuX|-66B$rl_19AA4XCTE31rAU zRtS4(wHRIwkYOCdAS13tj4+C^D|47ELSgJO2I3-=VsyQNxf*A2qIAi0iOVdtm4S?4 zKy_6rr@tLgS8?7U3#<&%V4K$lBQSU)uidpPBZfffB9L(yv`L)pR%hFsxu%SPO&iCv z(du#j=YeFfZOcem2R6B63J0j63}gxes;mB5D!svVHIcK=niI2#5ovT|rW*mA&V~^X zv+I@Ug}WA{;J8v;>nNxUbRq+)t5P}rZPiu(h3X}SUbt&D)m2wo6wE9JR9F4ARC*0v zg#yTp&N~oe0oaHPu)Q`S<6Z55DIFVT&%~#`kISeNJkm(it}>8545+S3<@C1=T}_{p zs54Opl!3HjKy}q$OQlzJHLaGePDU9>TLx5DrE>b)S)!}>OAve3c?J8yJTQ5W9+U(3>)2+%~jJFY>je}K8m3Lw%Gt~+k{{*v$)%W zw*Dv6WqhlDr}{AicxX2KQ+!63E@|5Z{k>lClqGW|0^8X9y+*Kh<6%H`HTVw&Lswav zz&G1$Fi40OPUp&q2@=dF*~m4luU1`gKBu+N=b}3gxf?S6U>4i<3^tNJc|9g2QE^TD`ciT#<)gA_tr>nRs z?Jc;7rD3F@tCDUUDRtRkFCktyhr!?&HrOi>X>*nY$(9J!k%$rF+U4RrjW$DGmC}yp zw5Y3>E#uiHgS#}6?6S=U*C&Mct~eF6%eo@=5Hj6Pkwe4FT{e8uc`P=#g3OX$iIeT+ zHE`LLw(E%YD2fDTAbGl)hQgTOlmhB1G>W(Af^{U95i%H;Ckig=`Ui6u2OHHWAV?7| z#VN9;QI=e7d%7@r-89*)>AibO(#Nf(yO&^3SyQWhj$B@BYBSqr& zh%#43O4}Gt7Gc|tcg>Pv(nhe+<;rTbi-Bb8s=T@}7P?a0*vlXz9LWg9FfOZ6jExw$ zQHlo*XxO^S1(|ywhE0eUPQgY@kYLwQT&n@(`dpb2*Ayd+$8tCLlC%w(V0eir8zfvN zl5Io+41&louE~i8LOAcq%UvT&;>IX4QrbKm$IBuzQrdDFvVydYI_xs)$gZ>zZ*;kW z8tq~Lk^;HN+g6EeA9=|+IrQwlp@;j2;`+N4E>EMTL~J`X2&NZ)-PFqVOMbpuAm42v1S z6EGr;E{CPUi0is`4K_gP8Xz5s4H}5-N?Q^^IQFhJ*~MAZ|-Y^r`tY&1hmkh5MBa6Z~m$SGC$H!&(=qf6Eodh43krFU6 zW?0Ms9@mI8x*V1Y!??`-hooT1xs9NLS4|r?5ZRTsoE72N(`JX+h@I|**ts_1mFtBY z75uxhh}!Wp0AYzsSJxfYC*bQ>ago}p1(0zz5Jq-KA#z+~bUBr-^hF#)@)y4p3b0X-^Z zpraXpdL&O*Q5=HCo2^tn2(o;<(j_CM?G+>OxNNDbaV2W7GN24(8v{wz)pnEVrI76_ z*df(eS7B&V29yD1z@LHGuV0P-`D*(FFrBl&;ng=z*QN|81Ij=U22@vr=(+#TCiN-< z%78LpFn|ZQKH8F__p7DlKK!Ul8Bhk4fp{5EU5%G9jaLSg0cF6S0oK*HJ|Doprh1hD zWk4CoW(HJOv-#}nu`2`0Kr$FmT}=k7T2mQN2C|s})zxf1`+DrkfHIH_22@v*!K&6& z29$wpW@*2H?M0%b=8X*#WRip)m2XkP)8CN2%@XOKi4|rE;pTUmN1~YngsFClAa9s z=;}uOd;tA?fUd%T>Z+#%s3QpssIGSI4IB99t#fD6wMPfZNw|8nq%z>n0M?t=1=WuG zRo7~)8|39KjcarPQW$%AdPbxSqbwu9t{}r)y#&fI8pW>62nWbqs$e6WMJ~$aFliVr z+4ZuKPA=@_#yHg{mjTKy0yvTk-jj=q8@S!&NHP#05svr4WlSPA#&y2{6jcV2zyQ{p z*K+L;U6u8vO_!1!n!Rku7#p+^swQz+!PPc`?FvQ!btM=R2@-%9-t0NJ&fF&qrO3;B ziO@hsP1)sOHtafv%d2bPaujJBVMdonPsBjYBwRgOQW zygo5@fJ9sqT?07M$Dmnp0J^Y^%>ZQzB4w8%4TQQ9q%66Yi84~=GS1UuM1~nGJGC;TpVcf@{I(9X$3NljWdf_~@3~&YQYKb5iA_-TImQ)7Z z88DW#T)J(#I;d~O)BzjE^p##$TUL{{YjBc4V_c(~q(SYnLB_RX34+F(twocrVs~LUy>*SNffa+=z#6wGZGN8JeqzhTgrWFIKtDX{|jwCRkx|&v( zzfLAe45+RqK|HjiCj+XhNxG1=Y+5m(y6Pzb>PP|us;g;r`Rino#DMB*62wDGdNL41 zSJwu9omG-93N5P)B**~Pxo*lpDjD!vvCiuO_6a@QKcu?qMUmplfHI&A#KnN>YFu<_ zuriL45+Tg zMVAIE1ImCh5R(Db)tD4%fHI&AC3tT^g(mCl9p6^vthcciHC<7J)P+-+n9|qt8MHX!eCvoC=c50A&{e97zW6$;HJD-0pHD83>RF$NS(iMr~!F3o{V@>~-f( zbEw6UB&d|#!CyJs|}D`GJql( zkpZxcjmx@baeZkRk<{fq>a*<{NdZQf(KRY)R~hJ%4Ag%9PUD(IEp62YEUGQi8WmMX zT9>?C{lz>m;IFGWCx@Pm^ZNpD-I4CG!CETy(Do9-xNXSvRNA(#f;UW$)(;zY*VSc! zb}0_94I082$PQz~h?MEDjW8Rb03*!k8a1`640K5b!e9KmY3<_XwV$K0s5T0Lg{U?y zd8cv7yqYiH1p~>@Ra|M25-YjPodrsG$sWc?MWlrKmzzA*YNaTUS~4a1D~p2D;LgUc%XH z8(cQUMk2Z@*Bg!qUE2HL9AFz>Sw>|}qXt8T+PJ)VbzfI$-7WgGE%IoF;x-QZ_JoZ2?(gGn7`bxw9u6cjJh&LA{;5RD41Q+ zFkIXv1A7Fs&0f0##~AS(Nu|8HjH{sxbYTV}t3Gbp{8e-A3c6*b`_;zPi|}RvNGmtI z#Wi`l%9mppkxTJfURRn$Jjy({il<9t1Ue*S9uoMfGeGN25klmR>e*s*1^ zBdyV=0nP&fJZ_DuYvbBQXwLhvR(; zWx$^S)m49f)T<0A1Ij?W45+Tg%b3P11ImCh;Lm{SD)@=(#lRY@3@8K202uI*R@GI~ z)r@-^olaO8PzE}Y0o7HXA1mYnMV)wpbZBKj8R&Ed>hd>Ke)>+;;(z1gs<@$ByXZ}7 zHH+S;{`B>#PyQ9oUx(SMuBxtf`l8hdCFsYy?*g4kwvdUT5&9@tE#JMzNB?}%0QD=j*GMFKAr(0ru1ySIuu)^-kl8 zx1F{O>iQa_6?d(et?Ft!boG;|gHq5seJ$w(lmTUc7;tnIa=Pk6r>z$4t9K!-PFwLN zbhY7&*YH39DFJnL-Jre|QwMAu(>JCHb-e&kWUQ_fh$@CK2Gy=IpbT^t10Aoc`-C3u zA5vYFC)5x<*=5PK~hMU$)ZPnpb}`Yr@E;uikZ@ z0JJtdWyPPdFwfZctMTh9e;+G<6(A#gH)|jqi2zrd_A+7^+(o*y6Oq>7x=LrCZZ!icEmA0w4Nj+Ox>p92flg-t-(c0aW>HI9^#O}& zi?l{X)sfaEZ&!aY53^0Bu0rTY>u4{vP9nDLgRqMX?#ki`+P1Ee^zpgoE-57K*$aaL z*vkkR66Y*R;jS9WfHKgD4B-0#@vYX)Yd=S0QEe0g3sG%a@=oKDd4ay?8sEbb=W_r< z*;wu*4qI-}J#<<9Ty3v-4kkl}v5PCW&v4aMDTbthj2H$MgApJRvb|C|aUto@%78M! z46v?BQH8ETP8sPGU6q<96;J9R#n@gumtzcryGW6?tPrp5s;f*mOQL~zb%9fgt3g>D zz(v$h29$wLWB}i4-Lzqe)7CDAkYexLzApU_Zcx!2Bzm(7yZX!bFwc}d2Y|}aMzYr0 zYq?EDmz9uR1bW3&7-jiHQhFkLS6yW}ZMOmP{c}5U73t8*fHEK%s9V0UY2)(djZ2%? zectwiT2)1$g8r{xS$o7q{x|)U8ksh)#AQ|X%U2UIsrE^x2 zbk+YM0KD?Fs;vwt1D(h~{o2oW6s>Q{U)%i6islVVown|C=dKm6XkA6^L)^RKel@Pw z0|dWcO=pE{-`rGJJ8?bh(8@qMFn}ijJGN|gq&509zz{i^C}I-FdcgEF8DbP5Aa6-Cfe+_mC?0RD!BhF1vS))cS5!dnK=B8%qMe)eX~ zC$H5P%d4v?&A1fHDvl13to{ z_p3hSs7o1829$wzFrd2H4$?G58Bhk40Urj0uJ#G|`cQV-j0cAiLSbtLJh5en+ zwO)UW>T0>J2W3DRPzDlbKy@{74z+?ZpbRJjk^$9K$%fj>fHI&AB+h{9YT_Jf1!X`P zPzEFes;iO>wUq&7Kp9A!0oB#SIn)ZufHI&ANCs3_B^zoh1ImChkT?UXtBG@{6_f#G zKpBt>u&yrq?@;bZ(KbA^>XaL4h?Bb3kHUV@`t;hi<~n;Ij2V3;1J%9injjb)-K_v zwjjXQo#?c+Ka1{ecjkl z?WLjWOG4p`Ly=3I2BT(7sOJ3EJsCivZTtD5Z6iY1Q%v=U5Zbn3X0vs*;*wC+Wg*&E zUfBaJJi!rCNND}k-qf0|?@g`ox*m4hF||9IBe~krg8}@c)20k`c?KFM_p1HtfBz?*eyeMx?jyZDTMn~QqDykuUT=>gE1Nz4HA* zuYT{y*S~Yb8+{LdtM|aydmQ?Dw;#O~I_TB?e*W^ZueMY^F_~^(<^3w_DnylZm2Nl< zMcr|0AG1XT)|OThSlxFz1F2cnI*KyTxePQ{6mA-S=%#b~ZyK{#-lh9*zWm_aaffWa z{Ghz?2j-36KX3fLg;(rVIDSCUxc-Hg?NW3}zoOAS^G0=BH6--!QTyR8mvj}kuZNz! zr2o&p-0S2o_dj#l!NZmvGHlsFLzf*mZ2A5}SL{1v)~w|5MlOGI({r6V~)R zc6GNSSA+(BxZlrTLJ@ymt-i9y)+@UgUeP0eLXW&jy$UDwE}Gm6t?-)OMb|p5?7H5y zH}z?T#(Ilt=aO7~Rj(yy_kD1W?@;U9$>`|HfHIJ51|Vx&&)a3&WqWVG>d@^|ep-3m z(UsR9Q+55Isv8DX-FQURO#`cMI=K4A{c3L5vu0}ln#td*zOv8uOS|Wd49z<+#JUP& zm*dy&ea41EM{NA*1>YP!X45ej=MEZ^JLsa^BQDxJa7^C87Z&VyUcsIt3j3c`_`Nd< z`IPGs|_Wb+Mo_`+} zZD;lT``JBT7}@KSvAv3~>D6$f^E9C4cmM0V?Ldn@UtKlQ(X@5v_a&ojT<_w``jEse zIHey%&npbLR6@4dKme+t3)zHi4+OC8wR>tdug781287$(6Z#=HGRc_$_OOAl7ls_j z?ivUM^WN~HOAUt%!)Is&P>ieqmQb0vlCzRF)wdB@kZV#H_7cIYz8T$z>d+qaBAtRk z^0q5`OK(iuo(xj>=rGnlk93M-TwVqk0tj{KsJ#Y7I?*W8?Gar^!8AS{(lWG6aSfIM z(&lAMJV=-8QUsYe-uz4YaN>f&{i??I?D9`LQ-6TC*sCwvy=LOUHP;QQnSMg;t%Jk2 z4z9iJq}tn0*ilytZ{!z;MeaB_a{GRfoA<1nwrga{cWbWfRdiwZ1%tc6OV3wf?EdqO z2M^C3G7M76?s`t?ch4;8 zHMpSr(QCRL@)~ma`Rb>GoYz^cIna4|K=E+rH2~1n*KZy1=f|IX>ZzxGF>OM(Q0UkJ zyC2bi*Zp_fb-!JAJ7D+zhwL$6z%IM|;@BQ9oYU*;@x5!O_MkOJFAM;m;f*UWMB|1< z^_v`H>Hqw#{<9ZgVB1kyy71Tc|Kq3q@PYw5tYTa*3d3?m0NZQ;Z2Iu8=vL;guezeA zYlX(wW;WGS)_n9r@g>gH`rhDv7!D1^cZ9bickb=e@-OP$_}sOy>#8c3pWWAa^+L4C zf#d{S11OmHuJHgHwv}QUK0||(`id=>r>uaZKxJ^XdSqW3;^Whtd6G^1pzDgrf*fGa z69{J2uzCT=q0te%{oQA3%C=DOj^cHtuRMyeKo^Y_b8k-%DZ~Jb+7$~M>*^5GTwA%~ z$@_3<0GsAL0)&o~j{38w9+M}92Sqy07lJYE{5dV@_K2>dI7LeHXlR+@8Y}~(-L&|5 z>e7^?%XKMYtv1!yt$TMaC(`I>aly&OgW-v`iZr7oL)O)Xn4ku z@LgwwXP(xa^98(-yN{}yIk0ZVL3MZRQ+LY`>aO3V=Bj?h7xh|rN_Tki+5o&B0LC7t zesje6n@_wv|MZCkb3bqd`HV?Lr%fsve09;!CKexcWy!$HOAi|BTqDo^JF?4JrTtDV z>~-AN-GB5iAkfv4)11FsEjp}CSMisuvhy8Xz5S|-Zk{&%#ecjwfByX4L!m>v_c(p; zy?(mK|NZG+d;WCqy?cd1{knI5=JDT79N7KOr}bPjHu`FT=pVVT-kS54qdqQ^=4Efd zAbUso=H{Hw@CpZXab^AE@P5skUBV~;F^+CTgUQC8jEb>6o9pXPq5P@qy(&Nr3f`aF z^!{w5G*^})ct=BB)${khcT&rsU%mDs>SK?9lCM@4kLm5a^dj1vVOmF*6L1YMf?eYq z3Z2_#3`etd9U9k~qlD23fT))EoX=q7f4TV0WBb9_UXb_kDScgsfEODv)>1?i^iOGg zWhTgJ+U_X-`jLH_*UU#La6)x>&NyF$#N0>cveRVR7>E|%^wB~r3=B%^0R@{jJ3oAJ zq|T~{9yKO_`BYujQw~RSLpng5Voo{+2MOeMD95!e9u|odxwPYJ1u|>k$ftPCf7kYgR8UD=W)czi!5mAO2^A z^RfZFet;Uy1Zyw%0%x|^VB-&AWq5Az;aqefjIg5~$7O^ujuf`ZfL2#-a%8IfleyAs zRKVg#S=`c;|M@xu!)|>4uMjRWs1)yUFO5>%2uCC#Mpq7M*sk%BFW=${m;(<=hA}i% zXl7llb#BaXUx&%SfUbT$vQLmfsgB&!aLz$p>bzPHO(IQ8oF9MCt9O6ak99*v;s=w= zWfwqdj2M;CjeOC@02k*(81>Igp~+xXpo=|q@jJ!WXZMknIbDMGh#V$IK^WsQg+U`& z^5bR8IvkTbQmpx%c`h5>vu*v4?um459m@dDLNy)&+%mZKuCv1TjSAm?Uikh|;roV% z?;X~hyK+bM*2ulbMP?pecjqCIJAN3sWzWcs18T1AUo!6d3(s^evC1(aG#GdYFnE0a zS=SVfy1wYV8;a=UVt@LS;-60{K5k+OBX|@rU_?p(VFlkmW$kwkcXX9R74NrtXP|Sx z3SGrJ3!tloW1L3;58XOt`t>)?yyMPa-9M|SsA$&E!*=WYf1s%Q?9#1p^@ov2q`bVm zwzjsuzW#5sZhK>Bi>Nx+@};5XGUtwK>w*{m=P>6+3mX<~-HS6DsvV(dEL;a0FOhW( z10KzAQlw2FE^ru7nAK(!zbQ8RhhlWOG z93HvjkjSn3N2c!;nYKsG)IEx?81VkE|AH5<_D6&9lXEwpc~!xv>xwS8vG~Fpi_f3d zCar@f6(ay!W$ljrN0;n%UdbLKi}yIEV8H2L|JNYr{$%s1&J8N->Y^VyZ^CkP^?XNH ze}C7Fcin#HuO687+nZmue=$XoXfq_w7|=IIA-d38v4 zdItfmcDw?%F?Ts>T67BEzH@xP9ktGbO*}U%8Ql}zHD~v64FgCixW)@E=t=g1lUg1y z(sRGcr3>L*Kccr+1q{9T^u82OUtRglC?^u7K03KCjOOj7ut~U3aOE&(Cfs;*Lb!_& zsWE~8w#g-tny07W)~kBM3XXx-e`p%9A*!qRr5HbkmY&%Myw$8(gd)(@&FA%!Lr}ac z!Y(p+4)#2a0FLAeHo`7fgX3)m_~sC2A7S_@iPO~F;5^lFjx_o!Guw3gN+*#9q9uew zKrj%TO*(fxB7$+QT}MH@fg=m043MUTV6G__p~PVuU3<;T0N3?TMRea$4B)KQTz6FM z-NVBVj*k5DqR4||9Cf{KWccp0npV6AZ*$4Ux~Hy)+;T`{`u^b?_YU8%cg@tjize*7 z@T}e7#UoHO7)OoH8-8u!=$nc!nqEBSrV^;@$m`pr6#>`|8B@If1;u-hD%yKw(cUBS z_a3@tx1*iss}NPZl@))k!u=}V$0~Idj{^Sm;2jXv$A9d1rkfQmv_ z>nm!vmv7I_$t^4_+`4t^?E9xfSIfusXla)_vW)i1QT<4}>MKg~F6c=Mc-60OfRq(| zFc*kx7{KVPzuoloeXIZeDC|f{9)bzPg@DxyyKI5;fOPGM-u6I15EK6RpO^ry*~t9a zK&n1`0S^$qIjnTrP?FlusEL_T)@41^=yd~VN%buG`6*~YWzxySb7f$CQ|ck=itgL#(c ztS?=>aQ*P!Tpz(DZ$1N;JqlGIe9K33K^^K~gW(lS0h|8Xx(JK&Fe@GliD`H@){M}9RH`|$l2gzp&{zH3%>N?HiuDU-i`eize>B;Oqf#;g%Bb;)ikk*!=TvD8BgSlCigxTs*z_f@wt~ zrxpzp&sq<=xbTn*3lBK2aNm)I`;9Eze^lQ7XRjG>tn+#R=i>s74K4q1=;cEkU48Gr zLo1JSK3)JHoPtjVc;>hFJpTK~=KS^VFTM2A5xa!?hPvM|?lkCXWNU3>Rit7|#oEn`@q*3Esl*&8E9GxCjAghXF(uo)yqK zG5#i7@#-HCk5>Cei*TVJ#EL-PN5^Ry z2%yn55HBzO>=G&8Vq96s^>%KryKN)sy!D0ar8%Pt|KsB8Q3?`wkC(`s)$vEl7M?Fd&Ptnuw9!q@(=cFF-&*B)Fn@!$o+_XOyCG|F)2wElct z{@7bf#@$ge?)H+gH{(%2(WvX%{(yx5Y(F}`;J|YWemElkfRXtJpS$_sb5`$pyrZiS zRR}3`75A%m@6x8LIp;W!0{-#%Lx2DC(-76CXWf9CRcvF29gPR7HRV+eRduC##mg2g zS-pC7Zf@=qGp=J@ExkMhQAMMVZNa13j%v|EaV~^_1-H$fg6o2^@W<|&_h!pP!m(d4 zxUa-v8v_|#Bf!gxUnk3+yN48A`TTiQ?b<&r}g};^q0+({|9vyaR^k9XvAc z$LHn#c*IwGo$xAfx>?1q|F~Z@bQPax_3G?Dy!hPnb6=bL{`>FWHfaQ21dyM<1`h#h zw^lb+*B9p&F8=VdrAwEtUAy)VcU<$&GrErRTA3{eH8bRcsEdM@A~v~vteE`FC> zHmpUv;DT_W(LiL&F6VLp5Vno@vRD4#8V}HQXbvU=Ww)(blyh!R>efepL7sO)%Z~sa z6kZya5uS!!5Hak9!72E&J03yfVI~hvkz9cz8SytL-iHDI^wfB1lI>VdI2ZUa$CJd5 z?7FR6V(Mx;2I3*q2+jabD|EH)@hNprOs)IFq{wf^@7P+jqr5OO>(biW&ZwO}7%#!9 zd+K_GHEJNZYoQwCODdsxB5f%8Y~4N&Upl>1AkKDcGlJ*DICD52GY{TUMq29GN^ z?vgfX{n4nrf#>BNc7E=_k*oGT;T6Qt&8pPZFOCnb#9y+`3E}fp-+u0?w_bT;!QB6R z{@LgFqkls~1O8mqur0i!VaKo z+l(gz!5Q|CjC|?*Z-nm{R(tcQ;k(Xh`r7#`aMPMkYp*@5_S(a%u05jg zs>9wJu@6AjRXk0d_E6bPzb%8b0;g@{l)_PvfsJ{=TVd5ZH9*7(@!5C8X%ufP8K{Q2`g{q$43P8ENj z+P-~zRaF&kS?lWRN=ix=E?oHDd+)*fo9Pq&d3uYeLRyogt3FFtUCMwmpbUTk2pwJk zQ1|yc>zI#wxhBf_Kuouk=eJ_Tz?ea?pk^6kp&Y? zUBwFk?tH9#<`d=9e_Qt9ml32mPg!p$hO`d7y5O{^wEk?YbJzOHf2s$K$vg7GO^1y# zb(Nk2Kv(ew0en-y*TX{VFAQzEH1wC-#@&AV?GHZqAjEU!%9R^8ZY(G$z-z0Zu6X?_ z1a|rIk3mG`SqS3S|`!vG%aXiEnM@SC8vnE z&)w@t>l0J!Uwgc{)cJcQZOtXQb^mieUgKJO+bK0SpICMMG5He){^zVc;N|<(OKvUs z%`+8`JiG1QKW>Gz;;!}TyGzIa0xu6J9*vi?-cU4rO5r(E3h^4(Q?4w)6W3!d${jdj zJThlMU{1T5+H*emIJJoI5w&4$0xNB`{ zYAP))efFNoAB{BcSD~xvu#9yM%0OB%kUUAz%3uJp7I}11{aa5pyfYi>S~q7#-Rzs| z{&)kPxJK}}_4gAak4(Tz1@MA_@Pl~$)ri_V&ZwGpeEt=O&KvpzM_11a;d`cFOuD!9 zsTa1-ez_9rI^(hOn}1h!!>lcM;yURb{4uL^!d<0!sQ_LuaKVj5Bc>LfaaFcgq z&dMJg{UUPlTR(Jl-T^Jo0q`^cy88KPq2(jGv91>2sp7x9hhQLRTTAtgEnXUB%M?sAcIyrHmuz8`J$4oEA->}Y_TzL9~ z0{qs0@)b@y>GFKEqc6%sJMzN3Lr3QBHzenn3zx54yKU1$lSxp{2rQrQLoY`Cgb+iKgIUu{&w<`Q+wf0RCvTnx2e$84O`0a5CFOg zE#*cxt58d5X65zWsnt&F;nfhpU)2hOzrs}wWk4BF2GW^<@~e8Fl}+g3{N6pTJDUCK zCVqrPPcv|HOOFn4M~atG;V=GZFjk%6NGo0m08u4f#hoy2R_RxJz7d65LP#5K?18Vf zYQ7~z-(y8KebE(xgur&d>Amswf^FY;#osEZhBA=F3^?DWp)CyH2qKdK@>*1)1K6c`_Xn!2+ zw5Lw-Y!smTbYQ^eG^s0<45Y(i&^ZtT#(4)wvc`K7I(rWpG#aV#jQ;rQYP_`yf4VY6 zmAiP*N&?Fd4e;E6-in213-tOc+SAJb@GQc4Djt0?06iYJKdnxY&SIdWKHpO7B**}N zhK3rQ%YeKRaZY8|v7;ZvS{U8qEB|1iIp6&UWVmDCW3TX0)o3u#bzH+k_y!h;DoZFC F{~wS1pv?dP literal 0 HcmV?d00001 diff --git a/src/WINNT/netidmgr_plugin/help/html/images/window_nc_prob.bmp b/src/WINNT/netidmgr_plugin/help/html/images/window_nc_prob.bmp new file mode 100644 index 0000000000000000000000000000000000000000..51c16d683b93471a705a0c6b982bf5e1971557b1 GIT binary patch literal 161094 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 literal 0 HcmV?d00001 diff --git a/src/WINNT/netidmgr_plugin/help/html/template.htm b/src/WINNT/netidmgr_plugin/help/html/template.htm new file mode 100644 index 0000000..cbdf541 --- /dev/null +++ b/src/WINNT/netidmgr_plugin/help/html/template.htm @@ -0,0 +1,11 @@ + + + title + + + + + + + + \ No newline at end of file diff --git a/src/WINNT/netidmgr_plugin/help/html/token_methods.htm b/src/WINNT/netidmgr_plugin/help/html/token_methods.htm new file mode 100644 index 0000000..7d163d6 --- /dev/null +++ b/src/WINNT/netidmgr_plugin/help/html/token_methods.htm @@ -0,0 +1,70 @@ + + + Token Acquisition Methods + + + + + + +

Token Acquisition Methods

+ +

AFS tokens can be obtained via several different methods. Each of +the methods that are natively supported by the OpenAFS plug-in is +described below. Note that, additional plug-ins may define other +acquisition methods that are not listed here. +

+ +

Contents

+ +
+ + +

Kerberos 5

+ +

A Kerberos 5 service ticket is obtained for the cell and used directly to +construct the AFS token. +

+ +
+

Kerberos 5 to 4 conversion (Kerberos 524)

+ +

A Kerberos 5 service ticket is obtained for the cell and then +converted to a Kerberos 4 service ticket using the krb524 daemon. The +resulting Kerberos 4 ticket is used to construct the AFS token. +

+ +
+

Kerberos 4

+ +

A Kerberos 4 service ticket is obtained for the cell and then used +to construct the AFS token. In order to use this method, the identity +must be be configured to obtain Kerberos 4 tickets when obtaining and +renewing credentials. Otherwise a Kerberos 4 TGT (ticket granting +ticket) will not be available to obtain the service ticket with. +

+ +
+

Automatic method selection

+ +

When the OpenAFS plug-in is configured to use automatic method +selection for obtaining an AFS token, it iterates through the Kerberos +5, Kerberos 524 and Kerberos 4 methods until one of them succeeds. If +a realm for the service ticket is specified, then the realm will be +used for all methods. +

+ +

The correct method to use for your AFS cell will depend on the +configuration of the AFS cell and the associated Kerberos realm. In +most cases, automatic method selection will determine the correct +realm. However, other cases, the method will have to be specified +explicitly. +

+ + + \ No newline at end of file diff --git a/src/WINNT/netidmgr_plugin/help/html/tokens_per_id.htm b/src/WINNT/netidmgr_plugin/help/html/tokens_per_id.htm new file mode 100644 index 0000000..ec14f2f --- /dev/null +++ b/src/WINNT/netidmgr_plugin/help/html/tokens_per_id.htm @@ -0,0 +1,189 @@ + + + Configuring identifications to obtain tokens for AFS cells + + + + + + +

Configuring identifications to obtain tokens for AFS cells

+ +

+The OpenAFS plug-in maintains a list of AFS cells for each identification. +A list is created the first time AFS tokens are obtained for an +identification or when the list of cells is +specified in the identification configuration panels. Each AFS cell that +is listed is associated with a method by which a token should +be obtained and the name of the Kerberos realm that is used to +obtain the service ticket (more details are provided in the Token Aquisition Methods topic). +

+ +

The following sections detail the process by which AFS cells can be +specified when obtaining new credentials or configuring identifications. +

+ + +

Specifying AFS cells when obtaining new credentials

+ +

+When the OpenAFS plug-in is installed, it enhances the NetIDMgr new credentials +dialog with an AFS tokens options panel. A typical new +credentials dialog is shown below demonstrating AFS cells listed in the +credentials summary window. +

+ +

New credentials window showing AFS cells in the credentials summary window

+ +

Clicking on the AFS link in the +credentials summary window will take you to the AFS options page. +Alternatively, you can select the Options +>> button which expands the dialog to show the +credentials type buttons, and then click the AFS button at the bottom of the expanded dialog. +

+ +

The AFS identification options page is shown below

+ +

AFS identification options page

+ +

This page allows you to specify if AFS tokens are obtained for this +identification and if so, for which tokens and by which method will they be +obtained. By unchecking the Obtain AFS +tokens checkbox, you can prevent AFS tokens from being obtained +for this idenfication. +

+ +

Once the new credentials dialog successfully completes, the list of +cells will be saved with the identification. The next +time you obtain new credentials for the identification, the list of cells +will be loaded automatically. This list can be edited either by using +the new credentials dialog or the identity configuration +panels. (See Changing persistent tokent lists). +

+ +

To add a cell:

+

+

    + +
  • Specify the name of the cell in the Cell +text box.
  • + +
  • Specify the Kerberos realm for the service ticket in the Realm text box. Note that you can select (Automatic) as the realm to allow the OpenAFS +plug-in to select the realm. For most cases, you don't need to specify +the realm manually and can safely leave the Realm as (Automatic)
  • + +
  • Specify the token acquisition method in the Method. You can select (Automatic) as the method to allow the OpenAFS +plug-in to select the method automatically. Similar to the automatic +realm selection, for most cases the plug-in will be able to correctly +select the token aquisition method. More details about credentials +acquisition methods are provided in the Token Acquisition Methods topic.
  • + +
  • Select the Add/Update button to add the +cell.
  • + +
+

+ +

Note that only one token can exist per cell per login session. +Which means that if you already have a token for cell X while +you are logged in as user A, then you can't have another +token for cell X. This means that only one identification can +obtain a token for a given cell.

+ +

Therefore, if you try to add a cell to an identification which is +already listed in the persistent cell list for another identification or +which currently exists under a different identification, you will receive a +warning such as the following: +

+ +

+Message displaying warning about a duplicate cell +

+ +

From here, you can decide to keep the cell listed for both +identifications (although only one of them can be active at any given +time), or remove the cell from all the other identifications and add it to +the current one. Also, you can cancel the add cell request. +

+ +

To modify a cell

+

+

    + +
  • Select the cell from the list of cells. The cell name, realm and +method in the text boxes will be updated to display the information +from the cell just selected.
  • + +
  • Make changes to the realm and method fields as appropriate
  • + +
  • Select Add / Update to update the +information in the cell list
  • + +
+

+ +

Note that if you don't click the Add / +Update, the cell entry will not be updated. +

+ +

To delete cells

+ +

+

    + +
  • Select the cells you want to delete from the cell list. You can +select multiple cells by clicking while holding the Ctrl key.
  • + +
  • Select the Delete button.
  • + +
+

+ +

Cell states

+ +

When you add cells or when you view the cell list, you will see +an icon next to each cell name. This icon represents the current state of +the cell's token. You can double-click on the cell to get details about the +state. +

+ +

+ +

+

+Token for cell foo.bar is a new token while +the token for net.mit.edu is in a problem +state. Double clicking the problem token will display a balloon +prompt indicating what the problem is. +

+ + +

Changing persistent cell lists for existing identifications

+ +

+The configuration panel for a persistent identification contains a panel for +specifying the AFS cells. This panel is identical to the panel used +by the new credentials dialog. You can easily modify the cell lists +as described above and click Apply and you +are done. +

+ +

The AFS configuration panel for an identification is shown below.

+ +

+ +

You can reach this configuration panel by opening the configuration +dialog (Options menu), and then clicking the +persistent identification you need to modify options for.

+ + + \ No newline at end of file diff --git a/src/WINNT/netidmgr_plugin/help/html/welcome.htm b/src/WINNT/netidmgr_plugin/help/html/welcome.htm new file mode 100644 index 0000000..592b13a --- /dev/null +++ b/src/WINNT/netidmgr_plugin/help/html/welcome.htm @@ -0,0 +1,61 @@ + + + Welcome to the OpenAFS plug-in for NetIDMgr + + + + + +

Welcome to the OpenAFS plug-in for NetIDMgr

+ + + +

+The OpenAFS plug-in extends the Network Identity Manager (or +NetIDMgr for short) to support AFS credential management. This plug-in +obtains AFS tokens for the cells associated with each identification +and provides the tools necessary to maintain them. +

+ +

+This version of the plug-in is provided by Secure Endpoints Inc. +Visit http://www.secure-endpoints.com +for updates. +

+ + + +

Using the OpenAFS plug-in

+ + + + +

External links

+ + + + \ No newline at end of file diff --git a/src/WINNT/netidmgr_plugin/help/popups_cfg.txt b/src/WINNT/netidmgr_plugin/help/popups_cfg.txt new file mode 100644 index 0000000..4dc07f6 --- /dev/null +++ b/src/WINNT/netidmgr_plugin/help/popups_cfg.txt @@ -0,0 +1,30 @@ +.topic IDH_SVCSTATUS +Current status of the OpenAFS Client Service. + +.topic IDH_SVCSTOP +Stops the OpenAFS Client service. Access to resources in AFS will not +be available until the service is started again. All exising AFS +tokens will also be lost. + +.topic IDH_SVCSTART +Start the service. Once the service starts, it will not have any AFS +tokens for any of the users. Therefore new tokens will be +automatically be obtained once the service starts. + +.topic IDH_SVCVERSION +Version of the OpenAFS Client that is installed on this machine. The +version is derived from the executable that provides the OpenAFS +Client service on this machine. + +.topic IDH_SVCCOMPANY +The company that provided the OpenAFS client. + +.topic IDH_SVCCPL +Open the OpenAFS Client Configuration utility. + +.topic IDH_STARTAFSCREDS +Once the OpenAFS plugin for NetIDMgr is installed, it is no longer +necessary to start AFSCreds.exe everytime a user logs in. The allows +you to disable AFSCreds.exe. Note that this is a per machine setting +and requires Administrator privileges on the machine to have an +effect. diff --git a/src/WINNT/netidmgr_plugin/help/popups_newcred.txt b/src/WINNT/netidmgr_plugin/help/popups_newcred.txt new file mode 100644 index 0000000..09e4db3 --- /dev/null +++ b/src/WINNT/netidmgr_plugin/help/popups_newcred.txt @@ -0,0 +1,31 @@ +.topic IDH_OBTAIN +If checked, obtains AFS tokens for this identity as specified below. +Otherwise, doesn't obtain AFS tokens at all. + +.topic IDH_CELL +The name of the cell for which this token will be obtained for. + +.topic IDH_REALM +The Kerberos realm used to obtain the service ticket for the cell. +Select 'Automatic' to let the AFS plug-in determine the realm +automatically. + +.topic IDH_METHOD +The method used to construct the AFS token. Select 'Automatic' to let +the AFS plugin determine the method automatically. + +.topic IDH_ADD +Add or update the token in the token list. If the token already +exists in the list, then the information will be updated. Otherwise +it will be added. + +.topic IDH_DELETE +Delete the selected tokens from the token list. Tokens that are +currently selected in the tokens list will be deleted. If no tokens +are selected in the list, then the token corresponding to the cell +listed in the 'Cell' input box will be deleted. + +.topic IDH_TOKENLIST +The list of tokens that will be obtained for this identity. +Double-click on any of the tokens here to get details about the state +of each token. diff --git a/src/WINNT/netidmgr_plugin/help/toc.hhc b/src/WINNT/netidmgr_plugin/help/toc.hhc new file mode 100644 index 0000000..3e153fa --- /dev/null +++ b/src/WINNT/netidmgr_plugin/help/toc.hhc @@ -0,0 +1,56 @@ + + + + + + + + + + + +
    +
  • + + + +
      +
    • + + + +
    • + + + +
    +
  • + + +
      +
    • + + + + +
    • + + + + +
    • + + + +
    +
  • + + +
      +
    • + + + +
    +
+ diff --git a/src/WINNT/netidmgr_plugin/images/OpenAFS.ico b/src/WINNT/netidmgr_plugin/images/OpenAFS.ico new file mode 100644 index 0000000000000000000000000000000000000000..f8fe9ca2acc79cdcda49407d7422dbf46b25693e GIT binary patch literal 31942 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 PzrfCTbUn#Ut^EH0C?&k# literal 0 HcmV?d00001 diff --git a/src/WINNT/netidmgr_plugin/images/afstoken.ico b/src/WINNT/netidmgr_plugin/images/afstoken.ico new file mode 100644 index 0000000000000000000000000000000000000000..61534df746f888a01d34bf64d68d2cf8607a1989 GIT binary patch literal 1406 zcmb7^v1=4T6vn?`#N!BU4wuK^luolUfi0)1^?s!X|&g zRTk#eNn@%MfgqT40gLBv-sUv1bNAlNyl-dc`|X?Eljb-V7nR4G`_hQfGondPh&c^t zbI|v*@!Tw(Nc=RJ^c^ijO{Y^$zfX1f?UUx`=QS7%w6L(CrKKf}Mk6gRFKcCGMXRf; z+S=OE#>R%$*VnbSwx-G3(B@|7y^a+>TwjlW>iGOz?>>LQ$cREz!u!V)icTj-cXUU01V<0@AP-Mxqc!}Eg^Zg_p|qzl7z_r3!C*)v z7z_r3!C){L5)%f4!C){L42DF9!C){L3!x~3KRse;*ot1C`y*)B41@nSY zVbKChmVt|fL4l>fl4XITz)|2Ra1=PQRB#j+3Je8?R=NU5fuX?Qcm-{i5TE`e&+@kF zK!tZ@R+bv?3RA_jVo+hKcvmh~7O8kwymLF!8y)|Of5pFTQN_Pfn+ijPp;CvoZB!+n z9)+H^Z6h^s{2Vri!|}_lZ5-6V;qb=6gXMN}lf&Y$I1r9uz5=i~H#(CX!C~P?unv3T zuz0ISvb49M?E)GM0YktLFa!(%L%vQhI1B+pz~C^j48eRD0)~JgU|?wi9fp7*UDzptaCBOM+d>g41^r>CbnJ3G_G z#f7e}u5^8Ut;=7b4+jT)#}lgCzJb=UUS@6GmWI05H%B+jqx(nb#&?Bod{-d9BeeXs zkoma#dyI!n7>bc2m`8jH0_EeL_vrBK1bCbh*? z7UtDSV?ekfAq2c1DFTAyXXZS~e_)}zH@k1XZ{C~F&U?F@NK2Na*IN-ku9ifea9^3b z^8BI5qX!~S(R$c3tR-!(9KB#c(vaGJI z%Ercq)O9WWeqS~>H)U^cPj+^8WP5vCwzjsUd1%@eX;2qALwWS9C@9VHrK6XPk_l(YZ-MFX#omAQW`NH+(CRIdM%) zo|q^xDDmD#dM*iu1Ve%$!4OC=Bp4D535EniV8W1KNH8QA5)6S3LxLf}kYGqKBtwHE z!I7MUZv<39pRal}7bSe#nMPZ4uz@cy`914fR5v77dVNe(p z1}~k$p)e>6CB9f?UPAKBPy8saR|hI`XX2vNIDotfO3+?m|59FZHH z{F(fj{Jun){F&Nh7%~i*I{30tm3Vp-?p%EtsX-xMfvvz%$QMiN9Mqt|QJjMZi{)le zfu+DwfG8x4EdWblU?Hd=6j=CS%)^=#Sc=ypSbR75Dxkq&Fc=I5gTY`h#CC$Az+f;K z3!U@#aA2ByZ)VK5jB1_QvzUzp4oXap&9D89mBF}KI4JeGH|zrQaB2M2O^ zcqm6lM{;_4Dkmo=a(;d;7Z(>Y8ja-o`dTKFiQL`Y$@quK+v8*2ElipLS4P+;?AZsk1*18qkB5Lv}qOBYZvpslWco md;j=VweD(nt*S<^M7~jzg$-#htf3vo|Axkm@8x~_iS`TYe4=Xr literal 0 HcmV?d00001 diff --git a/src/WINNT/netidmgr_plugin/images/nc_new.ico b/src/WINNT/netidmgr_plugin/images/nc_new.ico new file mode 100644 index 0000000000000000000000000000000000000000..cb6c7504e0e2f11731bdc06d5e526609cbcaef9b GIT binary patch literal 1406 zcmYk6F-ROy5Qax$;5dZnUAW33xhmGLvb7h4!{REtM;dFJK#mmFfq?M3Es{WrG}bAu zN)e~lCW2rg$U*MZ1!0THH?uk7?al7a|K6Mb+j)B@vcj>xF7bK$Tx7uA8^Wbm#ENt{ z$4z@(-mfgvi3nerOxg=)Ze=>1%JlbC=D)tm>guX=IvrVCTa%5A4H*muvbnh_!{Jc2 zwzgz{e_wWYcV%a1N4B@OW%ABsZ_nhDoQus=exCo4^P3y_`0YDJiO57Dv`rIQLQ81L zHEXA2^o*X-GkQk%Wd*&U7m|Wr(7A}BE4rd9f}$I_k(-8d;+*hL6iO^gViNY85)283 z1Ve%$kYGqKBp4D535LLgA;FMfNH8QA0v(0~LxLf}kYGrL21kM;Ifq$v0EMd`g-`S$8IEWb92tfTLx#aym*L1TWEd1L;~Xu- zr#;A{z1|(D@Gi`XR^we@Dwq}w3QPs>!eF6D!Morc+Y#RA_!s;O{=P*8|3YsH3=z+qa&cTDlcC$!fQCJiR#V}q0Sd@jzBt=kI z_!87%PZSpQeguoZ4ZaI#Fc=I5gTY`h7!2_`!Jsf035sgcPu92T{GPL|j5MGg)QEK0eC4G5K6o{EjDd*M9?kpjw>u-AetPry;%VNWFBW%q z|7S&hfB*2%>-)5R&xh|3sCS4E(|sJFM-GyYdul_O4J*>ScG$Xd*ssX`<-@KtbyYVp zZpPxeZpKYhx63_m#-s5vXF@d|jYi|Ds-k>-Qo}xU eSM=fPJ@JBg#(y6DS;mC>2z2rA|H~)w0qrln>>oJ* literal 0 HcmV?d00001 diff --git a/src/WINNT/netidmgr_plugin/images/nc_notowned.ico b/src/WINNT/netidmgr_plugin/images/nc_notowned.ico new file mode 100644 index 0000000000000000000000000000000000000000..84bf9b64ba7c14091c499b161363bb81dcec66e9 GIT binary patch literal 2166 zcmeH}u}fS*5Ql%4TtE&9dM8#E=86#OZL|yaf{nG^k|woHii3c4ARugKkpxntu}*PS zinz5l5d;f^2)R=W;UJT5=6Q+#L5k$v&Ay%A&hC6~cHePQOAoZXJSV@tKaiespKDyb zcqBc3C_O`4#-3v>wQ0GCon9V(H-+9Hz8Va?hw-5^91gj=V)2!k_b@&#dGr7Jrz^l2 z_^X$nb3ez+SW7ds(P{3}erxIu{BUEOZ@>A6j-T`4x1mN~Kd9YqYi4Ffv$M0BpP$#_ z;-Z$8melX}wX(9Jt*tF>Y;0(KeO+s7YZ|;VZEl+0>PU83_2uY?j?T~Z=Hn+aBrIbE ztBsq`5?Vq_uJI0eNJh_eGI~bO=svBW7xaQt&@RrHEp(JMklH+;jl8krMqV)4X6 ziAjm~Hqz517!nK#h6F<(!H{4`FeDff41ozlf+4|>U`Q|oIt&Sh1Ve%$!H^6Mjs!10?kEKwFXG8`F>3`d3|N(D!TA;XYi z@X}>CG7K4p8oyYzDIs~94}O%_s{<9e3vp3uau=8iNec-IOoiNq!9wmr?n3U^j>wHp z{zCpje&3=({z7dE3@YEa2nVXJUd^2OFV2Q{d0ROjHqV!N4C zVX3fGASwyt2*6UASP7~K6&Ai2>#!#kmg@Bg7C#NX3urJH3 zceS^-r^CZT9UL6!`1n{SCnq{PJJZF*g~sEtZf|cjnwZ}0@ADoHsII>Qeq+7xd|h|l z(`Ba39BRANT6d zdM|sp*AYO>s(LYeb$w%#Rp;?x0 +#include +#include +#include +#ifdef DEBUG +#include +#endif +#include + +/**************************************/ +/* khm_krb5_error(): */ +/**************************************/ +int +khm_krb5_error(krb5_error_code rc, LPCSTR FailedFunctionName, + int FreeContextFlag, krb5_context * ctx, + krb5_ccache * cache) +{ +#ifdef NO_KRB5 + return 0; +#else + +#ifdef SHOW_MESSAGE_IN_AN_ANNOYING_WAY + char message[256]; + const char *errText; + int krb5Error = ((int)(rc & 255)); + + errText = perror_message(rc); + _snprintf(message, sizeof(message), + "%s\n(Kerberos error %ld)\n\n%s failed", + errText, + krb5Error, + FailedFunctionName); + + MessageBoxA(NULL, message, "Kerberos Five", MB_OK | MB_ICONERROR | + MB_TASKMODAL | + MB_SETFOREGROUND); +#endif + + if (FreeContextFlag == 1) + { + if (*ctx != NULL) + { + if (*cache != NULL) { + pkrb5_cc_close(*ctx, *cache); + *cache = NULL; + } + + pkrb5_free_context(*ctx); + *ctx = NULL; + } + } + + return rc; + +#endif //!NO_KRB5 +} + +int +khm_krb5_initialize(khm_handle ident, + krb5_context *ctx, + krb5_ccache *cache) +{ +#ifdef NO_KRB5 + return(0); +#else + + LPCSTR functionName; + int freeContextFlag; + krb5_error_code rc = 0; + krb5_flags flags = 0; + + if (pkrb5_init_context == NULL) + return 1; + + if (*ctx == 0 && (rc = (*pkrb5_init_context)(ctx))) { + functionName = "krb5_init_context()"; + freeContextFlag = 0; + goto on_error; + } + + if(*cache == 0) { + wchar_t wccname[MAX_PATH]; + khm_size cbwccname; + + if(ident != NULL) { + cbwccname = sizeof(wccname); + do { + char ccname[256]; + + if(KHM_FAILED(kcdb_identity_get_attrib(ident, L"Krb5CCName", + NULL, wccname, + &cbwccname))) { + cbwccname = sizeof(wccname); + if (KHM_FAILED + (khm_krb5_find_ccache_for_identity(ident, + ctx, + wccname, + &cbwccname))) { +#ifdef DEBUG_LIKE_A_MADMAN + assert(FALSE); +#endif + break; + } + } + + if(UnicodeStrToAnsi(ccname, sizeof(ccname), wccname) == 0) + break; + + if((*pkrb5_cc_resolve)(*ctx, ccname, cache)) { + functionName = "krb5_cc_resolve()"; + freeContextFlag = 1; + goto on_error; + } + } while(FALSE); + } + +#ifndef FAILOVER_TO_DEFAULT_CCACHE + rc = 1; +#endif + if (*cache == 0 +#ifdef FAILOVER_TO_DEFAULT_CCACHE + && (rc = (*pkrb5_cc_default)(*ctx, cache)) +#endif + ) { + functionName = "krb5_cc_default()"; + freeContextFlag = 1; + goto on_error; + } + } + +#ifdef KRB5_TC_NOTICKET + flags = KRB5_TC_NOTICKET; +#endif + + if ((rc = (*pkrb5_cc_set_flags)(*ctx, *cache, flags))) + { + if (rc != KRB5_FCC_NOFILE && rc != KRB5_CC_NOTFOUND) + khm_krb5_error(rc, "krb5_cc_set_flags()", 0, ctx, + cache); + else if ((rc == KRB5_FCC_NOFILE || rc == KRB5_CC_NOTFOUND) && *ctx != NULL) { + if (*cache != NULL) + (*pkrb5_cc_close)(*ctx, *cache); + } + return rc; + } + return 0; + +on_error: + return khm_krb5_error(rc, functionName, freeContextFlag, ctx, cache); +#endif //!NO_KRB5 +} + +#define TIMET_TOLERANCE (60*5) + +khm_int32 KHMAPI +khm_get_identity_expiration_time(krb5_context ctx, krb5_ccache cc, + khm_handle ident, + krb5_timestamp * pexpiration) +{ + krb5_principal principal = 0; + char * princ_name = NULL; + krb5_creds creds; + krb5_error_code code; + krb5_error_code cc_code; + krb5_cc_cursor cur; + krb5_timestamp now, expiration = 0; + + wchar_t w_ident_name[KCDB_IDENT_MAXCCH_NAME]; + char ident_name[KCDB_IDENT_MAXCCH_NAME]; + khm_size cb; + + khm_int32 rv = KHM_ERROR_NOT_FOUND; + + if (!ctx || !cc || !ident || !pexpiration) + return KHM_ERROR_GENERAL; + + code = pkrb5_cc_get_principal(ctx, cc, &principal); + + if ( code ) + return KHM_ERROR_INVALID_PARAM; + + cb = sizeof(w_ident_name); + kcdb_identity_get_name(ident, w_ident_name, &cb); + UnicodeStrToAnsi(ident_name, sizeof(ident_name), w_ident_name); + + code = pkrb5_unparse_name(ctx, principal, &princ_name); + + /* compare principal to ident. */ + + if ( code || !princ_name || + strcmp(princ_name, ident_name) ) { + if (princ_name) + pkrb5_free_unparsed_name(ctx, princ_name); + pkrb5_free_principal(ctx, principal); + return KHM_ERROR_UNKNOWN; + } + + pkrb5_free_unparsed_name(ctx, princ_name); + pkrb5_free_principal(ctx, principal); + + code = pkrb5_timeofday(ctx, &now); + + if (code) + return KHM_ERROR_UNKNOWN; + + cc_code = pkrb5_cc_start_seq_get(ctx, cc, &cur); + + while (!(cc_code = pkrb5_cc_next_cred(ctx, cc, &cur, &creds))) { + krb5_data * c0 = krb5_princ_name(ctx, creds.server); + krb5_data * c1 = krb5_princ_component(ctx, creds.server, 1); + krb5_data * r = krb5_princ_realm(ctx, creds.server); + + if ( c0 && c1 && r && c1->length == r->length && + !strncmp(c1->data,r->data,r->length) && + !strncmp("krbtgt",c0->data,c0->length) ) { + + /* we have a TGT, check for the expiration time. + * if it is valid and renewable, use the renew time + */ + + if (!(creds.ticket_flags & TKT_FLG_INVALID) && + creds.times.starttime < (now + TIMET_TOLERANCE) && + (creds.times.endtime + TIMET_TOLERANCE) > now) { + expiration = creds.times.endtime; + + if ((creds.ticket_flags & TKT_FLG_RENEWABLE) && + (creds.times.renew_till > creds.times.endtime)) { + expiration = creds.times.renew_till; + } + } + } + } + + if (cc_code == KRB5_CC_END) { + cc_code = pkrb5_cc_end_seq_get(ctx, cc, &cur); + rv = KHM_ERROR_SUCCESS; + *pexpiration = expiration; + } + + return rv; +} + +khm_int32 KHMAPI +khm_krb5_find_ccache_for_identity(khm_handle ident, krb5_context *pctx, + void * buffer, khm_size * pcbbuf) +{ + krb5_context ctx = 0; + krb5_ccache cache = 0; + krb5_error_code code; + apiCB * cc_ctx = 0; + struct _infoNC ** pNCi = NULL; + int i; + khm_int32 t; + wchar_t * ms = NULL; + khm_size cb; + krb5_timestamp expiration = 0; + krb5_timestamp best_match_expiration = 0; + char best_match_ccname[256] = ""; + khm_handle csp_params = NULL; + khm_handle csp_plugins = NULL; + + if (!buffer || !pcbbuf) + return KHM_ERROR_GENERAL; + + ctx = *pctx; + + if (!pcc_initialize || + !pcc_get_NC_info || + !pcc_free_NC_info || + !pcc_shutdown) + goto _skip_cc_iter; + + code = pcc_initialize(&cc_ctx, CC_API_VER_2, NULL, NULL); + if (code) + goto _exit; + + code = pcc_get_NC_info(cc_ctx, &pNCi); + + if (code) + goto _exit; + + for(i=0; pNCi[i]; i++) { + if (pNCi[i]->vers != CC_CRED_V5) + continue; + + code = (*pkrb5_cc_resolve)(ctx, pNCi[i]->name, &cache); + if (code) + continue; + + /* need a function to check the cache for the identity + * and determine if it has valid tickets. If it has + * the right identity and valid tickets, store the + * expiration time and the cache name. If it has the + * right identity but no valid tickets, store the ccache + * name and an expiration time of zero. if it does not + * have the right identity don't save the name. + * + * Keep searching to find the best cache available. + */ + + if (KHM_SUCCEEDED(khm_get_identity_expiration_time(ctx, cache, + ident, + &expiration))) { + if ( expiration > best_match_expiration ) { + best_match_expiration = expiration; + StringCbCopyA(best_match_ccname, + sizeof(best_match_ccname), + "API:"); + StringCbCatA(best_match_ccname, + sizeof(best_match_ccname), + pNCi[i]->name); + expiration = 0; + } + } + + if(ctx != NULL && cache != NULL) + (*pkrb5_cc_close)(ctx, cache); + cache = 0; + } + + _skip_cc_iter: + + if (KHM_SUCCEEDED(kmm_get_plugins_config(0, &csp_plugins))) { + khc_open_space(csp_plugins, L"Krb5Cred\\Parameters", 0, &csp_params); + khc_close_space(csp_plugins); + csp_plugins = NULL; + } + +#ifdef DEBUG + if (csp_params == NULL) { + assert(FALSE); + } +#endif + + if (csp_params && + KHM_SUCCEEDED(khc_read_int32(csp_params, L"MsLsaList", &t)) && t) { + code = (*pkrb5_cc_resolve)(ctx, "MSLSA:", &cache); + if (code == 0 && cache) { + if (KHM_SUCCEEDED(khm_get_identity_expiration_time(ctx, cache, + ident, + &expiration))) { + if ( expiration > best_match_expiration ) { + best_match_expiration = expiration; + StringCbCopyA(best_match_ccname, sizeof(best_match_ccname), + "MSLSA:"); + expiration = 0; + } + } + } + + if (ctx != NULL && cache != NULL) + (*pkrb5_cc_close)(ctx, cache); + + cache = 0; + } + + if (csp_params && + khc_read_multi_string(csp_params, L"FileCCList", NULL, &cb) + == KHM_ERROR_TOO_LONG && + cb > sizeof(wchar_t) * 2) { + + wchar_t * t; + char ccname[MAX_PATH + 6]; + + ms = PMALLOC(cb); + +#ifdef DEBUG + assert(ms); +#endif + + khc_read_multi_string(csp_params, L"FileCCList", ms, &cb); + for(t = ms; t && *t; t = multi_string_next(t)) { + StringCchPrintfA(ccname, ARRAYLENGTH(ccname), + "FILE:%S", t); + + code = (*pkrb5_cc_resolve)(ctx, ccname, &cache); + if (code) + continue; + + if (KHM_SUCCEEDED(khm_get_identity_expiration_time(ctx, cache, + ident, + &expiration))) { + if ( expiration > best_match_expiration ) { + best_match_expiration = expiration; + StringCbCopyA(best_match_ccname, + sizeof(best_match_ccname), + ccname); + expiration = 0; + } + } + + if (ctx != NULL && cache != NULL) + (*pkrb5_cc_close)(ctx, cache); + cache = 0; + } + + PFREE(ms); + } + _exit: + if (csp_params) + khc_close_space(csp_params); + + if (pNCi) + (*pcc_free_NC_info)(cc_ctx, &pNCi); + + if (cc_ctx) + (*pcc_shutdown)(&cc_ctx); + + if (best_match_ccname[0]) { + + if (*pcbbuf = AnsiStrToUnicode((wchar_t *)buffer, + *pcbbuf, + best_match_ccname)) { + + *pcbbuf = (*pcbbuf + 1) * sizeof(wchar_t); + + return KHM_ERROR_SUCCESS; + } + + } + + return KHM_ERROR_GENERAL; +} diff --git a/src/WINNT/netidmgr_plugin/krb5common.h b/src/WINNT/netidmgr_plugin/krb5common.h new file mode 100644 index 0000000..29cae71 --- /dev/null +++ b/src/WINNT/netidmgr_plugin/krb5common.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2005 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +/* Adapted from multiple Leash header files */ + +#ifndef __KHIMAIRA_KRB5COMMON_H +#define __KHIMAIRA_KRB5COMMON_H + +#include + +#ifndef NO_KRB5 +int khm_krb5_error(krb5_error_code rc, LPCSTR FailedFunctionName, + int FreeContextFlag, krb5_context *ctx, + krb5_ccache *cache); + +int +khm_krb5_get_error_string(krb5_error_code rc, + wchar_t * buffer, + khm_size cb_buffer); + +int khm_krb5_initialize(khm_handle ident, krb5_context *, krb5_ccache *); + +khm_int32 KHMAPI +khm_krb5_find_ccache_for_identity(khm_handle ident, krb5_context *pctx, + void * buffer, khm_size * pcbbuf); + +khm_int32 KHMAPI +khm_get_identity_expiration_time(krb5_context ctx, krb5_ccache cc, + khm_handle ident, + krb5_timestamp * pexpiration); +#endif /* NO_KRB5 */ + +#endif diff --git a/src/WINNT/netidmgr_plugin/lang/en_us/langres.rc b/src/WINNT/netidmgr_plugin/lang/en_us/langres.rc new file mode 100644 index 0000000..fb185b6 --- /dev/null +++ b/src/WINNT/netidmgr_plugin/lang/en_us/langres.rc @@ -0,0 +1,273 @@ +// Microsoft Visual C++ generated resource script. +// +#include "..\..\langres.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "..\\..\\langres.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_NC_AFS DIALOGEX 0, 0, 300, 166 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "AFS Token Options",IDC_STATIC,7,7,286,11,SS_SUNKEN | NOT WS_GROUP + CONTROL "Obtain &AFS tokens",IDC_NCAFS_OBTAIN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,19,74,12 + GROUPBOX "Token for",IDC_STATIC,7,32,286,54 + LTEXT "&Cell",IDC_STATIC,12,41,42,10 + COMBOBOX IDC_NCAFS_CELL,57,38,152,12,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "&Realm",IDC_STATIC,12,57,42,10 + COMBOBOX IDC_NCAFS_REALM,57,54,152,12,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "&Method",IDC_STATIC,12,73,42,10 + COMBOBOX IDC_NCAFS_METHOD,57,70,152,12,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "&Add/Update",IDC_NCAFS_ADD_TOKEN,213,38,77,13,BS_NOTIFY + PUSHBUTTON "&Delete",IDC_NCAFS_DELETE_TOKEN,213,70,77,13,BS_NOTIFY + CONTROL "",IDC_NCAFS_TOKENLIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_SORTASCENDING | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,88,286,71 +END + +IDD_CFG_IDS_TAB DIALOGEX 0, 0, 235, 151 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "Obtain AFS tokens for new identities",IDC_CFG_OBTAIN, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,133,10 +END + +IDD_CFG_ID_TAB DIALOGEX 0, 0, 235, 151 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "Obtain &AFS tokens",IDC_NCAFS_OBTAIN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,74,12 + GROUPBOX "Token for",IDC_STATIC,7,18,221,56 + LTEXT "&Cell",IDC_STATIC,13,28,42,10 + COMBOBOX IDC_NCAFS_CELL,57,25,109,12,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "&Realm",IDC_STATIC,13,44,42,10 + COMBOBOX IDC_NCAFS_REALM,57,41,109,12,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "&Method",IDC_STATIC,13,60,42,10 + COMBOBOX IDC_NCAFS_METHOD,57,57,109,12,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "&Add/Update",IDC_NCAFS_ADD_TOKEN,174,26,50,13,BS_NOTIFY + PUSHBUTTON "&Delete",IDC_NCAFS_DELETE_TOKEN,174,56,50,13,BS_NOTIFY + CONTROL "",IDC_NCAFS_TOKENLIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_SORTASCENDING | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,75,221,69 +END + +IDD_CFG_AFS DIALOGEX 0, 0, 255, 182 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "AFS Service",IDC_CFG_SVCGRP,7,7,241,86 + LTEXT "Service status",IDC_CFG_LBL_STATUS,14,18,46,8 + EDITTEXT IDC_CFG_STATUS,68,16,172,14,ES_AUTOHSCROLL | ES_READONLY + PUSHBUTTON "Stop service",IDC_CFG_STOP,68,34,50,14 + PUSHBUTTON "Start service",IDC_CFG_START,127,34,50,14 + CONTROL "",IDC_CFG_PROGRESS,"msctls_progress32",NOT WS_VISIBLE | WS_BORDER,184,38,56,8 + LTEXT "Version",IDC_CFG_LBL_VERSION,14,57,24,8 + EDITTEXT IDC_CFG_VERSION,68,54,172,14,ES_AUTOHSCROLL | ES_READONLY + LTEXT "Company",IDC_CFG_LBL_COMPANY,14,75,31,8 + EDITTEXT IDC_CFG_COMPANY,68,72,172,14,ES_AUTOHSCROLL | ES_READONLY + PUSHBUTTON "Open AFS Control Panel ...",IDC_CFG_CPL,128,161,112,14 + GROUPBOX "Startup options",IDC_CFG_STARTGRP,7,99,241,48 + CONTROL "Prevent AFSCreds.exe from starting automatically everytime you log in to Windows.",IDC_CFG_STARTAFSCREDS, + "Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,14,108,226,22 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_NC_AFS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 293 + TOPMARGIN, 7 + BOTTOMMARGIN, 159 + END + + IDD_CFG_IDS_TAB, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 228 + TOPMARGIN, 7 + BOTTOMMARGIN, 144 + END + + IDD_CFG_ID_TAB, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 228 + TOPMARGIN, 7 + BOTTOMMARGIN, 144 + END + + IDD_CFG_AFS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 248 + VERTGUIDE, 14 + VERTGUIDE, 68 + VERTGUIDE, 240 + TOPMARGIN, 7 + BOTTOMMARGIN, 175 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_AFSTOKEN ICON "..\\..\\images\\afstoken.ico" +IDI_AFSPLUGIN ICON "..\\..\\images\\OpenAFS.ico" +IDI_NC_NEW ICON "..\\..\\images\\nc_new.ico" +IDI_NC_EXIST ICON "..\\..\\images\\nc_exist.ico" +IDI_NC_NOTOWNED ICON "..\\..\\images\\nc_notowned.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_AFS_SHORT_DESC "AFS tokens" + IDS_AFS_LONG_DESC "AFS tokens" + IDS_AFS_NAME "AFS" + IDS_NCAFS_COL_CELL "Cell" + IDS_NCAFS_COL_REALM "Realm" + IDS_NCAFS_COL_METHOD "Method" + IDS_NC_METHOD_AUTO "(Automatic)" + IDS_NC_METHOD_KRB5 "Kerberos 5" + IDS_NC_METHOD_KRB4 "Kerberos 4" + IDS_NC_METHOD_KRB524 "Krb524" + IDS_NC_REALM_AUTO "(Automatic)" +END + +STRINGTABLE +BEGIN + IDS_NC_TT_NO_CELL "You have not specified an AFS cell to authenticate to." + IDS_NC_TT_CANT_ADD "Can't add a new token" + IDS_NC_TT_MALFORMED_CELL + "The cell name you specified contains invalid characters." + IDS_NC_TT_NO_REALM "You have not specified a Kerberos realm to use to obtain tokesn for the cell." + IDS_NC_AUTO "(Auto)" + IDS_NC_TT_MALFORMED_REALM + "The realm name you entered contains invalid characters." + IDS_NC_TT_CANT_UPDATE "Can't update token" + IDS_AFS_CREDTEXT_DIS "

AFS: AFS is disabled. (click here to enable)

" + IDS_AFS_CREDTEXT_0 "

AFS: No tokens selected

" + IDS_AFS_CREDTEXT_1 "

AFS: Token for cell %s

" + IDS_AFS_CREDTEXT_N "

AFS: Tokens for cells %s

" + IDS_ATTR_CELL_SHORT_DESC "Cell" + IDS_ATTR_REALM_SHORT_DESC "Realm" + IDS_ATTR_METHOD_SHORT_DESC "Method" + IDS_ATTR_CLIENT_SHORT_DESC "Client Principal" + IDS_ATTR_CLIENT_PRINC_SHORT_DESC "Client Principal" +END + +STRINGTABLE +BEGIN + IDS_ATTR_SERVER_PRINC_SHORT_DESC "Server Principal" + IDS_DEF_LOCATION "AFS Cache Manager" + IDS_PLUGIN_DESC "AFS Credentials Provider" + IDS_CFG_IDS_TAB "AFS" + IDS_CFG_ID_TAB "AFS" + IDS_CFG_MAIN_LONG "AFS Configuration" + IDS_CFG_MAIN_SHORT "AFS" + IDS_CFG_SVCSTATUS "Status is unknown,Stopped,Starting ...,Stopping ...,Running,Continuing ...,Pausing ...,Paused,Status is unknown" + IDS_NC_METHODL_AUTO "(Automatically determine method)" + IDS_NC_METHODL_KRB5 "Kerberos 5" + IDS_NC_METHODL_KRB4 "Kerberos 4" + IDS_NC_METHODL_KRB524 "Krb524" + IDS_NC_METHOD_INVALID "(Invalid method identifier)" + IDS_NC_TT_CONFLICT "Tokens for cell %s are already listed for identity %s.\nDo you want to keep the token for this cell for other identities?" + IDS_NC_TT_PROBLEM "Problem with new token" + IDS_NC_TT_CONFLICTM "Tokens for cell %s are already listed for identity %s.\nDo you want to keep the token for other identities as well as this one?" +END + +STRINGTABLE +BEGIN + IDS_NC_TT_DETAILS "Details about token" + IDS_NC_TT_CONFLICTD "This token already exists under a different identity." + IDS_NC_TT_EXPIRED "Currently existing token has expired." + IDS_NC_TT_EXISTS "This token already exists for this identity." + IDS_NC_TT_NEW "This is a new token for this identity." + IDS_CFG_CANTSTART "The AFS service could not be started.\n\n" + IDS_CFG_CANTSTOP "The AFS service could not be stopped.\n\n" + IDS_ERR_CELLCONFIG "Could not locate configuration information for cell %1!S!." + IDS_ERR_NOSERVICE "The AFS service is not running. If OpenAFS is installed, you can start the service using the AFS configuration panel." + IDS_ERR_CELLCONFIG_S "The error code returned was %2!d!." + IDS_ERR_GENERAL "Tokens could not be obtained for cell %1!S!." +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/WINNT/netidmgr_plugin/lang/en_us/license.rtf b/src/WINNT/netidmgr_plugin/lang/en_us/license.rtf new file mode 100644 index 0000000000000000000000000000000000000000..9202b4f2f4468d18c4d7cc2687fefbbd1782806f GIT binary patch literal 1850 zcmZ`)U2oeq6y0-y{)dacbQ`=t@>de%NlTnofMXlBvaWz0N=cMWR3cT9it7dW?>kqH zoup|2LnNj9dCs}?eQJFfrb0W_J@RH9_DB7+kYyS0vl4n827|$2UrIjRr=`)}XB9R< zTDq-rE-f4Tt)G_pMp!3(K#^6Vx_E#|n3mSmLccOeR4pCN)_?VJ1b&vY%d zw89%pH?H)uP@YC7!|)^=;TN1HVVV@fUUwl z?06lV4m-Qycr;GiD$VwI^aHvy&CaUz#?#Drl}i%3ps8GoilU-cS~+jkof2I8+c)sl zM$0q{9?$AofZs3wpnebx|H5NL*^X8+Z>^+>E*hh>cNFXVlp|+KzZD3|OyQNJTdjaRt-SEy2zF5|t;z+}Vuz65S_NxCu93Ma zRZbO})b5u7KA1acbuG0&A&NaACuc!zje+_yRVlyYG(zvFk=7Ya;NRG_(CRQ9_sV*(zB^@hs_a z<{Dz=YzW*s*+bT5*Q44Ls>I4P79Dw6>*WvN@ka$d3X`|o>b`80vG1z2jR8VzwN-v& zT2G~v48=xT31Zh)pu+-XSaYWe!0$K0cR+~@0Cu(PRiz(Q)ihFALiYlMqf`a5I6>!_ z8Nb=@xQUmJy4WkGqyJgkuL8TaVW}|WFZ$YJaV$`aS~7n>6(=DoWs(5}LMXO?8oY8G z6zDW<2G&>rD1^D>_rU)MH1p9(RX!>80O3J_J=$93z0^QYanK``P~3HEAvasIB2-B` z(>^i^c%I9@`_MX1mB?hpcJ?WwaC;|(CXNHcU~_lJT9E+qKFYBaZqvz$nZ%d6yA*&0 z)LeFWa3dYZ1`H{U8{+2}9#WqC;(IN=f_!Z-R1MGkqyC_WjHSlo=33>&)2zMh{Rf>M z=kzGXcK|@ScSrGJw2%jv0|bb%WLR*Au!eUph7F^(qSgF5`4lZDg#BVU|0lkhT+x>= z(F!l#zo%$+MLd-A+XVa1H2HV2oUB$fUs8Otn8q-M_cEF#@nqGbcy>9xy^3cadPlEm zd=n>863=Hn%r)}7a-;b*-AtC3A34h3@ib08cX_YlWX8F!ku0J`v`pg5+iA3<#qDx2 RUri3p;E$}kJMk~|+y8J3f}Q{X literal 0 HcmV?d00001 diff --git a/src/WINNT/netidmgr_plugin/lang/en_us/resource.h b/src/WINNT/netidmgr_plugin/lang/en_us/resource.h new file mode 100644 index 0000000..b714539 --- /dev/null +++ b/src/WINNT/netidmgr_plugin/lang/en_us/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by langres.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/WINNT/netidmgr_plugin/langres.h b/src/WINNT/netidmgr_plugin/langres.h new file mode 100644 index 0000000..f651825 --- /dev/null +++ b/src/WINNT/netidmgr_plugin/langres.h @@ -0,0 +1,101 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by C:\work\pismere\athena\auth\krb5\src\windows\identity\plugins\afs\lang\en_us\langres.rc +// +#define IDS_AFS_SHORT_DESC 101 +#define IDS_AFS_LONG_DESC 102 +#define IDI_ICON1 102 +#define IDD_NC_AFS 103 +#define IDS_AFS_NAME 103 +#define IDS_NCAFS_COL_CELL 104 +#define IDD_CFG_IDS_TAB 104 +#define IDS_NCAFS_COL_REALM 105 +#define IDD_CFG_ID_TAB 105 +#define IDS_NCAFS_COL_METHOD 106 +#define IDD_CFG_AFS 106 +#define IDS_NC_METHOD_AUTO 107 +#define IDI_AFSTOKEN 107 +#define IDS_NC_METHOD_KRB5 108 +#define IDI_AFSPLUGIN 108 +#define IDS_NC_METHOD_KRB4 109 +#define IDI_NC_NEW 109 +#define IDS_NC_METHOD_KRB524 110 +#define IDI_NC_EXIST 110 +#define IDS_NC_REALM_AUTO 111 +#define IDI_NC_NOTOWNED 111 +#define IDS_NC_TT_NO_CELL 112 +#define IDS_NC_TT_CANT_ADD 113 +#define IDS_NC_TT_MALFORMED_CELL 114 +#define IDS_NC_TT_NO_REALM 115 +#define IDS_NC_AUTO 116 +#define IDS_NC_TT_MALFORMED_REALM 117 +#define IDS_NC_TT_CANT_UPDATE 118 +#define IDS_AFS_CREDTEXT_DIS 119 +#define IDS_AFS_CREDTEXT_0 120 +#define IDS_AFS_CREDTEXT_1 121 +#define IDS_AFS_CREDTEXT_N 122 +#define IDS_ATTR_CELL_SHORT_DESC 123 +#define IDS_ATTR_REALM_SHORT_DESC 124 +#define IDS_ATTR_METHOD_SHORT_DESC 125 +#define IDS_ATTR_CLIENT_SHORT_DESC 126 +#define IDS_ATTR_CLIENT_PRINC_SHORT_DESC 127 +#define IDS_ATTR_SERVER_PRINC_SHORT_DESC 128 +#define IDS_DEF_LOCATION 129 +#define IDS_PLUGIN_DESC 130 +#define IDS_CFG_IDS_TAB 131 +#define IDS_CFG_ID_TAB 132 +#define IDS_CFG_MAIN_LONG 133 +#define IDS_CFG_MAIN_SHORT 134 +#define IDS_CFG_SVCSTATUS 135 +#define IDS_NC_METHODL_AUTO 136 +#define IDS_NC_METHODL_KRB5 137 +#define IDS_NC_METHODL_KRB4 138 +#define IDS_NC_METHODL_KRB524 139 +#define IDS_NC_METHOD_INVALID 140 +#define IDS_NC_TT_CONFLICT 141 +#define IDS_NC_TT_PROBLEM 142 +#define IDS_NC_TT_CONFLICTM 143 +#define IDS_NC_TT_DETAILS 144 +#define IDS_NC_TT_CONFLICTD 145 +#define IDS_NC_TT_EXPIRED 146 +#define IDS_NC_TT_EXISTS 147 +#define IDS_NC_TT_NEW 148 +#define IDS_CFG_CANTSTART 149 +#define IDS_CFG_CANTSTOP 150 +#define IDS_ERR_CELLCONFIG 151 +#define IDS_ERR_NOSERVICE 152 +#define IDS_ERR_CELLCONFIG_S 153 +#define IDS_ERR_GENERAL 154 +#define IDC_NCAFS_OBTAIN 1001 +#define IDC_NCAFS_TOKENLIST 1002 +#define IDC_NCAFS_CELL 1004 +#define IDC_NCAFS_REALM 1005 +#define IDC_NCAFS_METHOD 1006 +#define IDC_NCAFS_ADD_TOKEN 1007 +#define IDC_NCAFS_DELETE_TOKEN 1008 +#define IDC_CFG_SVCGRP 1009 +#define IDC_CFG_LBL_STATUS 1010 +#define IDC_CFG_STATUS 1011 +#define IDC_CFG_LBL_VERSION 1012 +#define IDC_CFG_VERSION 1013 +#define IDC_CFG_STOP 1014 +#define IDC_CFG_START 1015 +#define IDC_CFG_CPL 1016 +#define IDC_CFG_OBTAIN 1017 +#define IDC_CFG_PROGRESS 1019 +#define IDC_CFG_LBL_COMPANY 1020 +#define IDC_CFG_COMPANY 1021 +#define IDC_CFG_STARTGRP 1022 +#define IDC_CHECK1 1023 +#define IDC_CFG_STARTAFSCREDS 1023 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 112 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1024 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/WINNT/netidmgr_plugin/main.c b/src/WINNT/netidmgr_plugin/main.c new file mode 100644 index 0000000..e3569e5 --- /dev/null +++ b/src/WINNT/netidmgr_plugin/main.c @@ -0,0 +1,148 @@ +/* Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +/* Disable the 'macro redefinition' warning which is getting + triggerred by a redefinition of the ENCRYPT and DECRYPT macros. */ +#pragma warning (push) +#pragma warning (disable: 4005) + +#include +#include +#include +#ifdef DEBUG +#include +#endif + +#pragma warning (pop) + +kmm_module h_khModule; /* KMM's handle to this module */ +HINSTANCE hInstance; +HMODULE hResModule; /* HMODULE to the resource library */ + +khm_handle csp_plugins = NULL; +khm_handle csp_afscred = NULL; +khm_handle csp_params = NULL; + +kmm_module_locale locales[] = { + LOCALE_DEF(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US), L"afscred_en_us.dll", KMM_MLOC_FLAG_DEFAULT) +}; +int n_locales = ARRAYLENGTH(locales); + +/* These two probably should not do anything */ +void init_afs() { +} + +void exit_afs() { +} + +/* called by the NetIDMgr module manager */ +KHMEXP khm_int32 KHMAPI init_module(kmm_module h_module) { + khm_int32 rv = KHM_ERROR_SUCCESS; + kmm_plugin_reg pi; + wchar_t buf[256]; + + h_khModule = h_module; + + rv = kmm_set_locale_info(h_module, locales, n_locales); + if(KHM_SUCCEEDED(rv)) { + hResModule = kmm_get_resource_hmodule(h_module); + } else { + goto _exit; + } + + ZeroMemory(&pi,sizeof(pi)); + + pi.msg_proc = afs_plugin_cb; + pi.name = AFS_PLUGIN_NAME; + pi.type = KHM_PITYPE_CRED; + pi.icon = LoadImage(hResModule, MAKEINTRESOURCE(IDI_AFSPLUGIN), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE); + pi.dependencies = AFS_PLUGIN_DEPS; + pi.description = buf; + + LoadString(hResModule, IDS_PLUGIN_DESC, + buf, ARRAYLENGTH(buf)); + + kmm_provide_plugin(h_module, &pi); + + if(KHM_FAILED(rv = init_imports())) + goto _exit; + + rv = kmm_get_plugins_config(0, &csp_plugins); + if(KHM_FAILED(rv)) goto _exit; + + rv = khc_load_schema(csp_plugins, schema_afsconfig); + if(KHM_FAILED(rv)) goto _exit; + + rv = khc_open_space(csp_plugins, CSNAME_AFSCRED, 0, &csp_afscred); + if(KHM_FAILED(rv)) goto _exit; + + rv = khc_open_space(csp_afscred, CSNAME_PARAMS, 0, &csp_params); + if(KHM_FAILED(rv)) goto _exit; + +_exit: + return rv; +} + +/* called by the NetIDMgr module manager */ +KHMEXP khm_int32 KHMAPI exit_module(kmm_module h_module) { + exit_imports(); + + if(csp_params) { + khc_close_space(csp_params); + csp_params = NULL; + } + if(csp_afscred) { + khc_close_space(csp_afscred); + csp_afscred = NULL; + } + if(csp_plugins) { + khc_unload_schema(csp_plugins, schema_afsconfig); + khc_close_space(csp_plugins); + csp_plugins = NULL; + } + + return KHM_ERROR_SUCCESS; /* the return code is ignored */ +} + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpvReserved) +{ + switch(fdwReason) { + case DLL_PROCESS_ATTACH: + hInstance = hinstDLL; + init_afs(); + break; + case DLL_PROCESS_DETACH: + exit_afs(); + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + break; + } + + return TRUE; +} diff --git a/src/WINNT/netidmgr_plugin/params.h b/src/WINNT/netidmgr_plugin/params.h new file mode 100644 index 0000000..f0a5609 --- /dev/null +++ b/src/WINNT/netidmgr_plugin/params.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KRBAFSCRED_PARAMS_H +#define __KHIMAIRA_KRBAFSCRED_PARAMS_H + + +#endif \ No newline at end of file diff --git a/src/WINNT/netidmgr_plugin/version.rc b/src/WINNT/netidmgr_plugin/version.rc new file mode 100644 index 0000000..cc43459 --- /dev/null +++ b/src/WINNT/netidmgr_plugin/version.rc @@ -0,0 +1,87 @@ +/* Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +/* $Id$ */ + +#include +#include + +#ifndef LANGVER + +#define STR_FILEDESC "OpenAFS Plugin for NetIDMgr" +#define STR_INTNAME "afscred" +#define STR_ORIGNAME "afscred.dll" + +#else + +#ifdef LANG_en_us + +#define STR_FILEDESC "English(US) language resources for OpenAFS plugin" +#define STR_INTNAME "afscred_en_us" +#define STR_ORIGNAME "afscred_en_us.dll" + +#else + +#error Unknown langugae + +#endif + +#endif + +1 VERSIONINFO + FILEVERSION AFSPLUGIN_VERSION_LST + PRODUCTVERSION KH_VERSION_LIST + FILEFLAGSMASK KH_VER_FILEFLAGMASK + FILEFLAGS KH_VER_FILEFLAGS + FILEOS KH_VER_FILEOS + FILETYPE KH_VER_FILETYPEDLL + FILESUBTYPE 0 + { + + BLOCK "StringFileInfo" + { + BLOCK "040904b0" + { + VALUE "CompanyName", "Secure Endpoints Inc." + VALUE "FileDescription", STR_FILEDESC + VALUE "FileVersion", AFSPLUGIN_VERSION_STR + VALUE "InternalName", STR_INTNAME + VALUE "LegalCopyright", "(C) 2005 Secure Endpoints Inc." + VALUE "OriginalFilename", STR_ORIGNAME + VALUE "ProductName", "OpenAFS Plugin for NetIDMgr" + VALUE "ProductVersion", KH_VERSION_STRING +#ifndef LANGVER + VALUE NIMV_MODULE, "OpenAFS" + VALUE NIMV_PLUGINS, "AfsCred" + VALUE NIMV_APIVER, KH_VERSION_STRINGAPI + VALUE NIMV_SUPPORT, "http://www.secure-endpoints.com" +#endif + } + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x409, 1200 + } + } diff --git a/src/config/NTMakefile b/src/config/NTMakefile index 2c1879a..7bb7d6e 100644 --- a/src/config/NTMakefile +++ b/src/config/NTMakefile @@ -374,6 +374,9 @@ idirs: doclink ! IF (!EXIST($(OJT)\WINNT\license\lang)) $(MKDIR) $(OJT)\WINNT\license\lang ! ENDIF +! IF (!EXIST($(OJT)\WINNT\netidmgr_plugin)) + $(MKDIR) $(OJT)\WINNT\netidmgr_plugin +! ENDIF ! IF (!EXIST($(OJT)\WINNT\pthread)) $(MKDIR) $(OJT)\WINNT\pthread ! ENDIF -- 1.9.4