/*++

Copyright (c) 1992-1993  Microsoft Corporation

Module Name:

    Alias.c

Abstract:

    NT alias functions:
        PortUasAliasSetup() -- one-time init of static data
        PortUasAddUserToAliases()
        PortUasAliasCleanup()

Author:

    John Rogers (JohnRo) 24-Apr-1992

Revision History:

    24-Apr-1992 JohnRo
        Created.
    05-May-1992 JohnRo
        Fixed desired _access parm to SamConnect() and SamOpenAlias().
        Fixed misleading msg when SamOpenDomain() fails.
        Call NetpGetLocalDomainId() instead of NetpGetDomainId() so we can get
        the kind that SamOpenDomain() wants.
        Only _open aliases for this product type.
    09-Jun-1992 JohnRo
        RAID 10139: PortUAS should add to admin group/alias.
        Fixed PortUasAliasSetup's error handling.
    27-Jan-1993 JohnRo
        RAID 8683: PortUAS should set primary group from Mac parms.
        Use NetLib's get product type function.
    01-Jun-1993 JohnRo
        Made changes suggested by PC-LINT 5.0

--*/


// Uncomment this to enable comm ops alias:
//#define USE_COMM_OPS


#include <nt.h>         // NTSTATUS, NT_SUCCESS(), etc.
#include <ntrtl.h>      // (Needed with nt.h and windows.h)
#include <nturtl.h>     // (Needed with ntrtl.h and windows.h)
#include <windows.h>    // LocalFree(), etc.
#include <lmcons.h>

#include <lmaccess.h>   // UF_ equates, LPUSER_INFO_2, etc.
#include <lmerr.h>      // NO_ERROR, NERR_ equates.
#include <netdebug.h>   // DBGSTATIC, NetpAssert(), etc.
#include <netlib.h>     // NetpGetProductType(), NetpMemoryFree().
#include <netlibnt.h>   // NetpNtStatusToApiStatus().
#include <ntsam.h>      // SAM_HANDLE, SamConnect(), etc.
#include <secobj.h>     // NetpDomainIdToSid().
#include <portuasp.h>   // My prototypes, UNEXPECTED_MSG(), PortUasSam*, etc.

#include "nlstxt.h"     // Nls message ID codes.


//#define OUR_ALIAS_DESIRED_ACCESS (ALIAS_ADD_MEMBER | STANDARD_RIGHTS_REQUIRED)
#define OUR_ALIAS_DESIRED_ACCESS  ALIAS_ADD_MEMBER

//#define OUR_GROUP_DESIRED_ACCESS (GROUP_ADD_MEMBER | STANDARD_RIGHTS_REQUIRED)
#define OUR_GROUP_DESIRED_ACCESS  GROUP_ADD_MEMBER


//int      _wcsicmp(const wchar_t *string1, const wchar_t *string2);

//
// State data (misc):
//
DBGSTATIC BOOL PortUasTargetIsLanmanNt;

//
// State data (alias handles and group handles):
//
DBGSTATIC SAM_HANDLE PortUasSamAccountOpsAliasHandle = NULL;
DBGSTATIC SAM_HANDLE PortUasSamAdminsAliasHandle = NULL;
DBGSTATIC SAM_HANDLE PortUasSamAdminsGroupHandle = NULL;
#ifdef USE_COMM_OPS
DBGSTATIC SAM_HANDLE PortUasSamCommOpsAliasHandle = NULL;
#endif
DBGSTATIC SAM_HANDLE PortUasSamPowerUsersAliasHandle = NULL;
DBGSTATIC SAM_HANDLE PortUasSamPrintOpsAliasHandle = NULL;
DBGSTATIC SAM_HANDLE PortUasSamServerOpsAliasHandle = NULL;


NET_API_STATUS
PortUasAliasSetup(
    VOID
    )

/*++

Routine Description:

    PortUas ports account information in a LanMan 2.0 UAS database into
    the SAM database.

Arguments:

    UasPathName - supplies the path name to the UAS database file.

Return Value:

    NET_API_STATUS - NERR_Success if successful, or one of the following:

--*/

