Connect to MySQL database w/ C# as non-root user? - c#

Let me start by saying I'm completely new to databases, but have been reading through the MySQL tutorial they have.
Right now, I'm trying to make an app that allows unprivileged users (non-root) to connect and do some commands on a database through a C# GUI app. The users will login to the database using windows authentication.
Now, I was able to to make a quick GUI where the person running the program can connect to the database on a local host using "root" and get whatever content is in it.
My question is, how exactly do I allow users to connect w/ non-root privileges? The only things I've been able to find all deal w/ using the connection string w/ "root" as the user.
Edit: The database is already made. I won't personally be able to connect to it as a root user and give other users permissions.

You could use the grant syntax, as root to grant permissions to other users. E.g.:
GRANT ALL PRIVILEGES ON mydatabase.* TO pfinferno

Some important concepts too are the rows in the table shown here:
select user,host,password from mysql.user where user='pfinferno';
Important takeaways, users can have multiple hostnames or wildcard. Each have their own rights and passwords. Though the password from the above is hashed, at least you can quickly eyeball it so see if all the passwords match (such as root with 3 accounts).
The host column is populated by the following values, mostly:
Specimen A:
localhost
127.0.0.1
%
common names such md21.newyork.comcastbusiness.net and the like
The % value is the wildcard. It is for flexibility, but it can be highly dangerous when used with users like 'root'#'%'. Keeping root as the 1st two only in Specimen A is highly advised. Also, the first two are quite different in different tools. What I recommend is having a root user row for the first two, and keeping the passwords the same. There are pros and cons to this approach. But remember that when you can't connect that something else out there was relying on the connection, be it an agent, phpmyadmin, a tool, the my.conf settings, etc. You will be revisiting this a lot.
In the example given by Mureinik it grants privileges to the user with the wildcard host = %. This means it is banking on a user created as such. Note that it is typical to have user accounts setup that way. Though there is nothing restricting you to locking it down tighter.
When a user attempts to connect to the server, the user he is ultimately connected as can be resolved to a different user/host combo, as can be seen in the case where there is no localhost (host) user or common-name, but rather one with a host value of wildcard %. This can be seen with this query:
Specimen B:
select current_user(),user();
The latter is the user presented by the connection attempt, the former is the one that was resolved and actual. The implications can cause one to waste days in debugging, as can be seen in these forums. Some people can't connect for days, I am serious.
Specimen C:
create user 'pfinferno'#'localhost' identified by 'thePassword';
create user 'pfinferno'#'127.0.0.1' identified by 'thePassword';
create user 'pfinferno' identified by 'thePassword';
create user 'pfinferno'#'md21.newyork.comcastbusiness.net' identified by 'thePassword';
-- note #3 above is same as 'pfinferno'#'%'
Now granted, the host name may be wildcard for normal users, and host name may be localhost and 127.0.0.1 for root only. But in the attempt to lock down security, admins often create user accounts based on hostname coming in (such as Specimen C, line 4), and vary security with grants based on that. Managing it can be a bit overwhelming. So they often just say screw it, I will create the wildcard user. That may be fine for a new user, say Susie, but for Secimen C line 4, if that is who you are coming in, you can have grants on line 3 user that you won't pick up. So the resolving of what user your are actuallity (See Specimen B), goes from most specific hostname to fallback to more general, until it finds one such as wildcard.
Unfortunately, users don't connect with hostname specified per se, they just are what they are. They try to connect. So you don't say hey I want to be this thing #hostname, you just are what you are. You are 'pfinferno'#'md21.newyork.comcastbusiness.net', but may fallback to wildcard.
If users are dropped in Specimen C except for wildcard %, well you better have the grants that went with them that you expect, because you are now a new user.
Try to limit the use of wildcards on the grant statement to not do *.* as in the lazy approach, which would just grant rights to all databases and tables. In Mureinik's example, it was for all tables in one database. Not too shabby. Try to fine tune rights, such as granting SELECT privileges on tables or not at all, to users that don't need them. Be careful with WITH GRANT OPTION as seen on the net with cut and paste from it. If you use it, you just granted the user rights to grant other users rights.
SSH Tunnels
One reason you might not want to use Secimen A host = % wildcard (other than the obvious We are Risk Averse) is that
create user 'pfinferno'#'localhost' identified by 'thePassword';
is perfect for SSH Tunnels. You would be connecting through a cryptographically secure channel with PKI, and present yourself as if you are # localhost. This drastically reduces security exposure.
Why Can't I Connect?
Hopefully the below visual with little commentary can show why I named this section as I did.
drop user 'pfinferno'#'localhost';
drop user 'pfinferno'#'127.0.0.1';
drop user 'pfinferno'#'%';
drop user 'pfinferno'#'md21.newyork.comcastbusiness.net';
flush privileges; -- some say this is not necessary, I have found otherwise
create user 'pfinferno'#'localhost' identified by 'thePassword';
create user 'pfinferno'#'127.0.0.1' identified by 'thePassword';
create user 'pfinferno' identified by 'thePassword';
create user 'pfinferno'#'md21.newyork.comcastbusiness.net' identified by 'thePassword';
...
select user,host,password from mysql.user where user='pfinferno';
grant all on so_gibberish.* to 'pfinferno'#'%'; -- grant all rights on so_gibberish db
flush privileges; -- some say this is not necessary, I have found otherwise
Look at some grants.
show grants for 'pfinferno'#'localhost'; -- sandboxed. Can just log in and sit there
+-----------------------------------------------------------------------------------------+
| Grants for pfinferno#localhost |
+-----------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'pfinferno'#'localhost' IDENTIFIED BY PASSWORD '*74692AE70C53...' |
+-----------------------------------------------------------------------------------------+
show grants for 'pfinferno'#'127.0.0.1'; -- sandboxed. Can just log in and sit there
same as above
show grants for 'pfinferno'; -- wildcard % user, has all rights on so_gibberish;
+-----------------------------------------------------------------------------------------+
| Grants for pfinferno#% |
+-----------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'pfinferno'#'%' IDENTIFIED BY PASSWORD '*74692AE70C53...' |
| GRANT ALL PRIVILEGES ON `so_gibberish`.* TO 'pfinferno'#'%' |
+-----------------------------------------------------------------------------------------+
Note above that GRANT USAGE means at least you have the rights to log in and sit (sandboxed). But the wildcard %user also has all rights on so_gibberish db in its entirety.
Now I go to mysql prompt via mysql -u pfinferno -pthePassword
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
+--------------------+
mysql> select current_user(),user();
+---------------------+---------------------+
| current_user() | user() |
+---------------------+---------------------+
| pfinferno#localhost | pfinferno#localhost |
+---------------------+---------------------+
The attempted user (user()) was resolved to the same (current_user()). I was sandboxed, able to do basically nothing, except select now() til bored to death.
mysql> use so_gibberish;
ERROR 1044 (42000): Access denied for user 'pfinferno'#'localhost' to database 'so_gibberish'
quit mysql CLI as that user.
Now
drop user 'pfinferno'#'localhost';
drop user 'pfinferno'#'127.0.0.1';
drop user 'pfinferno'#'md21.newyork.comcastbusiness.net';
I just dropped three out of four of my pfinferno users
Go to mysql prompt via mysql -u pfinferno -pthePassword
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| so_gibberish |
+--------------------+
mysql> select current_user(),user();
+----------------+---------------------+
| current_user() | user() |
+----------------+---------------------+
| pfinferno#% | pfinferno#localhost |
+----------------+---------------------+
mysql> use so_gibberish;
Database changed
This shows that # the CLI (or any program) from any host, that the attempt to connect is first as presented by user and then as resolved to actual ( see the output from user() and current_user(), respectively).
So, perhaps oddly, as I dropped users, the EFFECTIVE RIGHTS increased as the user was resolved to a different one. This can be the subject of hours/days/weeks of debugging. Users can have no clue who they are really logged in as (resolved to), or why they can't, as each, by the way, can have a different password ! This is especially true with user root with several rows in mysql.user ... and considering that probably 50% of all mysql users connect with it, initially, as developers. Just a guestimate.

