NET_API_STATUS NetUserAdd( PCWSTR pwszHostname, DWORD dwLevel, PVOID pBuffer, PDWORD pdwParmErr ) { const DWORD dwUserAccess = USER_ACCESS_GET_NAME_ETC | USER_ACCESS_SET_LOC_COM | USER_ACCESS_GET_LOCALE | USER_ACCESS_GET_LOGONINFO | USER_ACCESS_GET_ATTRIBUTES | USER_ACCESS_GET_GROUPS | USER_ACCESS_GET_GROUP_MEMBERSHIP | USER_ACCESS_CHANGE_GROUP_MEMBERSHIP | USER_ACCESS_SET_ATTRIBUTES | USER_ACCESS_SET_PASSWORD; const DWORD dwDomainAccess = DOMAIN_ACCESS_CREATE_USER | DOMAIN_ACCESS_LOOKUP_INFO_1; NTSTATUS status = STATUS_SUCCESS; WINERROR err = ERROR_SUCCESS; PNET_CONN pConn = NULL; SAMR_BINDING hSamrBinding = NULL; DOMAIN_HANDLE hDomain = NULL; ACCOUNT_HANDLE hUser = NULL; DWORD dwSamrInfoLevel = 0; DWORD dwSamrPasswordInfoLevel = 0; DWORD dwParmErr = 0; UserInfo *pSamrUserInfo = NULL; UserInfo *pSamrPasswordUserInfo = NULL; DWORD dwSize = 0; DWORD dwSpaceLeft = 0; PIO_CREDS pCreds = NULL; PWSTR pwszUsername = NULL; DWORD dwRid = 0; BOOL bPasswordSet = FALSE; NET_VALIDATION_LEVEL eValidation = NET_VALIDATION_USER_ADD; BAIL_ON_INVALID_PTR(pBuffer, err); if (!(dwLevel == 1 || dwLevel == 2 || dwLevel == 3 || dwLevel == 4)) { err = ERROR_INVALID_LEVEL; BAIL_ON_WIN_ERROR(err); } status = LwIoGetActiveCreds(NULL, &pCreds); BAIL_ON_NT_STATUS(status); err = NetAllocateSamrUserInfo(NULL, &dwSamrInfoLevel, NULL, dwLevel, pBuffer, pConn, &dwSize, eValidation, &dwParmErr); BAIL_ON_WIN_ERROR(err); dwSpaceLeft = dwSize; dwSize = 0; if (dwSpaceLeft) { status = NetAllocateMemory((void**)&pSamrUserInfo, dwSpaceLeft); BAIL_ON_NT_STATUS(status); } err = NetAllocateSamrUserInfo(&pSamrUserInfo->info21, &dwSamrInfoLevel, &dwSpaceLeft, dwLevel, pBuffer, pConn, &dwSize, eValidation, &dwParmErr); BAIL_ON_WIN_ERROR(err); status = NetConnectSamr(&pConn, pwszHostname, dwDomainAccess, 0, pCreds); BAIL_ON_NT_STATUS(status); hSamrBinding = pConn->Rpc.Samr.hBinding; hDomain = pConn->Rpc.Samr.hDomain; err = LwAllocateWc16StringFromUnicodeString( &pwszUsername, (PUNICODE_STRING)&pSamrUserInfo->info21.account_name); BAIL_ON_WIN_ERROR(err); status = SamrCreateUser(hSamrBinding, hDomain, pwszUsername, dwUserAccess, &hUser, &dwRid); if (status == STATUS_USER_EXISTS) { err = NERR_UserExists; } else if (status == STATUS_ALIAS_EXISTS) { err = NERR_GroupExists; } BAIL_ON_NT_STATUS(status); /* * Check if there's password to be set (if it's NULL * the function returns ERROR_INVALID_PASSWORD) */ dwSamrPasswordInfoLevel = 26; dwSize = 0; err = NetAllocateSamrUserInfo(NULL, &dwSamrPasswordInfoLevel, NULL, dwLevel, pBuffer, pConn, &dwSize, eValidation, &dwParmErr); if (err == ERROR_SUCCESS) { dwSpaceLeft = dwSize; dwSize = 0; if (dwSpaceLeft) { status = NetAllocateMemory((void**)&pSamrPasswordUserInfo, dwSpaceLeft); BAIL_ON_NT_STATUS(status); } err = NetAllocateSamrUserInfo(&pSamrPasswordUserInfo->info26, &dwSamrPasswordInfoLevel, &dwSpaceLeft, dwLevel, pBuffer, pConn, &dwSize, eValidation, &dwParmErr); BAIL_ON_WIN_ERROR(err); status = SamrSetUserInfo(hSamrBinding, hUser, dwSamrPasswordInfoLevel, pSamrPasswordUserInfo); BAIL_ON_NT_STATUS(status); bPasswordSet = TRUE; } else if (err == ERROR_INVALID_PASSWORD) { /* This error only means we're not going to try set the password */ err = ERROR_SUCCESS; } else { BAIL_ON_WIN_ERROR(err); } /* * Prevent from trying to rename (to the same name) the account * that has just been created. Created samr user info buffer * contains whatever is passed from net user info buffer. */ if (dwSamrInfoLevel == 21 && (pSamrUserInfo->info21.fields_present & SAMR_FIELD_ACCOUNT_NAME)) { pSamrUserInfo->info21.fields_present ^= SAMR_FIELD_ACCOUNT_NAME; } /* * Disable the account only if there was no password */ if (!bPasswordSet && dwSamrInfoLevel == 21) { pSamrUserInfo->info21.account_flags |= ACB_DISABLED; } status = SamrSetUserInfo(hSamrBinding, hUser, dwSamrInfoLevel, pSamrUserInfo); BAIL_ON_NT_STATUS(status); status = SamrClose(hSamrBinding, hUser); BAIL_ON_NT_STATUS(status); cleanup: NetDisconnectSamr(&pConn); if (pdwParmErr) { *pdwParmErr = dwParmErr; } if (pSamrUserInfo) { NetFreeMemory(pSamrUserInfo); } if (pSamrPasswordUserInfo) { NetFreeMemory(pSamrPasswordUserInfo); } LW_SAFE_FREE_MEMORY(pwszUsername); if (pCreds) { LwIoDeleteCreds(pCreds); } if (err == ERROR_SUCCESS && status != STATUS_SUCCESS) { err = NtStatusToWin32Error(status); } return err; error: goto cleanup; }
NET_API_STATUS NetUserSetInfo( PCWSTR pwszHostname, PCWSTR pwszUsername, DWORD dwLevel, PVOID pBuffer, PDWORD pdwParmErr ) { /* This is necessary to be able to set account password. Otherwise we get access denied. Don't ask... */ const DWORD dwDomainAccess = DOMAIN_ACCESS_LOOKUP_INFO_1; const DWORD dwUserAccess = USER_ACCESS_GET_NAME_ETC | USER_ACCESS_GET_LOCALE | USER_ACCESS_GET_LOGONINFO | USER_ACCESS_GET_ATTRIBUTES | USER_ACCESS_GET_GROUPS | USER_ACCESS_GET_GROUP_MEMBERSHIP | USER_ACCESS_SET_LOC_COM | USER_ACCESS_SET_ATTRIBUTES | USER_ACCESS_CHANGE_PASSWORD | USER_ACCESS_SET_PASSWORD; NTSTATUS status = STATUS_SUCCESS; WINERROR err = ERROR_SUCCESS; PNET_CONN pConn = NULL; SAMR_BINDING hSamrBinding = NULL; ACCOUNT_HANDLE hUser = NULL; DWORD dwUserRid = 0; DWORD dwSamrInfoLevel = 0; DWORD dwSamrPasswordInfoLevel = 0; DWORD dwParmErr = 0; UserInfo *pSamrUserInfo = NULL; UserInfo *pSamrPasswordUserInfo = NULL; DWORD dwSize = 0; DWORD dwSpaceLeft = 0; PIO_CREDS pCreds = NULL; NET_VALIDATION_LEVEL eValidation = NET_VALIDATION_USER_SET; if (!(dwLevel == 0 || dwLevel == 1 || dwLevel == 2 || dwLevel == 3 || dwLevel == 4 || dwLevel == 1003 || dwLevel == 1007 || dwLevel == 1008 || dwLevel == 1011)) { err = ERROR_INVALID_LEVEL; BAIL_ON_WIN_ERROR(err); } BAIL_ON_INVALID_PTR(pwszUsername, err); BAIL_ON_INVALID_PTR(pBuffer, err); status = LwIoGetActiveCreds(NULL, &pCreds); BAIL_ON_NT_STATUS(status); err = NetAllocateSamrUserInfo(NULL, &dwSamrInfoLevel, NULL, dwLevel, pBuffer, pConn, &dwSize, eValidation, &dwParmErr); BAIL_ON_WIN_ERROR(err); dwSpaceLeft = dwSize; dwSize = 0; if (dwSpaceLeft) { status = NetAllocateMemory(OUT_PPVOID(&pSamrUserInfo), dwSpaceLeft); BAIL_ON_NT_STATUS(status); err = NetAllocateSamrUserInfo(&pSamrUserInfo->info21, &dwSamrInfoLevel, &dwSpaceLeft, dwLevel, pBuffer, pConn, &dwSize, eValidation, &dwParmErr); BAIL_ON_WIN_ERROR(err); } status = NetConnectSamr(&pConn, pwszHostname, dwDomainAccess, 0, pCreds); BAIL_ON_NT_STATUS(status); hSamrBinding = pConn->Rpc.Samr.hBinding; status = NetOpenUser(pConn, pwszUsername, dwUserAccess, &hUser, &dwUserRid); BAIL_ON_NT_STATUS(status); /* * Check if there's password to be set (if it's NULL * the function returns ERROR_INVALID_PASSWORD) */ dwSamrPasswordInfoLevel = 26; dwSize = 0; err = NetAllocateSamrUserInfo(NULL, &dwSamrPasswordInfoLevel, NULL, dwLevel, pBuffer, pConn, &dwSize, eValidation, &dwParmErr); if (err == ERROR_SUCCESS) { dwSpaceLeft = dwSize; dwSize = 0; if (dwSpaceLeft) { status = NetAllocateMemory(OUT_PPVOID(&pSamrPasswordUserInfo), dwSpaceLeft); BAIL_ON_NT_STATUS(status); } err = NetAllocateSamrUserInfo(&pSamrPasswordUserInfo->info26, &dwSamrPasswordInfoLevel, &dwSpaceLeft, dwLevel, pBuffer, pConn, &dwSize, eValidation, &dwParmErr); BAIL_ON_WIN_ERROR(err); status = SamrSetUserInfo(hSamrBinding, hUser, dwSamrPasswordInfoLevel, pSamrPasswordUserInfo); BAIL_ON_NT_STATUS(status); } else if (err == ERROR_INVALID_LEVEL || (err == ERROR_INVALID_PASSWORD && dwLevel != 1003)) { /* This error only means we're not going to try set the password. Either it's set to NULL in infolevel where it's optional or called infolevel doesn't support setting password */ err = ERROR_SUCCESS; } else { BAIL_ON_WIN_ERROR(err); } if (dwSamrInfoLevel) { status = SamrSetUserInfo(hSamrBinding, hUser, dwSamrInfoLevel, pSamrUserInfo); BAIL_ON_NT_STATUS(status); } status = SamrClose(hSamrBinding, hUser); BAIL_ON_NT_STATUS(status); cleanup: NetDisconnectSamr(&pConn); if (pdwParmErr) { *pdwParmErr = dwParmErr; } if (pSamrUserInfo) { NetFreeMemory(pSamrUserInfo); } if (pCreds) { LwIoDeleteCreds(pCreds); } if (err == ERROR_SUCCESS && status != STATUS_SUCCESS) { err = LwNtStatusToWin32Error(status); } return err; error: goto cleanup; }