{
    NET_API_STATUS  ApiStatus;
    NTSTATUS        NtStatus;
    NT_PRODUCT_TYPE ProductType;

    //
    // Caller should have already set global domain IDs and SAM domain handles.
    //

    NetpAssert( PortUasAccountsDomainId != NULL );
    NetpAssert( PortUasBuiltinDomainId  != NULL );

    NetpAssert( PortUasSamAccountsDomainHandle != NULL );
    NetpAssert( PortUasSamBuiltinDomainHandle  != NULL );

    //
    // Decide on policy: should we combine operator accounts into the
    // "power user" alias (on WindowsNT) or leave them in seperate
    // aliases (on LanManNT)?
    //

    ApiStatus = NetpGetProductType(
            NULL,               // local (no server name)
            &ProductType );     // get result here.
    if (ApiStatus != NO_ERROR) {

        UNEXPECTED_MSG( "NetpGetProductType", ApiStatus );
        goto SetupError;
    }
    if (ProductType == NtProductLanManNt) {
        PortUasTargetIsLanmanNt = TRUE;
    } else {
        PortUasTargetIsLanmanNt = FALSE;
    }
    if (Verbose) {
        DEBUG_MSG( (PREFIX_PORTUAS "Product type is " FORMAT_DWORD
                ", IsLanmanNt is " FORMAT_DWORD ".\n", ProductType,
                (DWORD) PortUasTargetIsLanmanNt) );
    }

    //
    // Open the well-known aliases and/or groups for this product type.
    //
    if (PortUasTargetIsLanmanNt) {

        NtStatus = SamOpenAlias(
                PortUasSamBuiltinDomainHandle,  // aliases live in builtin dom.
                OUR_ALIAS_DESIRED_ACCESS,
                DOMAIN_ALIAS_RID_ACCOUNT_OPS,   // alias ID
                & PortUasSamAccountOpsAliasHandle );  // alias handle
        if ( !NT_SUCCESS( NtStatus ) ) {
            ApiStatus = NetpNtStatusToApiStatus( NtStatus );
            UNEXPECTED_MSG( "SamOpenAlias(account)", ApiStatus );
            goto SetupError;
        }
        NetpAssert( PortUasSamAccountOpsAliasHandle != NULL );

        NtStatus = SamOpenGroup(
                PortUasSamAccountsDomainHandle,   // groups live in accts domain
                OUR_GROUP_DESIRED_ACCESS,
                DOMAIN_GROUP_RID_ADMINS,          // alias ID
                & PortUasSamAdminsGroupHandle );  // alias handle
        if ( !NT_SUCCESS( NtStatus ) ) {
            ApiStatus = NetpNtStatusToApiStatus( NtStatus );
            UNEXPECTED_MSG( "SamOpenGroup(admins global group)", ApiStatus );
            goto SetupError;
        }
        NetpAssert( PortUasSamAdminsGroupHandle != NULL );

        NtStatus = SamOpenAlias(
                PortUasSamBuiltinDomainHandle,
                OUR_ALIAS_DESIRED_ACCESS,
                DOMAIN_ALIAS_RID_PRINT_OPS,   // alias ID
                & PortUasSamPrintOpsAliasHandle );  // alias handle
        if ( !NT_SUCCESS( NtStatus ) ) {
            ApiStatus = NetpNtStatusToApiStatus( NtStatus );
            UNEXPECTED_MSG( "SamOpenAlias(print)", ApiStatus );
            goto SetupError;

        }
        NetpAssert( PortUasSamPrintOpsAliasHandle != NULL );

        NtStatus = SamOpenAlias(
                PortUasSamBuiltinDomainHandle,
                OUR_ALIAS_DESIRED_ACCESS,
                DOMAIN_ALIAS_RID_SYSTEM_OPS,   // alias ID
                & PortUasSamServerOpsAliasHandle );  // alias handle
        if ( !NT_SUCCESS( NtStatus ) ) {
            ApiStatus = NetpNtStatusToApiStatus( NtStatus );
            UNEXPECTED_MSG( "SamOpenAlias(server)", ApiStatus );
            goto SetupError;
        }
        NetpAssert( PortUasSamServerOpsAliasHandle != NULL );

    } else {

        // Target must be Windows/NT (not Lanman/NT), which changes policy.

        NtStatus = SamOpenAlias(
                PortUasSamBuiltinDomainHandle,
                OUR_ALIAS_DESIRED_ACCESS,
                DOMAIN_ALIAS_RID_ADMINS ,   // alias ID
                & PortUasSamAdminsAliasHandle );  // alias handle
        if ( !NT_SUCCESS( NtStatus ) ) {
            ApiStatus = NetpNtStatusToApiStatus( NtStatus );
            UNEXPECTED_MSG( "SamOpenAlias(admins local group)", ApiStatus );
            goto SetupError;
        }
        NetpAssert( PortUasSamAdminsAliasHandle != NULL );

        NtStatus = SamOpenAlias(
                PortUasSamBuiltinDomainHandle,
                OUR_ALIAS_DESIRED_ACCESS,
                DOMAIN_ALIAS_RID_POWER_USERS,   // alias ID
                & PortUasSamPowerUsersAliasHandle );  // alias handle
        if ( !NT_SUCCESS( NtStatus ) ) {
            ApiStatus = NetpNtStatusToApiStatus( NtStatus );
            UNEXPECTED_MSG( "SamOpenAlias(power users)", ApiStatus );
            goto SetupError;
        }
        NetpAssert( PortUasSamPowerUsersAliasHandle != NULL );

    }

    return (NO_ERROR);

SetupError:

    //
    // Come here to process errors.  ApiStatus has already been set.
    //

    NetpAssert( ApiStatus != NO_ERROR );

    (VOID) PortUasAliasCleanup();

    return (ApiStatus);

} // PortUasAliasSetup