Well, first of all, you have to realize that this "root" (from MySQL) is different from the "root" user (from your computer).
When you log on MySQL database (as root firstly), you can create another user with least privileges. (Creating user: https://dev.mysql.com/doc/refman/5.1/en/create-user.html)
After that, you can give (grant) some privileges to this specific user. (Granting privileges: https://dev.mysql.com/doc/refman/5.1/en/grant.html)
You can, for instance, allow that some user will only select data from the tables. Another user can insert/update. And so on.
So, in your application, you can specify another user instead of root, as you can normally see.

Related

Office 365 EWS - GetUserAvailability ID is null

I am connecting to Exchange on 365 using EWS and using the GetUserAvailability method on the ExchangeService object to retrieve a collection of calendar items for a number of users (no more that 30).
My user that I am using to connect to EWS has impersonation rights and everything is working with the exception of one thing.
When I loop through the AttendeesAvailability collection, and then through each CalendarEvent within its CalendarEvents collection, I am able to see the Details object and access things like the Subject, Location, IsPrivate, etc...
The problem I have, is that the Details.StoreId is always null.
I have two 365 Exchange environments, our and a client's. When I try and access the StoreId on ours, it works absolutely fine and has the unique ID as expected. When I run the same come on the client's instance, it is null; but all the other properties are populated for the Details object.
This lead me to believe that it was a permissions issue for the service user within Exchange. So, I have slowly raised the permissions levels, hoping to hit the sweet spot, but nothing. The user is now an Exchange Admin (which I believe should have blanket access to anything and everything within the Exchange environment - including calendar items for each person's mailbox). Still not joy.
So, is it a configuration issue with the Exchange setup?
It is worth noting that all the other functionality that my application has, works perfect. It is just this ID that I am missing.
I am at a loss on this one. If it wasn't for the fact that I have shaved my head due to the lock down, I would be pulling my hair out right about now.
Any help or suggestions would be greatly appreciated.
Cheers
UPDATE 1
So, I have found that if I perform the same process on the service account that I am authenticating with, I get the StoreId back. This account was created after the migration from on-prem to O365 (which may mean nothing). I am in the process of getting access to another account that has been created since the migration to see if it works for that one as well. This will help me identify if the issue is likely to be with the migrated mailboxes only or with any mailbox apart from the account used to authenticate. I will update soon...
UPDATE 2
I have tested against a new account. I set the default permissions on the new account's calendar to Reviewer (even tried Owner and Anonymous just for a sanity check). When I authenticate using the service account, I don't get the StoreId. When I authenticate using the new test account, I do get the StoreId.
This leads me to believe that it is a permissions issue. It's now a case of finding out what that magic permissions is. It still eludes me as to why it is only the ID that I don't get back.
Still open to any suggestions.
UPDATE 3
It looks like it may be something different between build versions on EWS.
The version that works is reporting a server version of 15.20.3021.030.
The version that does not work is version 15.20.3045.019.
So, it's been a while, but Microsoft have now fixed the issue and the existing code is now working fine.
I can also confirm that this work when using OAuth as well.
The problem is probably related to not having sufficient rights from the account you use for the Exchange Service authentication to the target account.
Use the command below to give rights and try again.
Add-MailboxFolderPermission -Identity User#domain.com:\Calendar -User serviceceaccount#domain.com -AccessRights Reviewer
This is a very annoying issue, I have opened an incident with MS in the past, but got the disappointing answer that this won't change soon.
Below is the PS code to set rights to all users, rooms, resources, regardless of culture (language) and name of the calendar folder.
Param(
[parameter(position=0,Mandatory=$true)][string] $serviceaccount,
[parameter(position=1,Mandatory=$true)][string] $mailboxtype
)
$acl = "Reviewer"
if ($mailboxtype -eq "All") {
$mailboxes = Get-Mailbox | Where {$_.RecipientTypeDetails -eq "UserMailbox" -or $_.RecipientTypeDetails -eq "RoomMailbox" -or $_.RecipientTypeDetails -eq "EquipmentMailbox"} | Select -expand PrimarySmtpAddress | Select -expand Address | Where {$_ -NotLike "Administrator#*"}
}
elseif ($mailboxtype -eq "Users") {
$mailboxes = Get-Mailbox | Where {$_.RecipientTypeDetails -eq "UserMailbox"} | Select -expand PrimarySmtpAddress | Select -expand Address | Where {$_ -NotLike "Administrator#*"}
}
elseif ($mailboxtype -eq "Resources") {
$mailboxes = Get-Mailbox | Where {$_.RecipientTypeDetails -eq "RoomMailbox" -or $_.RecipientTypeDetails -eq "EquipmentMailbox"} | Select -expand PrimarySmtpAddress | Select -expand Address
}
else {
"Error specifying target mailbox type. Choose one of: 'All', 'Users' or 'Resources'.`n
'All' - UserMailbox, RoomMailbox, EquipmentMailbox.`n
'Users' - UserMailbox.`n
'Resources' - RoomMailbox, EquipmentMailbox."
}
foreach ($mbx in $mailboxes)
{
$calName = Get-MailboxFolderStatistics -Identity $mbx -FolderScope Calendar | Where-Object { $_.FolderType -eq 'Calendar'} | Select-Object Name, FolderId #-ErrorAction Stop
#echo "${mbx}:\$($calName.Name)"
Add-MailboxFolderPermission -Identity ${mbx}:\$($calName.Name) -User $serviceaccount -AccessRights $acl | Select-Object Identity, FolderName, User, AccessRights
}
Save as PS script, call with two params: serviceaccount & mailboxtype

NT AUTHORITY\Local Service is not listed in the Access Control List of a directory

I'm having this issue where I'm trying to check if NT\Authority Local Service has read\execute permissions on a directory (folder). The product that I work on REQUIRES that the folder the user is installing to has read\execute permissions set for Local Service.
The problem is that when I get the Access Control List (ACL) recursively (groups-within-groups), Local Service is not listed so I can't check if he has permissions to that folder or not.
By default, Local Service does not have read/execute permissions to user profiles (My Documents, Desktop, etc...) but I won't know if Local Service has access to other directories the user chooses to install to.
NOTE: Local Service DOES have access to Program Files, even though it is NOT listed in the ACL. Is it hidden somewhere else?
This is a short snippet on how I'm pulling the ACL:
GroupPrincipal groupPrincipal =
GroupPrincipal.FindByIdentity(principalContext, identityReferenceValue);
// GetMembers(true) is recursive (groups-within-groups)
foreach (var member in groupPrincipal.GetMembers(true)) {
if (member.SamAccountName.Equals("LOCAL SERVICE")) {
foundLocalService = true;
break;
}
}
Is there any other way I should be doing this? (Other than adding an access rule for Local Service on that directory)
Is Local Service just not listed in Directories ACL's?
Any help would be greatly appreciated.
It's notoriously difficult to calculate "effective permissions" for an account. But the simple answer to your question is that you will likely want to look for either on of:
The local Users group, sometimes shown as BUILTIN\Users or COMPUTERNAME\Users, or
Authenticated Users, sometimes shown as NT AUTHORITY\Authenticated Users.
Authenticated Users is one of the well-know SIDs. It is "a group that includes all users whose identities were authenticated when they logged on.". As long as you can prove who you are, you are included in Authenticated Users. The SID for this is always S-1-5-11 on every Windows computer.
However, it's not really considered a real group. To find it when adding permissions to a folder, you have to have "Built-in security principals" selected under "Select this object type":
The local Users group contains Authenticated Users by default. On my computer, I actually see both Users and Authenticated Users in the default permissions on the file system.
That's what you will most likely see, and that's likely all that matters.
But that's not the only way. You could see Everyone (S-1-1-0), which includes every user, authenticated or not.
Or, it could be a file or folder that has the LOCAL SERVICE account as the owner.
Or, there could be a local group that was created manually and LOCAL SERVICE was added to.
One way to get a more authoritative list of what you can look for is to run this under the LOCAL SERVICE account:
whoami /groups
That will tell you every group in the authentication token, which is every group that you are considered a member of for authentication purposes.
But you can't just open a command prompt as LOCAL SERVICE. So one way to do this is to open the Task Scheduler and create a task that runs under LOCAL SERVICE, with the action of:
Program: cmd
Arguments: /c "whoami /groups > C:\temp\localservice.txt"
Then run the task and, when it's done, look at C:\temp\localservice.txt. It will have a table of group names and their SIDs that you can look for.

Win32_GroupUser does not return nested groups from domain security groups

When I use Win32_GroupUser to request members of a local security group, I also get nested domain groups.
E.g. in PowerShell, this query on a member server:
Get-WmiObject Win32_GroupUser -Filter "GroupComponent='Win32_Group.Domain=`"myserver`",Name=`"LocalTestGroup02`"'" | select PartComponent
returns both user- and domain accounts:
PartComponent
-------------
\\myserver\root\cimv2:Win32_Group.Domain="FLIP",Name="TestGroup"
\\myserver\root\cimv2:Win32_UserAccount.Domain="FLIP",Name="test5"
\\myserver\root\cimv2:Win32_UserAccount.Domain="FLIP",Name="test4"
The same query, but this time on a domain controller and for membership of a domain security group:
Get-WmiObject Win32_GroupUser -Filter "GroupComponent='Win32_Group.Domain=`"FLIP`",Name=`"TestGroup2`"'" | select PartComponent
only returns the user accounts, despite that its members are exactly the same as the local group above.
PartComponent
------------
\\VW-DC01\root\cimv2:Win32_UserAccount.Domain="FLIP",Name="test4"
\\VW-DC01\root\cimv2:Win32_UserAccount.Domain="FLIP",Name="test5"
I see this behavior
when executed locally and from another server with the -ComputerName parameter
also with a C# application using System.Management
on Domain local, Global, and Universal security groups
Does anybody know why that is?
More important, can I change this behavior so I also get to see nested groups in domain groups?
I'm trying to enumerate membership of both local- and domain groups, including members in nested groups.
PS: I know I can also use the Get-ADGroupMember cmdlet, but that's not an option. I only have a WMI connection to the target servers, not LDAP or WinRM.
Thx,
You will only be able to see local groups (i.e. non-domain) on the computer itself, not from the domain controller.
As for seeing nested groups, apart from rolling your own recursive function, you can take a look at Quest Active Directory tools
function is Get-QADGroupMember

Sid of local group in machine

I try to find sid of the administrator group for example.
According to Microsoft the sid of that group is: S-1-5-21-machine-500
when the machine is identifier represents the three sub-authority values associated with a specific machine.
You can see it in the follow link:
https://msdn.microsoft.com/en-us/library/cc980032.aspx
I don't understand what the meaning of the <machine> and how I can get it using in c#.
Anybody know what is it or how I can get it?
Everything up to the 500 identifies the issuer of the SID, in this case the machine.
S-1-5 means "NT AUTHORITY", i.e. this SID is issued by is a Windows NT system. (The reason for this distinction is that SIDs are a subset of OIDs, an OSI standard for generating unique IDs, part of the DCE project and also used in LDAP).
S-1-5-21-X-X-X means "Issued by a domain", where X-X-X is a unique random number generated when the domain was created, or when the machine was installed.
This is also known as the "machine SID" or "domain SID" if it is for the domain. Specifically, the 21 identifies that the next three groups identify a domain, which will in turn issue more SIDs.
S-1-5-21-X-X-X-500 is the administrator account of the machine identified by S-1-5-21-X-X-X
The 500 is the RID or Relative ID. That's equivalent to the GID on Linux. 500 is the first local account or group to be created because RIDs are allocated beginning with 500. The next local account or group would get RID 501, and so on. 500 is generally the account Administrator, not a group though.
If you want to find information about security groups from C#, you should use the ADSI API, accessible from the System.DirectoryServices namespace. For local groups you need to use WinNT://MACHINENAME/ as your initial path.
https://msdn.microsoft.com/en-us/library/system.directoryservices.directoryentry.path(v=vs.110).aspx
Note that users are not listed as members of their primary group (usually Users). You have to look at the User's PrimaryGroup property for that.

How to get "primary" user of a remote computer

I need to know how to get the name and domain of the primary user of a computer, remotely. I define the primary user preferably as the user logged on most times, or longest time over a period. Alternatively, if this is impossible, as the user currently/last logged on.
Currently, i scan an Active Directoy for all computer objects in an OU. I then loop though them, and try to get the name of the user using WMI.
I look in Win32_ComputerSystem to see if UserName returns a value. If this is not the case, i look in Win32_LogonSession and get the username for all LogonTypes that equal 2 or 10. If this returns none, or multiple values, i discard the result and look in Win32_Process for all non-system processes and define the primary user as the user with most processes running.
There are several problems with my approach:
Win32_ComputerSystem - UserName is often null.
Win32_LogonSession often return multiple or no values. There can be only 1 primary user.
Looking in Win32_Process is kinda ridiculous, since this will only return me the user with most processes, most likely not the primary user.
If no user is currently logged on, looking in Win32_Process returns no value and none of the 3 steps might return a value.
My 3 approaches might get me the current user. Does anyone know of a way to get the primary user? Or at least a better way to get the current. Not necessarily using WMI.
Thanks
If you have administrator privileges on the remote computer and sharing access
you can use Computer Management and select to
connect to other computer and see what users and groups are on that computer.
Or you can use TS Remote Administration or Remote Desktop if the remote computer has that capability.
Use psexec.exe (www.sysinternals.com) to run commands on a remote pc:
psexec \\pc1 net user | find /i "Steve"
psexec \\pc1 c:\tool\psloggedon | find /i "Steve"
1) Find if John Black has an account on PC1.
2) Find if he is currently logged on.
Use psloggedon.exe that uis is another SysInternals tool.
Check this also: http://www.wisesoft.co.uk/scripts/vbscript_display_username_of_user_on_a_remote_computer.aspx
Also if you want to find the User Name of the currently logged user on a remote computer using the remote computer IP then Go to the command line an type nbtstat -A <IP of remote computer> This will return all of the NetBIOS names registered on the computer, one of which is the username.

Categories