526 lines
15 KiB
C
526 lines
15 KiB
C
|
#include "pch.h"
|
||
|
#include "sysmigp.h"
|
||
|
|
||
|
// Win95-specific (in the SDK)
|
||
|
#include <svrapi.h>
|
||
|
|
||
|
//
|
||
|
// Types for LAN Man structs
|
||
|
//
|
||
|
|
||
|
typedef struct access_info_2 ACCESS_INFO_2;
|
||
|
typedef struct access_list_2 ACCESS_LIST_2;
|
||
|
typedef struct share_info_50 SHARE_INFO_50;
|
||
|
|
||
|
//
|
||
|
// Flags that help determine when custom access is enabled
|
||
|
//
|
||
|
|
||
|
#define READ_ACCESS_FLAGS 0x0081
|
||
|
#define READ_ACCESS_MASK 0x7fff
|
||
|
#define FULL_ACCESS_FLAGS 0x00b7
|
||
|
#define FULL_ACCESS_MASK 0x7fff
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// Types for dynamically loading SVRAPI.DLL
|
||
|
//
|
||
|
|
||
|
typedef DWORD(NETSHAREENUM_PROTOTYPE)(
|
||
|
const char FAR * pszServer,
|
||
|
short sLevel,
|
||
|
char FAR * pbBuffer,
|
||
|
unsigned short cbBuffer,
|
||
|
unsigned short FAR * pcEntriesRead,
|
||
|
unsigned short FAR * pcTotalAvail
|
||
|
);
|
||
|
typedef NETSHAREENUM_PROTOTYPE * NETSHAREENUM_PROC;
|
||
|
|
||
|
typedef DWORD(NETACCESSENUM_PROTOTYPE)(
|
||
|
const char FAR * pszServer,
|
||
|
char FAR * pszBasePath,
|
||
|
short fsRecursive,
|
||
|
short sLevel,
|
||
|
char FAR * pbBuffer,
|
||
|
unsigned short cbBuffer,
|
||
|
unsigned short FAR * pcEntriesRead,
|
||
|
unsigned short FAR * pcTotalAvail
|
||
|
);
|
||
|
typedef NETACCESSENUM_PROTOTYPE * NETACCESSENUM_PROC;
|
||
|
|
||
|
HANDLE g_SvrApiDll;
|
||
|
NETSHAREENUM_PROC pNetShareEnum;
|
||
|
NETACCESSENUM_PROC pNetAccessEnum;
|
||
|
|
||
|
BOOL
|
||
|
pLoadSvrApiDll (
|
||
|
VOID
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Loads up svrapi.dll if it exsits and obtains the entry points for
|
||
|
NetShareEnum and NetAccessEnum.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
none
|
||
|
|
||
|
Return value:
|
||
|
|
||
|
TRUE if the DLL was loaded, or FALSE if the DLL could not be loaded
|
||
|
for any reason.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
BOOL b;
|
||
|
|
||
|
g_SvrApiDll = LoadLibrary (S_SVRAPI_DLL);
|
||
|
if (!g_SvrApiDll) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
pNetShareEnum = (NETSHAREENUM_PROC) GetProcAddress (g_SvrApiDll, S_ANSI_NETSHAREENUM);
|
||
|
pNetAccessEnum = (NETACCESSENUM_PROC) GetProcAddress (g_SvrApiDll, S_ANSI_NETACCESSENUM);
|
||
|
|
||
|
b = (pNetShareEnum != NULL) && (pNetAccessEnum != NULL);
|
||
|
|
||
|
if (!b) {
|
||
|
FreeLibrary (g_SvrApiDll);
|
||
|
g_SvrApiDll = NULL;
|
||
|
}
|
||
|
|
||
|
return b;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
pFreeSvrApiDll (
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
if (g_SvrApiDll) {
|
||
|
FreeLibrary (g_SvrApiDll);
|
||
|
g_SvrApiDll = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
pAddIncompatibilityAlert (
|
||
|
DWORD MessageId,
|
||
|
PCTSTR Share,
|
||
|
PCTSTR Path
|
||
|
)
|
||
|
{
|
||
|
PCTSTR Group;
|
||
|
PCTSTR Warning;
|
||
|
PCTSTR Args[2];
|
||
|
PCTSTR Object;
|
||
|
|
||
|
Args[0] = Share;
|
||
|
Args[1] = Path;
|
||
|
|
||
|
Warning = ParseMessageID (MessageId, Args);
|
||
|
Group = BuildMessageGroup (MSG_INSTALL_NOTES_ROOT, MSG_NET_SHARES_SUBGROUP, Share);
|
||
|
|
||
|
Object = JoinPaths (TEXT("*SHARES"), Share);
|
||
|
|
||
|
MYASSERT (Warning);
|
||
|
MYASSERT (Group);
|
||
|
MYASSERT (Object);
|
||
|
|
||
|
MsgMgr_ObjectMsg_Add (Object, Group, Warning);
|
||
|
|
||
|
FreeStringResource (Warning);
|
||
|
FreeText (Group);
|
||
|
|
||
|
FreePathString (Object);
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
pSaveShares (
|
||
|
VOID
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Enumerates all shares on the machine and saves them to MemDb.
|
||
|
|
||
|
In password-access mode, the shares are preserved but the passwords
|
||
|
are not. An incompatibility message is generated if a password is
|
||
|
lost.
|
||
|
|
||
|
In user-level-access mode, the shares are preserved and the name of
|
||
|
each user who has permission to the share is written to memdb. If
|
||
|
the user has custom access permission flags, an incompatibility
|
||
|
message is generated, and the user gets the least restrictive
|
||
|
security that matches the custom access flags.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
none
|
||
|
|
||
|
Return value:
|
||
|
|
||
|
TRUE if the shares were enumerated successfully, or FALSE if a failure
|
||
|
occurs from a Net API.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
CHAR Buf[16384]; // static because NetShareEnum is unreliable
|
||
|
SHARE_INFO_50 *psi;
|
||
|
DWORD rc = ERROR_SUCCESS;
|
||
|
WORD wEntriesRead;
|
||
|
WORD wEntriesAvailable;
|
||
|
WORD Flags;
|
||
|
TCHAR MemDbKey[MEMDB_MAX];
|
||
|
BOOL LostCustomAccess = FALSE;
|
||
|
MBCHAR ch;
|
||
|
PCSTR ntPath;
|
||
|
BOOL skip;
|
||
|
|
||
|
if (!pLoadSvrApiDll()) {
|
||
|
DEBUGMSG ((DBG_WARNING, "SvrApi.Dll was not loaded. Net shares are not preserved."));
|
||
|
return ERROR_SUCCESS;
|
||
|
}
|
||
|
|
||
|
__try {
|
||
|
rc = pNetShareEnum (NULL,
|
||
|
50,
|
||
|
Buf,
|
||
|
sizeof (Buf),
|
||
|
&wEntriesRead,
|
||
|
&wEntriesAvailable
|
||
|
);
|
||
|
|
||
|
if (rc != ERROR_SUCCESS) {
|
||
|
if (rc == NERR_ServerNotStarted) {
|
||
|
|
||
|
rc = ERROR_SUCCESS;
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
LOG ((LOG_ERROR, "Failure while enumerating network shares."));
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
if (wEntriesAvailable != 0 && wEntriesRead != wEntriesAvailable) {
|
||
|
|
||
|
LOG ((
|
||
|
LOG_ERROR,
|
||
|
"Could not read all available shares! Available: %d, Read: %d",
|
||
|
wEntriesAvailable,
|
||
|
wEntriesRead
|
||
|
));
|
||
|
}
|
||
|
|
||
|
while (wEntriesRead) {
|
||
|
wEntriesRead--;
|
||
|
psi = (SHARE_INFO_50 *) Buf;
|
||
|
psi = &psi[wEntriesRead];
|
||
|
|
||
|
DEBUGMSG ((DBG_NAUSEA, "Processing share %s (%s)", psi->shi50_netname, psi->shi50_path));
|
||
|
|
||
|
// Require share to be a user-defined, persistent disk share
|
||
|
if ((psi->shi50_flags & SHI50F_SYSTEM) ||
|
||
|
!(psi->shi50_flags & SHI50F_PERSIST) ||
|
||
|
(psi->shi50_type != STYPE_DISKTREE &&
|
||
|
psi->shi50_type != STYPE_PRINTQ)
|
||
|
) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Verify folder will not be in %windir% on NT
|
||
|
//
|
||
|
|
||
|
ntPath = GetPathStringOnNt (psi->shi50_path);
|
||
|
if (!ntPath) {
|
||
|
MYASSERT (FALSE);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
skip = FALSE;
|
||
|
|
||
|
if (StringIPrefix (ntPath, g_WinDir)) {
|
||
|
ch = _mbsnextc (ntPath + g_WinDirWackChars - 1);
|
||
|
if (ch == 0 || ch == '\\') {
|
||
|
|
||
|
DEBUGMSG ((DBG_VERBOSE, "Skipping share %s because it is in %%windir%%", psi->shi50_netname));
|
||
|
skip = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FreePathString (ntPath);
|
||
|
if (skip) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Process passwords
|
||
|
//
|
||
|
|
||
|
if (!(psi->shi50_flags & SHI50F_ACLS)) {
|
||
|
//
|
||
|
// Skip if passwords are specified
|
||
|
//
|
||
|
|
||
|
if (psi->shi50_rw_password[0] && psi->shi50_ro_password[0]) {
|
||
|
LOG ((LOG_WARNING, "Skipping share %s because it is guarded by share-level passwords", psi->shi50_netname));
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (psi->shi50_rw_password[0] &&
|
||
|
(psi->shi50_flags & SHI50F_ACCESSMASK) == SHI50F_FULL
|
||
|
) {
|
||
|
LOG ((LOG_WARNING, "Skipping full access share %s because it is guarded by a password", psi->shi50_netname));
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (psi->shi50_ro_password[0] &&
|
||
|
(psi->shi50_flags & SHI50F_ACCESSMASK) == SHI50F_RDONLY
|
||
|
) {
|
||
|
LOG ((LOG_WARNING, "Skipping read-only share %s because it is guarded by a password", psi->shi50_netname));
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Mark directory to be preserved, so we don't delete it if we remove other
|
||
|
// things and make it empty.
|
||
|
//
|
||
|
|
||
|
MarkDirectoryAsPreserved (psi->shi50_path);
|
||
|
|
||
|
//
|
||
|
// Save the remark, path, type and access
|
||
|
//
|
||
|
|
||
|
MemDbSetValueEx (MEMDB_CATEGORY_NETSHARES, psi->shi50_netname,
|
||
|
MEMDB_FIELD_REMARK, psi->shi50_remark,0,NULL);
|
||
|
|
||
|
MemDbSetValueEx (MEMDB_CATEGORY_NETSHARES, psi->shi50_netname,
|
||
|
MEMDB_FIELD_PATH, psi->shi50_path,0,NULL);
|
||
|
|
||
|
MemDbSetValueEx (MEMDB_CATEGORY_NETSHARES, psi->shi50_netname,
|
||
|
MEMDB_FIELD_TYPE, NULL, psi->shi50_type, NULL);
|
||
|
|
||
|
if ((psi->shi50_flags & SHI50F_ACCESSMASK) == SHI50F_ACCESSMASK) {
|
||
|
CHAR AccessInfoBuf[16384];
|
||
|
WORD wItemsAvail, wItemsRead;
|
||
|
ACCESS_INFO_2 *pai;
|
||
|
ACCESS_LIST_2 *pal;
|
||
|
|
||
|
//
|
||
|
// Obtain entire access list and write it to memdb
|
||
|
//
|
||
|
|
||
|
rc = pNetAccessEnum (NULL,
|
||
|
psi->shi50_path,
|
||
|
0,
|
||
|
2,
|
||
|
AccessInfoBuf,
|
||
|
sizeof (AccessInfoBuf),
|
||
|
&wItemsRead,
|
||
|
&wItemsAvail
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// If this call fails with not loaded, we have password-level
|
||
|
// security (so we don't need to enumerate the ACLs).
|
||
|
//
|
||
|
|
||
|
if (rc != NERR_ACFNotLoaded) {
|
||
|
|
||
|
if (rc != ERROR_SUCCESS) {
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
LOG ((LOG_ERROR, "Failure while enumerating network access for %s, rc=%u", psi->shi50_path, rc));
|
||
|
pAddIncompatibilityAlert (
|
||
|
MSG_INVALID_ACL_LIST,
|
||
|
psi->shi50_netname,
|
||
|
psi->shi50_path
|
||
|
);
|
||
|
|
||
|
} else {
|
||
|
int i;
|
||
|
|
||
|
if (wItemsAvail != wItemsRead) {
|
||
|
LOG ((LOG_ERROR, "More access items are available than what can be listed."));
|
||
|
}
|
||
|
|
||
|
// An interesting characteristic!
|
||
|
DEBUGMSG_IF ((wItemsRead != 1, DBG_WHOOPS, "Warning: wItemsRead == %u", wItemsRead));
|
||
|
|
||
|
// Structure has one ACCESS_INFO_2 struct followed by one or more
|
||
|
// ACCESS_LIST_2 structs
|
||
|
pai = (ACCESS_INFO_2 *) AccessInfoBuf;
|
||
|
pal = (ACCESS_LIST_2 *) (&pai[1]);
|
||
|
|
||
|
for (i = 0 ; i < pai->acc2_count ; i++) {
|
||
|
//
|
||
|
// Add incompatibility messages for Custom access rights
|
||
|
//
|
||
|
|
||
|
DEBUGMSG ((DBG_NAUSEA, "Share %s, Access flags: %x",
|
||
|
psi->shi50_netname, pal->acl2_access));
|
||
|
|
||
|
Flags = pal->acl2_access & READ_ACCESS_FLAGS;
|
||
|
|
||
|
if (Flags && Flags != READ_ACCESS_FLAGS) {
|
||
|
LostCustomAccess = TRUE;
|
||
|
}
|
||
|
|
||
|
Flags = pal->acl2_access & FULL_ACCESS_FLAGS;
|
||
|
|
||
|
if (Flags && Flags != FULL_ACCESS_FLAGS) {
|
||
|
LostCustomAccess = TRUE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Write NetShares\<share>\ACL\<user/group> to memdb,
|
||
|
// using the 32-bit key value to hold the access flags.
|
||
|
//
|
||
|
|
||
|
wsprintf (MemDbKey, TEXT("%s\\%s\\%s\\%s"), MEMDB_CATEGORY_NETSHARES,
|
||
|
psi->shi50_netname, MEMDB_FIELD_ACCESS_LIST, pal->acl2_ugname);
|
||
|
|
||
|
MemDbSetValue (MemDbKey, pal->acl2_access);
|
||
|
|
||
|
//
|
||
|
// Write to KnownDomain\<user/group> unless the user or group
|
||
|
// is an asterisk.
|
||
|
//
|
||
|
|
||
|
if (StringCompare (pal->acl2_ugname, TEXT("*"))) {
|
||
|
MemDbSetValueEx (
|
||
|
MEMDB_CATEGORY_KNOWNDOMAIN,
|
||
|
pal->acl2_ugname,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
0,
|
||
|
NULL
|
||
|
);
|
||
|
}
|
||
|
|
||
|
pal++;
|
||
|
}
|
||
|
|
||
|
psi->shi50_flags |= SHI50F_ACLS;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Write share flags (SHI50F_*)
|
||
|
//
|
||
|
|
||
|
wsprintf (MemDbKey, TEXT("%s\\%s"), MEMDB_CATEGORY_NETSHARES, psi->shi50_netname);
|
||
|
if (!MemDbSetValue (MemDbKey, psi->shi50_flags)) {
|
||
|
LOG ((LOG_ERROR, "Failure while saving share information to database."));
|
||
|
}
|
||
|
|
||
|
if (!(psi->shi50_flags & SHI50F_ACLS)) {
|
||
|
//
|
||
|
// Write password-level permissions
|
||
|
//
|
||
|
|
||
|
if (psi->shi50_rw_password[0]) {
|
||
|
MemDbSetValueEx (MEMDB_CATEGORY_NETSHARES, psi->shi50_netname,
|
||
|
MEMDB_FIELD_RW_PASSWORD, psi->shi50_rw_password,
|
||
|
0,NULL);
|
||
|
}
|
||
|
|
||
|
if (psi->shi50_ro_password[0]) {
|
||
|
MemDbSetValueEx (MEMDB_CATEGORY_NETSHARES, psi->shi50_netname,
|
||
|
MEMDB_FIELD_RO_PASSWORD, psi->shi50_ro_password,
|
||
|
0,NULL);
|
||
|
}
|
||
|
|
||
|
if (psi->shi50_ro_password[0] || psi->shi50_rw_password[0]) {
|
||
|
pAddIncompatibilityAlert (
|
||
|
MSG_LOST_SHARE_PASSWORDS,
|
||
|
psi->shi50_netname,
|
||
|
psi->shi50_path
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (LostCustomAccess) {
|
||
|
LostCustomAccess = FALSE;
|
||
|
pAddIncompatibilityAlert (
|
||
|
MSG_LOST_ACCESS_FLAGS,
|
||
|
psi->shi50_netname,
|
||
|
psi->shi50_path
|
||
|
);
|
||
|
}
|
||
|
|
||
|
rc = ERROR_SUCCESS;
|
||
|
}
|
||
|
}
|
||
|
__finally {
|
||
|
pFreeSvrApiDll();
|
||
|
}
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
SaveShares (
|
||
|
IN DWORD Request
|
||
|
)
|
||
|
{
|
||
|
switch (Request) {
|
||
|
case REQUEST_QUERYTICKS:
|
||
|
return TICKS_SAVE_SHARES;
|
||
|
case REQUEST_RUN:
|
||
|
return pSaveShares ();
|
||
|
default:
|
||
|
DEBUGMSG ((DBG_ERROR, "Bad parameter in SaveShares"));
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
WkstaMig (
|
||
|
VOID
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine provides a single location to add additional routines
|
||
|
needed to perform workstation migration.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
none
|
||
|
|
||
|
Return value:
|
||
|
|
||
|
none - Error path is not yet implemented.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
if (pSaveShares() != ERROR_SUCCESS) {
|
||
|
DEBUGMSG ((DBG_WHOOPS, "SaveShares failed, error ignored."));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|