DBGSTATIC NET_API_STATUS
PortUasAddUserToAlias(
    IN PSID UserSid,
    IN SAM_HANDLE AliasHandle
    )
{
    NET_API_STATUS ApiStatus;
    NTSTATUS NtStatus;

    NetpAssert( UserSid != NULL );
    NetpAssert( AliasHandle != NULL );

    NtStatus = SamAddMemberToAlias(
            AliasHandle,
            UserSid );

    if ( !NT_SUCCESS( NtStatus ) ) {
        ApiStatus = NetpNtStatusToApiStatus( NtStatus );

        // Allow repeated runs of PortUAS...
        if (ApiStatus == ERROR_MEMBER_IN_ALIAS) {
            return (NO_ERROR);
        }

        UNEXPECTED_MSG( "SamAddMemberToAlias", ApiStatus );
        return (ApiStatus);
    }

    return (NO_ERROR);
}


NET_API_STATUS
PortUasNameToRid(
    IN  LPCTSTR      Name,      // may be user or group name.
    IN  SID_NAME_USE ExpectedType,
    OUT PULONG       UserRid
    )
{
    NET_API_STATUS ApiStatus;
    PSID_NAME_USE  NameUse;
    NTSTATUS       NtStatus;
    UNICODE_STRING UnicodeName;
    PULONG         TempRid;

    NetpAssert( Name != NULL );
    NetpAssert( (*Name) != NULLC );
    NetpAssert( UserRid != NULL );

    NetpAssert( PortUasSamAccountsDomainHandle != NULL );

    //
    // Get a RID for this user name.
    //
    RtlInitUnicodeString(
            & UnicodeName,          // dest (NT struct)
            Name );             // src (null-terminated)

    NtStatus = SamLookupNamesInDomain(
            PortUasSamAccountsDomainHandle,  // users live in accounts domain
            (ULONG) 1,               // only want one name.
            &UnicodeName,            // name (in NT struct)
            &TempRid,                // alloc and set RIDs.
            &NameUse );              // alloc and set name types.

    if ( !NT_SUCCESS( NtStatus ) ) {
        ApiStatus = NetpNtStatusToApiStatus( NtStatus );
        UNEXPECTED_MSG( "SamLookupNamesInDomain(accounts)", ApiStatus );
        DEBUG_MSG( (PREFIX_PORTUAS "SAM lookup(accounts) of "
                FORMAT_LPWSTR " failed.\n", Name) );
        return (ApiStatus);
    }

    NetpAssert( NameUse != NULL );
    NetpAssert( TempRid != NULL );

    *UserRid = *TempRid;

    //
    // Did type user wanted match the actual one?
    //
    if (ExpectedType != *NameUse) {
        NetpAssert( FALSE );                   // BUGBUG: Is this possible?
        ApiStatus = ERROR_INVALID_PARAMETER;   // BUGBUG: A better error code?
        goto Cleanup;
    }

    ApiStatus = NO_ERROR;

Cleanup:
    //
    // Free memory which SAM allocated for us.
    //
    NtStatus = SamFreeMemory( NameUse );
    if ( !NT_SUCCESS( NtStatus ) ) {
        ApiStatus = NetpNtStatusToApiStatus( NtStatus );
        UNEXPECTED_MSG( "SamFreeMemory(use)", ApiStatus );
        return (ApiStatus);
    }

    NtStatus = SamFreeMemory( TempRid );
    if ( !NT_SUCCESS( NtStatus ) ) {
        ApiStatus = NetpNtStatusToApiStatus( NtStatus );
        UNEXPECTED_MSG( "SamFreeMemory(rid)", ApiStatus );
        return (ApiStatus);
    }

    return (ApiStatus);

} // PortUasNameToRid


