/// <summary> /// Checks to see if a given share is shared or not. /// The share path returned by the share must match the given FullPath. /// </summary> /// <param name="serverName"></param> /// <param name="shareName"></param> /// <param name="fullPath"></param> /// <returns>SharedFolderState</returns> public static Share.State IsShared(string serverName, string shareName, string fullPath) { //by default we say that the folder is not shared Share.State sfs = Share.State.IsNotShared; //check the share IntPtr ptr = IntPtr.Zero; int errCode = NativeMethods.NetShareGetInfo(serverName, shareName, 502, out ptr); if ((NativeMethods.NET_API_STATUS)errCode == NativeMethods.NET_API_STATUS.NERR_Success) { //share exists at the unc path..check the returned unc path's path to the passed fullPath //Compare the path's - make sure they are the same NativeMethods.SHARE_INFO_502 shareInfo = new NativeMethods.SHARE_INFO_502(); shareInfo = (NativeMethods.SHARE_INFO_502)Marshal.PtrToStructure(ptr, typeof(NativeMethods.SHARE_INFO_502)); if (shareInfo.shi502_path.ToLower() == fullPath.ToLower()) { //paths are the same - mark it as shared sfs = Share.State.IsShared; } else { //paths are different - mark it as a duplicate share name! sfs = Share.State.IsDuplicateShare; } //clear out the unmanaged ptr - if errCode = true, Win32's NetShareGetInfo() fills in the ptr with a structure compatible with NativeMethods.SHARE_INFO_502 Marshal.FreeHGlobal(ptr); } return sfs; }
/// <summary> /// Creates a share for the specified path via win32 API. Shares are created with Everyone Full Control - use NTFS to secure it! /// </summary> /// <param name="server">the server name, pass null to use the local machine</param> /// <param name="path">the path to the folder to share</param> /// <param name="shareName">what the share should be called</param> /// <param name="shareDesc">a description for the share</param> /// <param name="type">the type of share (normal or special admin hidden share). use ShareType enum</param> /// <param name="SharePermissions">a list of ShareACL structures which define the share permissions for this share</param> /// <returns>Share.ShareStatus</returns> public static Share.ShareStatus CreateWin32Share(string server, string path, string shareName, string shareDesc, ShareType type, List<ACL> SharePermissions) { //this will be filled by the SetEntriesInAcl Win32 call below with our ACL IntPtr AclPtr = IntPtr.Zero; //create the explicit access rule...this holds all our ShareACL's NativeMethods.EXPLICIT_ACCESS[] explicitAccessRule = new NativeMethods.EXPLICIT_ACCESS[SharePermissions.Count]; //loop through our share permission list, and create our ACL for the share for (int i = 0; i < explicitAccessRule.Length; i++) { //System.Security.Principal.NTAccount ADObject = new System.Security.Principal.NTAccount(SharePermissions[i].GetFullAccountName()); //this is how we get a sid from an account...left here in case we need it in the future //SecurityIdentifier ADObjectSID = (SecurityIdentifier)ADObject.Translate(typeof(SecurityIdentifier)); //create the trustee for this user NativeMethods.TRUSTEE account = new NativeMethods.TRUSTEE(); account.MultipleTrusteeOperation = NativeMethods.MULTIPLE_TRUSTEE_OPERATION.NO_MULTIPLE_TRUSTEE; account.pMultipleTrustee = 0; account.TrusteeForm = NativeMethods.TRUSTEE_FORM.TRUSTEE_IS_NAME; account.ptstrName = SharePermissions[i].GetFullAccountName(); account.TrusteeType = NativeMethods.TRUSTEE_TYPE.TRUSTEE_IS_UNKNOWN; //let the win32 api figure out if it's a user or group... //set to allow or deny if (SharePermissions[i].Action == Share.Action.Allow) explicitAccessRule[i].grfAccessMode = NativeMethods.ACCESS_MODE.GRANT_ACCESS; else explicitAccessRule[i].grfAccessMode = NativeMethods.ACCESS_MODE.DENY_ACCESS; //build the permission if (SharePermissions[i].Permission == Share.Permission.Read) explicitAccessRule[i].grfAccessPermissions = (uint)NativeMethods.ACCESS_MASK.LM_SHARE_READ; else if (SharePermissions[i].Permission == Share.Permission.Change) explicitAccessRule[i].grfAccessPermissions = (uint)NativeMethods.ACCESS_MASK.LM_SHARE_CHANGE; else if (SharePermissions[i].Permission == Share.Permission.FullControl) explicitAccessRule[i].grfAccessPermissions = (uint)NativeMethods.ACCESS_MASK.LM_SHARE_ALL; //assign the trustee to the access rule explicitAccessRule[i].grfInheritance = 0; //no inheritance, shares don't use inheritance explicitAccessRule[i].Trustee = account; //set the access rule in the ACL (which adds it to our AclPtr) uint setEntriesResult = NativeMethods.SetEntriesInAcl(1, ref explicitAccessRule[i], AclPtr, ref AclPtr); if (setEntriesResult != 0) return ShareStatus.Err_Invalid_SID_Mapping; } //our default security descriptor pointer (points to zero) for now IntPtr secDescPtr = IntPtr.Zero; //create our security descriptor NativeMethods.SECURITY_DESCRIPTOR secDesc = new NativeMethods.SECURITY_DESCRIPTOR(); secDesc.Revision = (byte)NativeMethods.SECURITY_DESCRIPTOR_REVISION; //initialize the security descriptor uint descriptorInit = NativeMethods.InitializeSecurityDescriptor(ref secDesc, NativeMethods.SECURITY_DESCRIPTOR_REVISION); //for some reason Microsoft decided that a zero return code for InitializeSecurityDescriptor means failure...go figure //the msdn page reads: "returns 0 on failure and non-zero on success" //it seems to always return 1 on success...we test that for now //I feel that is saffer then testing for 0 for fail and a catch-all else statement for success...we don't know if that is really true... //http://msdn.microsoft.com/en-us/library/aa378863(v=vs.85).aspx if (descriptorInit == 1) { //add the acl to the security descriptor uint setSecurityResult = NativeMethods.SetSecurityDescriptorDacl(ref secDesc, true, AclPtr, false); //returns 0 on failure... //http://msdn.microsoft.com/en-us/library/aa379583(v=VS.85).aspx if (setSecurityResult == 1) { //last check to make sure our security descriptor is good... uint isValidSD = NativeMethods.IsValidSecurityDesctiptor(ref secDesc); //returns 0 on failure... //http://msdn.microsoft.com/en-us/library/aa379147(v=VS.85).aspx if (isValidSD == 1) { //security descritor looks good..point our pointer to it //note - this is unmanaged memory, we have to manually clear it below when we are finished with it! secDescPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(secDesc)); Marshal.StructureToPtr(secDesc, secDescPtr, false); } else { return Share.ShareStatus.Err_Invalid_Security_Descriptor; } } else { return Share.ShareStatus.Err_Invalid_Security_Descriptor; } } else { return Share.ShareStatus.Err_Security_Descriptor_Init; } //create the barebones 502 share info structure.. NativeMethods.SHARE_INFO_502 shInfo = new NativeMethods.SHARE_INFO_502(); //populate our 502 share info structure shInfo.shi502_netname = shareName; shInfo.shi502_permissions = 0; // ignored for user-level sercurity shInfo.shi502_path = path; shInfo.shi502_passwd = null; // ignored for user-level security shInfo.shi502_remark = shareDesc; shInfo.shi502_max_uses = -1; //what type of share should we create? if (type == ShareType.Normal) shInfo.shi502_type = (uint)NativeMethods.SHARE_TYPE.STYPE_DISKTREE; else if (type == ShareType.Special) shInfo.shi502_type = (uint)NativeMethods.SHARE_TYPE.STYPE_SPECIAL; //assign our new shiny security descriptor to our share_info_502 object shInfo.shi502_security_descriptor = secDescPtr; // Call the win32 Net API to add the share.. int error = 0; NativeMethods.NET_API_STATUS nRetValue = NativeMethods.NetShareAdd(server, 502, ref shInfo, ref error); //free up our security descriptor and acl memory allocations //because these are non-managed memory locations, we must do this manually Marshal.FreeHGlobal(secDescPtr); Marshal.FreeHGlobal(AclPtr); Share.ShareStatus ret; switch (nRetValue) { case NativeMethods.NET_API_STATUS.NERR_Success: ret = ShareStatus.Success; break; case NativeMethods.NET_API_STATUS.NERR_UnknownDevDir: ret = ShareStatus.Err_PathNotFound; break; case NativeMethods.NET_API_STATUS.NERR_DuplicateShare: ret = ShareStatus.Err_DuplicateShare; break; case NativeMethods.NET_API_STATUS.ERROR_BAD_NETPATH: ret = ShareStatus.Err_Network_Path_Not_Found; break; default: ret = ShareStatus.Err_UnknownError; break; } return ret; }