Skip to content

Commit 79fb1d3

Browse files
committed
Use WMI to create SMB Global Mapping
1 parent 7b2a546 commit 79fb1d3

File tree

1 file changed

+55
-28
lines changed

1 file changed

+55
-28
lines changed

pkg/os/smb/api.go

Lines changed: 55 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,15 @@ package smb
33
import (
44
"fmt"
55
"strings"
6+
"syscall"
67

78
"github.com/kubernetes-csi/csi-proxy/pkg/cim"
8-
"github.com/kubernetes-csi/csi-proxy/pkg/utils"
9+
"golang.org/x/sys/windows"
10+
)
11+
12+
const (
13+
credentialDelimiter = ":"
14+
longPathPrefix = `\\?\`
915
)
1016

1117
type API interface {
@@ -31,6 +37,30 @@ func remotePathForQuery(remotePath string) string {
3137
return strings.ReplaceAll(remotePath, "\\", "\\\\")
3238
}
3339

40+
func escapeUserName(userName string) string {
41+
// refer to https://github.com/PowerShell/PowerShell/blob/9303de597da55963a6e26a8fe164d0b256ca3d4d/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimConverter.cs#L169-L170
42+
escaped := strings.ReplaceAll(userName, "\\", "\\\\")
43+
escaped = strings.ReplaceAll(escaped, credentialDelimiter, "\\"+credentialDelimiter)
44+
return escaped
45+
}
46+
47+
func createSymlink(link, target string, isDir bool) error {
48+
linkPtr, _ := syscall.UTF16PtrFromString(link)
49+
targetPtr, _ := syscall.UTF16PtrFromString(target)
50+
51+
var flags uint32
52+
if isDir {
53+
flags = windows.SYMBOLIC_LINK_FLAG_DIRECTORY
54+
}
55+
56+
err := windows.CreateSymbolicLink(
57+
linkPtr,
58+
targetPtr,
59+
flags,
60+
)
61+
return err
62+
}
63+
3464
func (*SmbAPI) IsSmbMapped(remotePath string) (bool, error) {
3565
inst, err := cim.QuerySmbGlobalMappingByRemotePath(remotePathForQuery(remotePath))
3666
if err != nil {
@@ -58,43 +88,40 @@ func (*SmbAPI) NewSmbLink(remotePath, localPath string) error {
5888
// so add one if needed.
5989
remotePath = remotePath + "\\"
6090
}
91+
longRemotePath := remotePath
92+
if !strings.HasPrefix(longRemotePath, longPathPrefix) {
93+
longRemotePath = longPathPrefix + longRemotePath
94+
}
95+
longLocalPath := localPath
96+
if !strings.HasPrefix(longLocalPath, longPathPrefix) {
97+
longLocalPath = longPathPrefix + longLocalPath
98+
}
6199

62-
cmdLine := `New-Item -ItemType SymbolicLink $Env:smblocalPath -Target $Env:smbremotepath`
63-
output, err := utils.RunPowershellCmd(cmdLine, fmt.Sprintf("smbremotepath=%s", remotePath), fmt.Sprintf("smblocalpath=%s", localPath))
100+
err := createSymlink(longLocalPath, longRemotePath, true)
64101
if err != nil {
65-
return fmt.Errorf("error linking %s to %s. output: %s, err: %v", remotePath, localPath, string(output), err)
102+
return fmt.Errorf("error linking %s to %s. err: %v", remotePath, localPath, err)
66103
}
67104

68105
return nil
69106
}
70107

71108
func (api *SmbAPI) NewSmbGlobalMapping(remotePath, username, password string) error {
72-
// use PowerShell Environment Variables to store user input string to prevent command line injection
73-
// https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-5.1
74-
cmdLine := fmt.Sprintf(`$PWord = ConvertTo-SecureString -String $Env:smbpassword -AsPlainText -Force`+
75-
`;$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Env:smbuser, $PWord`+
76-
`;New-SmbGlobalMapping -RemotePath $Env:smbremotepath -Credential $Credential -RequirePrivacy $%t`, api.RequirePrivacy)
77-
78-
if output, err := utils.RunPowershellCmd(cmdLine,
79-
fmt.Sprintf("smbuser=%s", username),
80-
fmt.Sprintf("smbpassword=%s", password),
81-
fmt.Sprintf("smbremotepath=%s", remotePath)); err != nil {
82-
return fmt.Errorf("NewSmbGlobalMapping failed. output: %q, err: %v", string(output), err)
109+
params := map[string]interface{}{
110+
"RemotePath": remotePath,
111+
"RequirePrivacy": api.RequirePrivacy,
112+
}
113+
if username != "" {
114+
// refer to https://github.com/PowerShell/PowerShell/blob/9303de597da55963a6e26a8fe164d0b256ca3d4d/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimConverter.cs#L166-L178
115+
// on how SMB credential is handled in PowerShell
116+
params["Credential"] = escapeUserName(username) + credentialDelimiter + password
83117
}
118+
119+
result, _, err := cim.InvokeCimMethod(cim.WMINamespaceSmb, "MSFT_SmbGlobalMapping", "Create", params)
120+
if err != nil {
121+
return fmt.Errorf("NewSmbGlobalMapping failed. result: %d, err: %v", result, err)
122+
}
123+
84124
return nil
85-
//TODO: move to use WMI when the credentials could be correctly handled
86-
//params := map[string]interface{}{
87-
// "RemotePath": remotePath,
88-
// "RequirePrivacy": api.RequirePrivacy,
89-
//}
90-
//if username != "" {
91-
// params["Credential"] = fmt.Sprintf("%s:%s", username, password)
92-
//}
93-
//result, _, err := cim.InvokeCimMethod(cim.WMINamespaceSmb, "MSFT_SmbGlobalMapping", "Create", params)
94-
//if err != nil {
95-
// return fmt.Errorf("NewSmbGlobalMapping failed. result: %d, err: %v", result, err)
96-
//}
97-
//return nil
98125
}
99126

100127
func (*SmbAPI) RemoveSmbGlobalMapping(remotePath string) error {

0 commit comments

Comments
 (0)