DBGSTATIC NET_API_STATUS
PortUasUserNameToRidAndSid(
    IN  LPCWSTR UserName,
    OUT PULONG  UserRid,
    OUT PSID *  UserSid
    )
{
    NET_API_STATUS ApiStatus;

    NetpAssert( UserName != NULL );
    NetpAssert( (*UserName) != NULLC );
    NetpAssert( UserRid != NULL );
    NetpAssert( UserSid != NULL );

    NetpAssert( PortUasSamAccountsDomainHandle != NULL );

    //
    // Get a RID for this user name.
    //
    ApiStatus = PortUasNameToRid(
            (LPCTSTR) UserName,
            SidTypeUser,            // expected type
            UserRid );              // set RID for caller.
    if (ApiStatus != NO_ERROR) {
        UNEXPECTED_MSG( "PortUasNameToRid", ApiStatus );
        return (ApiStatus);
    }

    //
    // Convert the RID to a SID.
    //

    ApiStatus = NetpDomainIdToSid(
            PortUasAccountsDomainId,  // user IDs live in accounts domain
            *UserRid,
            UserSid );
    if (ApiStatus != NO_ERROR) {
        UNEXPECTED_MSG( "NetpDomainIdToSid", ApiStatus );
        return (ApiStatus);
    }
    NetpAssert( (*UserSid) != NULL );

    return (NO_ERROR);

} // PortUasUserNameToRidAndSid


NET_API_STATUS
PortUasAddUserToAliases(
    IN LPCWSTR UserName,
    IN DWORD Priv,              // USER_PRIV_ values from lmaccess.h
    IN DWORD AuthFlags          // AF_ values from lmaccess.h
    )
{
    NET_API_STATUS ApiStatus;
    NTSTATUS NtStatus;
    ULONG UserRid;
    PSID UserSid;

    NetpAssert( UserName != NULL );
    NetpAssert( (*UserName) != NULLC );

    //
    // If it's an ordinary user (or guest), ignore it.
    //
    if ( (AuthFlags == 0) && (Priv != USER_PRIV_ADMIN) ) {
        return (NO_ERROR);
    }

    //
    // Convert UserName to UserSid.
    //
    ApiStatus = PortUasUserNameToRidAndSid(
            UserName,
            &UserRid,                  // set RID for this user.
            &UserSid );                // alloc and set ptr for SID.
    if (ApiStatus != NO_ERROR) {
        return (ApiStatus);
    }
    NetpAssert( UserSid != NULL );

    if (Priv == USER_PRIV_ADMIN) {
        //
        // Depending on product type, add to admins local group or admins
        // global group.
        //
        if (PortUasTargetIsLanmanNt) {
            //PROGRESS_MSG( ("Adding user " FORMAT_LPWSTR
            //       " to admins global group...\n", UserName) );
	    (void)NlsPutMsg(STDOUT, PUAS_ADDING_USER_TO_ADMINS_GLOBAL_GROUP, UserName );
            NtStatus = SamAddMemberToGroup(
                    PortUasSamAdminsGroupHandle,
                    UserRid,
                    SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT |
                        SE_GROUP_ENABLED );

            if ( !NT_SUCCESS( NtStatus ) ) {
                ApiStatus = NetpNtStatusToApiStatus( NtStatus );

                // Allow repeated runs of PortUAS...
                if (ApiStatus == NERR_UserInGroup) {
                    return (NO_ERROR);
                }

                UNEXPECTED_MSG( "SamAddMemberToGroup", ApiStatus );
                return (ApiStatus);
            }
        } else {
            //PROGRESS_MSG( ("Adding user " FORMAT_LPWSTR
            //       " to admins local group...\n", UserName) );
	    (void)NlsPutMsg(STDOUT, PUAS_ADDING_USER_TO_ADMINS_LOCAL_GROUP, UserName );
            ApiStatus = PortUasAddUserToAlias( UserSid,
                    PortUasSamAdminsAliasHandle );
            if (ApiStatus != NO_ERROR) {
                // Msg already logged.
                return (ApiStatus);
            }
        }
    }

    //
    // Add user (operator) to applicable alias(es).
    //
    if (PortUasTargetIsLanmanNt) {

        if (AuthFlags & AF_OP_ACCOUNTS) {
            //PROGRESS_MSG( ("Adding user " FORMAT_LPWSTR " to acct ops alias.\n",
            //       UserName) );
	    (void)NlsPutMsg(STDOUT, PUAS_ADDING_USER_TO_ACCT_OPS_ALIAS, UserName );
            ApiStatus = PortUasAddUserToAlias( UserSid,
                    PortUasSamAccountOpsAliasHandle );
            if (ApiStatus != NO_ERROR) {
                // Msg already logged.
                return (ApiStatus);
            }
        }

        if (AuthFlags & AF_OP_COMM) {

#ifdef USE_COMM_OPS
            //PROGRESS_MSG( ("Adding user " FORMAT_LPWSTR " to comm ops alias.\n",
            //       UserName) );
	    (void)NlsPutMsg(STDOUT, PUAS_ADDING_USER_TO_COMM_OPS_ALIAS, UserName );
            ApiStatus = PortUasAddUserToAlias( UserSid,
                    PortUasSamCommOpsAliasHandle );
            if (ApiStatus != NO_ERROR) {
                // Msg already logged.
                return (ApiStatus);
            }
#else
            //WARNING_MSG( ("Ignoring comm operator flag for " FORMAT_LPWSTR
            //       ".\n", UserName) );
	    (void)NlsPutMsg(STDOUT,PUAS_IGNORING_COMM_OPERATOR_FLAG, UserName);
#endif
        }

        if (AuthFlags & AF_OP_PRINT) {
            //PROGRESS_MSG( ("Adding user " FORMAT_LPWSTR " to print alias.\n",
            //       UserName) );
	    (void)NlsPutMsg(STDOUT, PUAS_ADDING_USER_TO_PRINT_ALIAS, UserName );
            ApiStatus = PortUasAddUserToAlias( UserSid,
                    PortUasSamPrintOpsAliasHandle );
            if (ApiStatus != NO_ERROR) {
                // Msg already logged.
                return (ApiStatus);
            }
        }

        if (AuthFlags & AF_OP_SERVER) {
            //PROGRESS_MSG( ("Adding user " FORMAT_LPWSTR " to srv ops alias.\n",
            //       UserName) );
	    (void)NlsPutMsg(STDOUT, PUAS_ADDING_USER_TO_SRV_OPS_ALIAS, UserName );
            ApiStatus = PortUasAddUserToAlias( UserSid,
                    PortUasSamServerOpsAliasHandle );
            if (ApiStatus != NO_ERROR) {
                // Msg already logged.
                return (ApiStatus);
            }
        }

    } else {

        ApiStatus = PortUasAddUserToAlias( UserSid,
                PortUasSamPowerUsersAliasHandle );
        if (ApiStatus != NO_ERROR) {
            // Msg already logged.
            return (ApiStatus);
        }
    }

    NetpMemoryFree( UserSid );   // Free mem alloc'ed by PortUasUserNameToSid().

    NetpAssert( ApiStatus == NO_ERROR );
    return (NO_ERROR);
}


NET_API_STATUS
PortUasAliasCleanup(
    VOID
    )
{

    CLOSE_SAM_HANDLE( PortUasSamAdminsAliasHandle );
    CLOSE_SAM_HANDLE( PortUasSamAdminsGroupHandle );
    CLOSE_SAM_HANDLE( PortUasSamAccountOpsAliasHandle );
#ifdef USE_COMM_OPS
    CLOSE_SAM_HANDLE( PortUasSamCommOpsAliasHandle );
#endif
    CLOSE_SAM_HANDLE( PortUasSamPowerUsersAliasHandle );
    CLOSE_SAM_HANDLE( PortUasSamPrintOpsAliasHandle );
    CLOSE_SAM_HANDLE( PortUasSamServerOpsAliasHandle );

    return (NO_ERROR);

} // PortUasAliasCleanup